以下所有代碼都是根據源碼思想的簡化簡寫偽碼。
-
1.將template解析為AST(Abstract Syntax Tree)
/** * 將template解析為ast * <div class="parent">{{tips}}</div> * 解析為ast(以對象的形式標識dom)后: * { * attrsMap:{class:"parent"},//屬性集合 * children:[{ * expression:"_s(tips)",//render方法表達式 * static:false,//是否是靜態節點 * text:"{{tips}}",//文本內容 * type:2//2:占位符文本 * }],//子節點集合 * tag:'div',//標簽名 * type:1,//節點類型 1:ele 2:占位符文本 3:純文本 * staticClass:'parent',//靜態類 * static:false,//是否是靜態節點 * parent:null//父AST對象 * } */ var ast = parse(template.trim(),options); // 至于template解析為ast的流程,也就是parse的實現。是根據正則匹配出標簽開始,匹配出標簽結束,匹配出標簽屬性,匹配出標簽文本。 // 再通過先后觸發的標簽開始結束方法確定父子關系。這里主要記錄思想,實現不過多贅述,詳細可看parse的源碼
-
2.將AST編譯為render方法字串
/** * 將AST編譯為render方法 * { * attrsMap:{class:"parent"},//屬性集合 * children:[{ * expression:"_s(tips)",//render方法表達式 * static:false,//是否是靜態節點 * text:"{{tips}}",//文本內容 * type:2//2:占位符文本 * }],//子節點集合 * tag:'div',//標簽名 * type:1,//節點類型 1:ele 2:占位符文本 3:純文本 * staticClass:'parent',//靜態類 * static:false,//是否是靜態節點 * parent:null//父AST對象 * } * 編譯為render方法字串后,!!!這里是字串不是方法 * with (this) { * return _c('div', {staticClass: "parent"}, [_v(_s(tips))]) * } * 返回值code.render方法中就是這個字串,具體的_c,_v,_s等等方法之后介紹 * 到此AST的使命就結束了,那么render方法的作用是啥呢?其實執行render方法得到的就是template對應的虛擬dom(vnode) */ var code = generate(ast,options);
-
3.到此template就被解析成了render方法,小節一波
/** * 靜態節點:永遠不會因為數據改變而變化的節點 * 1.vue被實例化 * 2.將dom轉為template字串 * 3.將template字串轉為AST * 4.將AST轉為render方法(staticRenderFns是靜態節點render方法的數組) * 5.將render方法掛載到Vue實例下的$options屬性下的render上(同原理還有staticRenderFns) * * */ function Vue(option){ //... this.$mount(); //... } Vue.prototype.$mount = function() { //... var compiled = Vue.compile(template); this.$options.render = compiled.render; this.$options.staticRenderFns = compiled.staticRenderFns; //... } Vue.compile = function (template) { var ast = parse(template.trim(),options); var code = generate(ast,options); var res = {}; res.render = createFunction(code.render, fnGenErrors); res.staticRenderFns = code.staticRenderFns.map(function (code) { return createFunction(code, fnGenErrors) }); return res; }
-
4.現在render方法已經綁定到實例中,接下來處理數據給數據添加攔截方法
/** * 添加數據攔截,先說Object.defineProperty */ var data = { } function Vue(option){ //... this.$mount(); //... } Vue.prototype.$mount = function() { //... var compiled = Vue.compile(template); this.$options.render = compiled.render; this.$options.staticRenderFns = compiled.staticRenderFns; //... } Vue.compile = function (template) { var ast = parse(template.trim(),options); var code = generate(ast,options); var res = {}; res.render = createFunction(code.render, fnGenErrors); res.staticRenderFns = code.staticRenderFns.map(function (code) { return createFunction(code, fnGenErrors) }); return res; }
_m方法 靜態根節點
_l方法 for循環
_e方法 if語句
_c方法 處理完指令剩余標簽
_v方法 處理文本節點或含mustache的文本
with方法的作用:(來實現scope)
with(obj)作用就是將后面的{}中的語句塊中的缺省對象設置為obj,那么在其后面的{}語句塊中引用obj的方法或屬性時可以省略obj.的輸入而直接使用方法或屬性的名稱。
```javascript
function scope() {
this.data = {
test1: '獨立作用域'
};
this.say = function (ss) {
console.log(ss);
}
}
var s = new scope();
var fncode = 'with(this){say(data.test1);}';
var render = new Function(fncode);
render.call(s, s);
```
未完待續!!!!