vue數據雙向綁定原理-解析器Complie
阿新 • • 發佈:2018-07-31
ava div des for lse 遍歷 `` dde move
??
1)vue數據雙向綁定原理-observer
?2)vue數據雙向綁定原理-wather?
3)vue數據雙向綁定原理-解析器Complie
vue數據雙向綁定原理, 和簡單的實現,本文將實現mvvm的模板指令解析器
上一步實現了簡單數據綁定, 最後實現解析器,來解析 v-model ,v-on:click等指令,和 {{}} 模板數據.解析器Compile實現步驟:
1.解析模板指令,並替換模板數據,初始化視圖
2.將模板指令對應的節點綁定對應的更新函數,初始化相應的訂閱器
為了解析模板,首先需要獲取到dom元素,然後對含有dom元素上含有指令的節點進行處理,因此這個環節需要對dom操作比較頻繁,所有可以先建一個fragment片段,將需要解析的dom節點存入fragment片段裏再進行處理:
function node2Fragment(el) {
var fragment = document.createDocumentFragment(), child;
// 將原生節點拷貝到fragment
while (child = el.firstChild) {
fragment.appendChild(child);
}
return fragment;
}
接下來 渲染‘{{}}‘模板
//Compile function Compile(el, vm) { this.$vm = vm this.$el = this.isElementNode(el) ? el : document.querySelector(el); if (this.$el) { this.$fragment = this.node2Fragment(this.$el); this.init(); this.$el.appendChild(this.$fragment); } }; Compile.prototype = { init: function () { this.compileElement(this.$fragment); }, node2Fragment: function (el) { //... }, //編譯模板 compileElement: function (el) { var childNodes = el.childNodes, self = this; [].slice.call(childNodes).forEach(function (node) { var text = node.textContent; var reg = /{{(.*)}}/; //表達式文本 //按元素節點方式編譯 if (self.isElementNode(node)) { self.compile(node); } else if (self.isTextNode(node) && reg.test(text)) { self.compileText(node, RegExp.$1); }; //遍歷編譯子節點 if (node.childNodes && node.childNodes.length) { self.compileElement(node); }; }); }, isElementNode: function (node) { return node.nodeType == 1; }, isTextNode: function (node) { return node.nodeType == 3; }, compileText: function (node, exp) { var self = this; var initText = this.$vm[exp]; this.updateText(node, initText); new Watcher(this.$vm, exp, function (value) { self.updateText(node, value); }); }, updateText: function (node, value) { node.textContent = typeof value == ‘undefined‘ ? ‘‘ : value; }, }
處理解析指令對相關指令進行函數綁定,
Compile.prototype = { ...... isDirective: function (attr) { return attr.indexOf(‘v-‘) == 0; }, isEventDirective: function (dir) { return dir.indexOf(‘on:‘) === 0; }, //處理v-指令 compile: function (node) { var nodeAttrs = node.attributes, self = this; [].slice.call(nodeAttrs).forEach(function (attr) { // 規定:指令以 v-xxx 命名 // 如 <span v-text="content"></span> 中指令為 v-text var attrName = attr.name; // v-text if (self.isDirective(attrName)) { var exp = attr.value; // content var dir = attrName.substring(2); // text if (self.isEventDirective(dir)) { // 事件指令, 如 v-on:click self.compileEvent(node, self.$vm, exp, dir); } else { // 普通指令如:v-model,v-html,當前只處理v-model self.compileModel(node, self.$vm, exp, dir); } //處理完畢要幹掉 v-on:,v-model 等元素屬性 node.removeAttribute(attrName) } }); }, compileEvent: function (node, vm, exp, dir) { var eventType = dir.split(‘:‘)[1]; var cb = vm.$options.methods && vm.$options.methods[exp]; if (eventType && cb) { node.addEventListener(eventType, cb.bind(vm), false); } }, compileModel: function (node, vm, exp, dir) { var self = this; var val = this.$vm[exp]; this.updaterModel(node, val); new Watcher(this.$vm, exp, function (value) { self.updaterModel(node, value); }); node.addEventListener(‘input‘, function (e) { var newValue = e.target.value; if (val === newValue) { return; } self.$vm[exp] = newValue; val = newValue; }); }, updaterModel: function (node, value, oldValue) { node.value = typeof value == ‘undefined‘ ? ‘‘ : value; }, }
最後再關聯起來
function Vue(options) {
.....
observe(this.data, this);
this.$compile = new Compile(options.el || document.body, this)
return this;
}
來嘗試下效果
```html
{{name}}
{{name}}
OK. 基本完善了
vue數據雙向綁定原理-解析器Complie