摘要:補(bǔ)充指令解析器源碼補(bǔ)充下節(jié)點(diǎn)類型的知識(shí)元素節(jié)點(diǎn)屬性節(jié)點(diǎn)文本節(jié)點(diǎn)節(jié)點(diǎn)實(shí)體引用名稱節(jié)點(diǎn)實(shí)體名稱節(jié)點(diǎn)處理指令節(jié)點(diǎn)注釋節(jié)點(diǎn)文檔節(jié)點(diǎn)文檔類型節(jié)點(diǎn)文檔片段節(jié)點(diǎn)聲明節(jié)點(diǎn)指令解析器解析節(jié)點(diǎn),直接固定某個(gè)節(jié)點(diǎn)進(jìn)行替換數(shù)據(jù)的解析模板指令,替換模板數(shù)據(jù)初始化試圖
補(bǔ)充指令解析器compile
github源碼
補(bǔ)充下HTML節(jié)點(diǎn)類型的知識(shí): 元素節(jié)點(diǎn) Node.ELEMENT_NODE(1) 屬性節(jié)點(diǎn) Node.ATTRIBUTE_NODE(2) 文本節(jié)點(diǎn) Node.TEXT_NODE(3) CDATA節(jié)點(diǎn) Node.CDATA_SECTION_NODE(4) 實(shí)體引用名稱節(jié)點(diǎn) Node.ENTRY_REFERENCE_NODE(5) 實(shí)體名稱節(jié)點(diǎn) Node.ENTITY_NODE(6) 處理指令節(jié)點(diǎn) Node.PROCESSING_INSTRUCTION_NODE(7) 注釋節(jié)點(diǎn) Node.COMMENT_NODE(8) 文檔節(jié)點(diǎn) Node.DOCUMENT_NODE(9) 文檔類型節(jié)點(diǎn) Node.DOCUMENT_TYPE_NODE(10) 文檔片段節(jié)點(diǎn) Node.DOCUMENT_FRAGMENT_NODE(11) DTD聲明節(jié)點(diǎn) Node.NOTATION_NODE(12)
Compile指令解析器,解析DOM節(jié)點(diǎn),直接固定某個(gè)節(jié)點(diǎn)進(jìn)行替換數(shù)據(jù)的
解析模板指令,替換模板數(shù)據(jù),初始化試圖將模板指令對(duì)應(yīng)的節(jié)點(diǎn)綁定對(duì)應(yīng)的更新函數(shù),初始化對(duì)應(yīng)的訂閱器
首先需要獲取到DOM元素,然后對(duì)含有DOM元素上含有指令的節(jié)點(diǎn)進(jìn)行處理,
因此這個(gè)環(huán)節(jié)需要對(duì)DOM操作比較頻繁,所有可以先建一個(gè)fragment片段,
將需要解析的DOM節(jié)點(diǎn)存入fragment片段里再進(jìn)行處理:
//直接上代碼:(先判斷"{{}}") function Compile(elm){// el->"#name" ,vm->{el:;data:;} this.vm = elm; this.el = document.querySelector(elm.el); this.fragment = null; this.init(); } Compile.prototype = { init:function(){ if(this.el) { //將需要解析的DOM節(jié)點(diǎn)存入fragment片段里再進(jìn)行處理 this.fragment = this.nodeToFragment(this.el); //接下來遍歷各個(gè)節(jié)點(diǎn),對(duì)含有指定的節(jié)點(diǎn)特殊處理,先處理指令“{{}}”: this.compileElement(this.fragment); //綁定到el上 this.el.appendChild(this.fragment); }else{ console.log("DOM元素不存在"); } }, //創(chuàng)建代碼片段 nodeToFragment:function(el){ var fragment = document.createDocumentFragment(); var child = el.firstChild; while(child){//將DOM元素移入fragment fragment.appendChild(child); child = el.firstChild; } return fragment; }, //對(duì)所有子節(jié)點(diǎn)進(jìn)行判斷,1.初始化視圖數(shù)據(jù),2.綁定更新函數(shù)的訂閱器 compileElement:function(el){ var childNodes = el.childNodes; var self = this; [].slice.call(childNodes).forEach(function(node){ var reg = /{{(.*)}}/;//匹配" {{}} " var text = node.textContent; if(self.isTextNode(node) && reg.test(text)) {//判斷" {{}} " self.compileText(node,reg.exec(text)[1]); } if(node.childNodes && node.childNodes.length){ self.compileElement(node);//// 繼續(xù)遞歸遍歷子節(jié)點(diǎn) } }); }, //初始化視圖updateText和生成訂閱器: compileText:function(node,exp){ var self = this; var initText = this.vm[exp]; //代理訪問self_vue.data.name1 -> self_vue.name1 this.updateText(node,initText);//將初始化的數(shù)據(jù)初始化到視圖中 new Watcher(this.vm,exp,function(value){//{},name, // 生成訂閱器并綁定更新函數(shù) self.updateText(node,value); }) }, updateText: function (node, value) { node.textContent = typeof value == "undefined" ? "" : value; }, isTextNode:function(node){ return node.nodeType == 3;//文本節(jié)點(diǎn) } };
為了將解析器Compile與監(jiān)聽器Observer和訂閱者Watcher關(guān)聯(lián)起來,我們需要再修改一下類SelfVue函數(shù):
// function SelfVue(data,el,exp){ //first function SelfVue(options){ var self = this; // this.data = data; //first this.data = options.data; this.el = options.el; this.vm = this; //second console.log(this) Object.keys(this.data).forEach(function (key) { self.proxyKeys(key);//綁定代理屬性 }); //監(jiān)聽數(shù)據(jù): observers(this.data); //first: /*el.innerHTML = this.data[exp];//初始化模板數(shù)據(jù)的值 new Watcher(this,exp,function(value){//綁定訂閱器 el.innerHTML = value; });*/ //初始化視圖updateText和生成訂閱器 new Compile(this); return this; }
到這里,大括號(hào)"{{}}"類型的雙向數(shù)據(jù)綁定完成;
補(bǔ)充上v-model和事件指令:在compileElement函數(shù)加上對(duì)其他指令節(jié)點(diǎn)進(jìn)行判斷,然后遍歷其所有屬性
添加事件指令添加一個(gè)v-model指令
Compile.prototype = { init:function(){ if(this.el) { //將需要解析的DOM節(jié)點(diǎn)存入fragment片段里再進(jìn)行處理 this.fragment = this.nodeToFragment(this.el); //接下來遍歷各個(gè)節(jié)點(diǎn),對(duì)含有指定的節(jié)點(diǎn)特殊處理,先處理指令“{{}}”: this.compileElement(this.fragment); //綁定到el上 this.el.appendChild(this.fragment); }else{ console.log("DOM元素不存在"); } }, //創(chuàng)建代碼片段 nodeToFragment:function(el){ var fragment = document.createDocumentFragment(); var child = el.firstChild; while(child){//將DOM元素移入fragment fragment.appendChild(child); child = el.firstChild; } return fragment; }, //對(duì)所有子節(jié)點(diǎn)進(jìn)行判斷,1.初始化視圖數(shù)據(jù),2.綁定更新函數(shù)的訂閱器 compileElement:function(el){ var childNodes = el.childNodes; var self = this; [].slice.call(childNodes).forEach(function(node){ var reg = /{{(.*)}}/;//匹配" {{}} " var text = node.textContent; /* 補(bǔ)充判斷: */ if(self.isElementNode(node)){//元素節(jié)點(diǎn)判斷 self.compile(node); }else if(self.isTextNode(node) && reg.test(text)) {//文本節(jié)點(diǎn)判斷 ,判斷" {{}} " self.compileText(node,reg.exec(text)[1]); } if(node.childNodes && node.childNodes.length){ self.compileElement(node);//// 繼續(xù)遞歸遍歷子節(jié)點(diǎn) } }); }, //初始化視圖updateText和生成訂閱器: compileText:function(node,exp){ var self = this; var initText = this.vm[exp]; //代理訪問self_vue.data.name1 -> self_vue.name1 this.updateText(node,initText);//將初始化的數(shù)據(jù)初始化到視圖中 new Watcher(this.vm,exp,function(value){//{},name, // 生成訂閱器并綁定更新函數(shù) self.updateText(node,value); }) }, updateText: function (node, value) { node.textContent = typeof value == "undefined" ? "" : value; }, compile:function(node){ var nodeAttrs = node.attributes; var self = this; Array.prototype.forEach.call(nodeAttrs,function(attr){ var attrName = attr.name; if(self.isDirective(attrName)){//查到" v- " var exp = attr.value; var dir = attrName.substring(2);//" v-on/v-model " if(self.isEventDirective(dir)){ // 事件指令 self.compileEvent(node,self.vm,exp,dir); }else{ self.compileModel(node,self.vm,exp,dir); } node.removeAttribute(attrName); } }) }, compileEvent:function(node,vm,exp,dir) {//代碼片段<><>,{data:;vm:;el:;},v-on="add",on: var eventType = dir.split(":")[1];//on var cb = vm.methods && vm.methods[exp]; if(eventType && cb){ node.addEventListener(eventType,cb.bind(vm),false); } }, compileModel:function(node,vm,exp,dir){//代碼片段<><>,{data:;vm:;el:;},v-on="addCounts",model: var self = this; var val = this.vm[exp]; this.modelUpdater(node,val); new Watcher(this.vm,exp,function(value){ self.modelUpdater(node,value); }); node.addEventListener("input",function(e){ var newValue = e.target.value; if(val === newValue){ return; } self.vm[exp] = newValue; val = newValue; }) }, modelUpdater:function(node,value){ node.value = typeof value == "undefined" ? "" : value; }, isTextNode:function(node){ return node.nodeType == 3;//文本節(jié)點(diǎn) }, isElementNode:function(node){ return node.nodeType == 1;//元素節(jié)點(diǎn) }, isDirective:function(attr){//查找自定義屬性為:v- 的屬性 return attr.indexOf("v-") == 0; }, isEventDirective:function(dir){ // 事件指令 return dir.indexOf("on:") === 0 } };
再改造下類SelfVue,使它更像Vue的用法:
function SelfVue(options){ var self = this; this.data = options.data; this.el = options.el; this.methods = options.methods; this.vm = this; //second Object.keys(this.data).forEach(function (key) { self.proxyKeys(key);//綁定代理屬性 }); //監(jiān)聽數(shù)據(jù): observers(this.data); //初始化視圖updateText和生成訂閱器 new Compile(this); options.mounted.call(this); return this; }
測(cè)試:
系列文章的目錄:{{name1}}
{{name2}}
{{title}}
{{event}}
Vue雙向綁定的實(shí)現(xiàn)原理系列(一):Object.defineproperty
Vue雙向綁定的實(shí)現(xiàn)原理系列(二):設(shè)計(jì)模式
Vue雙向綁定的實(shí)現(xiàn)原理系列(三):監(jiān)聽器Observer和訂閱者Watcher
Vue雙向綁定的實(shí)現(xiàn)原理系列(四):補(bǔ)充指令解析器compile
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/51816.html
摘要:補(bǔ)充指令解析器源碼補(bǔ)充下節(jié)點(diǎn)類型的知識(shí)元素節(jié)點(diǎn)屬性節(jié)點(diǎn)文本節(jié)點(diǎn)節(jié)點(diǎn)實(shí)體引用名稱節(jié)點(diǎn)實(shí)體名稱節(jié)點(diǎn)處理指令節(jié)點(diǎn)注釋節(jié)點(diǎn)文檔節(jié)點(diǎn)文檔類型節(jié)點(diǎn)文檔片段節(jié)點(diǎn)聲明節(jié)點(diǎn)指令解析器解析節(jié)點(diǎn),直接固定某個(gè)節(jié)點(diǎn)進(jìn)行替換數(shù)據(jù)的解析模板指令,替換模板數(shù)據(jù)初始化試圖 補(bǔ)充指令解析器compile github源碼 補(bǔ)充下HTML節(jié)點(diǎn)類型的知識(shí): 元素節(jié)點(diǎn) Node.ELEMEN...
摘要:至此監(jiān)聽器和訂閱者功能基本完成,后面再加上指令解析器的功能系列文章的目錄雙向綁定的實(shí)現(xiàn)原理系列一雙向綁定的實(shí)現(xiàn)原理系列二設(shè)計(jì)模式雙向綁定的實(shí)現(xiàn)原理系列三監(jiān)聽器和訂閱者雙向綁定的實(shí)現(xiàn)原理系列四補(bǔ)充指令解析器 監(jiān)聽器Observer和訂閱者Watcher 實(shí)現(xiàn)簡(jiǎn)單版Vue的過程,主要實(shí)現(xiàn){{}}、v-model和事件指令的功能 主要分為三個(gè)部分 github源碼 1.數(shù)據(jù)監(jiān)聽器Obser...
摘要:至此監(jiān)聽器和訂閱者功能基本完成,后面再加上指令解析器的功能系列文章的目錄雙向綁定的實(shí)現(xiàn)原理系列一雙向綁定的實(shí)現(xiàn)原理系列二設(shè)計(jì)模式雙向綁定的實(shí)現(xiàn)原理系列三監(jiān)聽器和訂閱者雙向綁定的實(shí)現(xiàn)原理系列四補(bǔ)充指令解析器 監(jiān)聽器Observer和訂閱者Watcher 實(shí)現(xiàn)簡(jiǎn)單版Vue的過程,主要實(shí)現(xiàn){{}}、v-model和事件指令的功能 主要分為三個(gè)部分 github源碼 1.數(shù)據(jù)監(jiān)聽器Obser...
摘要:并且,由于是在不同的數(shù)據(jù)上觸發(fā)同步,可以精確的將變更發(fā)送給綁定的視圖,而不是對(duì)所有的數(shù)據(jù)都執(zhí)行一次檢測(cè)。默認(rèn)值為表示能否修改屬性的值。 了解Object.defineProperty() github源碼 Object.defineProperty()方法直接在一個(gè)對(duì)象上定義一個(gè)新屬性,或者修改一個(gè)已經(jīng)存在的屬性, 并返回這個(gè)對(duì)象。 vueJS采用 ES5 提供的 Object....
摘要:并且,由于是在不同的數(shù)據(jù)上觸發(fā)同步,可以精確的將變更發(fā)送給綁定的視圖,而不是對(duì)所有的數(shù)據(jù)都執(zhí)行一次檢測(cè)。默認(rèn)值為表示能否修改屬性的值。 了解Object.defineProperty() github源碼 Object.defineProperty()方法直接在一個(gè)對(duì)象上定義一個(gè)新屬性,或者修改一個(gè)已經(jīng)存在的屬性, 并返回這個(gè)對(duì)象。 vueJS采用 ES5 提供的 Object....
閱讀 2335·2021-09-26 10:21
閱讀 2807·2021-09-08 09:36
閱讀 3074·2019-08-30 15:56
閱讀 967·2019-08-30 12:57
閱讀 946·2019-08-26 10:39
閱讀 3568·2019-08-23 18:11
閱讀 3088·2019-08-23 17:12
閱讀 1094·2019-08-23 12:18