国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

Vue AST源碼解析第一篇

2019-11-19 16:02:24
字體:
來源:轉載
供稿:網友

講完了數據劫持原理和一堆初始化,現在是DOM相關的代碼了。

上一節是從這個函數開始的:

// Line-3924 Vue.prototype._init = function(options) {  // 大量初始化  // ...  // Go!  if (vm.$options.el) {   vm.$mount(vm.$options.el);  } };

弄完data屬性的數據綁定后,開始處理el屬性,也就是掛載的DOM節點,這里的vm.$options.el也就是傳進去的'#app'字符串。

有一個值得注意的點是,源碼中有2個$mount函數都是Vue$3的原型函數,其中一個標記了注釋public mount method,在7531行,另外一個在9553行。打斷點進入的是后面,因為定義的晚,覆蓋了前面的函數。

// Line-7531 // public mount method Vue$3.prototype.$mount = function(el,hydrating) {  el = el && inBrowser ? query(el) : undefined;  return mountComponent(this, el, hydrating) }; // Line-9552 var mount = Vue$3.prototype.$mount; Vue$3.prototype.$mount = function(  el,  hydrating ) {  // ...很多代碼  return mount.call(this, el, hydrating) };

現在進入后面的$mount函數看看內部結構:

// Line-9552 var mount = Vue$3.prototype.$mount; Vue$3.prototype.$mount = function(el,hydrating) {  // 將el格式化為DOM節點  el = el && query(el);  // 判斷是否掛載到body或者html標簽上  if (el === document.body || el === document.documentElement) {   "development" !== 'production' && warn(    "Do not mount Vue to <html> or <body> - mount to normal elements instead."   );   return this  }  var options = this.$options;  // 處理template/el 轉換為渲染函數  if (!options.render) {   // ...非常多代碼  }  return mount.call(this, el, hydrating) };

代碼前半段首先將el轉換為DOM節點,并判斷是否掛載到body或者html標簽,看看簡單的query函數:

 // Line-4583 function query(el) {  // 如果是字符串就調用querySelector  if (typeof el === 'string') {   var selected = document.querySelector(el);   if (!selected) {    "development" !== 'production' && warn(     'Cannot find element: ' + el    );    // 找不到就返回一個div    return document.createElement('div')   }   return selected  }  // 不是字符串就默認傳進來的是DOM節點   else {   return el  } }

函數比較簡單,值得注意的幾個點是,由于調用的是querySelector方法,所以可以傳標簽名、類名、C3新選擇器等,都會返回查詢到的第一個。當然,總是傳一個ID或者確定的DOM節點才是正確用法。

下面看接下來的代碼:

// Line-9552 var mount = Vue$3.prototype.$mount; Vue$3.prototype.$mount = function(el,hydrating) {  // ...el轉換為DOM節點  // ...  // 沒有render屬性 進入代碼段  if (!options.render) {   var template = options.template;   // 沒有template 跳   if (template) {    if (typeof template === 'string') {     if (template.charAt(0) === '#') {      template = idToTemplate(template);      /* istanbul ignore if */      if ("development" !== 'production' && !template) {       warn(        ("Template element not found or is empty: " + (options.template)),        this       );      }     }    } else if (template.nodeType) {     template = template.innerHTML;    } else {     {      warn('invalid template option:' + template, this);     }     return this    }   }   // 有el 獲取字符串化的DOM樹   else if (el) {    template = getOuterHTML(el);   }   if (template) {    // ...小段代碼   }  }  return mount.call(this, el, hydrating) };

由于沒有template屬性,會直接進入第二個判斷條件,調用getOuterHTML來初始化template變量,函數比較簡單, 來看看:

// Line-9623 function getOuterHTML(el) {  if (el.outerHTML) {   return el.outerHTML  }  // 兼容IE中的SVG  else {   var container = document.createElement('div');   container.appendChild(el.cloneNode(true));   return container.innerHTML  } }

簡單來講,就是調用outerHTML返回DOM樹的字符串形式,看圖就明白了:

下面看最后一段代碼:

 // Line-9552 var mount = Vue$3.prototype.$mount; Vue$3.prototype.$mount = function(el,hydrating) {  // ...el轉換為DOM節點  // ...  // 沒有render屬性 進入代碼段  if (!options.render) {   // ...處理template   // ...   if (template) {    // 編譯開始    if ("development" !== 'production' && config.performance && mark) {     mark('compile');    }    // 將DOM樹字符串編譯為函數    var ref = compileToFunctions(template, {     shouldDecodeNewlines: shouldDecodeNewlines,     delimiters: options.delimiters    }, this);    // options添加屬性    var render = ref.render;    var staticRenderFns = ref.staticRenderFns;    options.render = render;    options.staticRenderFns = staticRenderFns;    // 編譯結束    if ("development" !== 'production' && config.performance && mark) {     mark('compile end');     measure(((this._name) + " compile"), 'compile', 'compile end');    }   }  }  return mount.call(this, el, hydrating) };

忽略2段dev模式下的提示代碼,剩下的代碼做了3件事,調用compileToFunctions函數肢解DOM樹字符串,將返回的對象屬性添加到options上,再次調用mount函數。

首先看一下compileToFunctions函數,該函數接受3個參數,分別為字符串、配置對象、當前vue實例。

由于函數比較長,而且部分是錯誤判斷,簡化后如下:

// Line-9326 function compileToFunctions(template,options,vm) {  // 獲取配置參數  options = options || {};  // ...  var key = options.delimiters ?   String(options.delimiters) + template :   template;  // 檢測緩存  if (functionCompileCache[key]) {   return functionCompileCache[key]  }  // 1  var compiled = compile(template, options);  // ...  // 2  var res = {};  var fnGenErrors = [];  res.render = makeFunction(compiled.render, fnGenErrors);  var l = compiled.staticRenderFns.length;  res.staticRenderFns = new Array(l);  for (var i = 0; i < l; i++) {   res.staticRenderFns[i] = makeFunction(compiled.staticRenderFns[i], fnGenErrors);  }  // ...  // 3  return (functionCompileCache[key] = res) }

可以看到,這個函數流程可以分為4步,獲取參數 => 調用compile函數進行編譯 => 將得到的compiled轉換為函數 => 返回并緩存。

 第一節現在這樣吧。一張圖總結下:

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 伊宁县| 广宗县| 客服| 望江县| 潍坊市| 济阳县| 崇左市| 连云港市| 麻阳| 鄂伦春自治旗| 吴桥县| 武宁县| 南安市| 池州市| 读书| 蕉岭县| 临武县| 札达县| 永顺县| 偃师市| 晋宁县| 永兴县| 曲水县| 吐鲁番市| 汉中市| 武清区| 隆尧县| 甘孜| 丽江市| 志丹县| 郧西县| 青浦区| 星子县| 黄冈市| 中超| 临安市| 突泉县| 林州市| 荥阳市| 灌云县| 莫力|