成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Vue源碼解析(一)-模版渲染

KitorinZero / 3139人閱讀

摘要:接下來(lái)我們分析下是如何調(diào)用的,這就涉及到了經(jīng)典的機(jī)制此處先簡(jiǎn)單介紹,下一篇會(huì)有較詳細(xì)的分析。

Vue demo

先給出vue簡(jiǎn)單的使用demo,通過(guò)創(chuàng)建一個(gè)Vue的實(shí)例,

將被替換成template模版中的內(nèi)容,a,b的值也會(huì)被換成data屬性的值

var vm = new Vue({ el: "#app", template: `

{{a}}

{}

btn1
btn2
str1
str2
str3
`, data(){ return { a: 1, b: 2 } } })
模版渲染

以下的分析代碼都經(jīng)過(guò)作者簡(jiǎn)化,只為簡(jiǎn)單清楚的解析vue的實(shí)現(xiàn)邏輯
首先根據(jù)參數(shù)template屬性生成render函數(shù)

function Vue (options) {
  this._init(options);
}

Vue.prototype._init = function (options) {
  var vm = this;
  //參數(shù)el屬性存在,就調(diào)用mount方法;初始化時(shí)也可以不傳el屬性,后續(xù)調(diào)用mount方法
  if (vm.$options.el) {
      vm.$mount(vm.$options.el);
  }
}

Vue.prototype.$mount = function () {
  var ref = compileToFunctions(template, {
    shouldDecodeNewlines: shouldDecodeNewlines,
    delimiters: options.delimiters,
    comments: options.comments
  }, this);
  //由template參數(shù)得到render方法
  var render = ref.render;   
  //由template參數(shù)得到最大靜態(tài)渲染樹(shù)
  var staticRenderFns = ref.staticRenderFns;
};

下面看下compileToFunctions生成render方法的具體實(shí)現(xiàn)

  var ast = parse(template.trim(), options);
  optimize(ast, options);
  var code = generate(ast, options);

首先根據(jù)template字符串生成ast對(duì)象,parse函數(shù)主要是通過(guò)正則表達(dá)式將str轉(zhuǎn)換成
樹(shù)結(jié)構(gòu)的對(duì)象,ast對(duì)象基本結(jié)構(gòu)如下:


然后對(duì)ast對(duì)象進(jìn)行優(yōu)化,找出ast對(duì)象中所有的最大靜態(tài)子樹(shù)(可以簡(jiǎn)單理解為不包含參數(shù)data屬性的dom節(jié)點(diǎn),每次data數(shù)據(jù)改變導(dǎo)致頁(yè)面重新渲染的時(shí)候,最大靜態(tài)子樹(shù)不需要重新計(jì)算生成),基本實(shí)現(xiàn)邏輯如下:

function optimize (root, options) {
  //將ast對(duì)象的所有節(jié)點(diǎn)標(biāo)記為是否靜態(tài)
  markStatic(root);
  markStaticRoots(root, false);
}
function markStatic (node) {
  //當(dāng)前節(jié)點(diǎn)是否靜態(tài)
  node.static = isStatic(node);
  //遞歸標(biāo)記node的子節(jié)點(diǎn)是否靜態(tài)
  for (var i = 0, l = node.children.length; i < l; i++) {
    var child = node.children[i];
    markStatic(child);
    //只要有一個(gè)子節(jié)點(diǎn)非靜態(tài),父節(jié)點(diǎn)也非靜態(tài)
    if (!child.static) {
      node.static = false;
    }
  }
}
function markStaticRoots (node, isInFor) {
    //將包含至少一個(gè)非文本子節(jié)點(diǎn)(node.type === 3代表文本節(jié)點(diǎn))的節(jié)點(diǎn)標(biāo)記為最大靜態(tài)樹(shù)的根節(jié)點(diǎn)
    if (node.static && node.children.length && !(
      node.children.length === 1 &&
      node.children[0].type === 3
    )) {
      node.staticRoot = true;
      return
    } else {
      node.staticRoot = false;
    }
    //當(dāng)前node節(jié)點(diǎn)不是靜態(tài)根節(jié)點(diǎn),遞歸判斷子節(jié)點(diǎn)
    if (node.children) {
      for (var i = 0, l = node.children.length; i < l; i++) {
        markStaticRoots(node.children[i], isInFor || !!node.for);
      }
    }
}

本例中的最大靜態(tài)樹(shù):

最終生成的渲染函數(shù)code如下

其中render是整個(gè)模版的渲染函數(shù),staticrenderfns是靜態(tài)樹(shù)的渲染函數(shù),staticrenderfns中的函數(shù)只會(huì)初始化一次,后續(xù)不需要再計(jì)算
render函數(shù)中用到的一些方法如下

function installRenderHelpers (target) {
  target._o = markOnce;
  target._n = toNumber;
  //轉(zhuǎn)換為string對(duì)象
  target._s = toString;
  target._l = renderList;
  target._t = renderSlot;
  target._q = looseEqual;
  target._i = looseIndexOf;
  target._m = renderStatic;
  target._f = resolveFilter;
  target._k = checkKeyCodes;
  target._b = bindObjectProps;
  //生成虛擬文本節(jié)點(diǎn)
  target._v = createTextVNode;
  target._e = createEmptyVNode;
  target._u = resolveScopedSlots;
  target._g = bindObjectListeners;
}
//生成虛擬dom節(jié)點(diǎn)
vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); };

得到render函數(shù)后會(huì)繼續(xù)調(diào)用下面的方法

function mountComponent (
    updateComponent = function () {
      vm._update(vm._render(), hydrating);
    };
    //對(duì)vue實(shí)例新建一個(gè)Watcher監(jiān)聽(tīng)對(duì)象,每當(dāng)vm.data數(shù)據(jù)有變化,Watcher監(jiān)聽(tīng)到后負(fù)責(zé)調(diào)用updateComponent進(jìn)行dom更新
    vm._watcher = new Watcher(vm, updateComponent, noop);
)

render函數(shù)調(diào)用后(vm._render())會(huì)生成vnode對(duì)象,也就是大家熟知的虛擬dom樹(shù),調(diào)用update方法就能根據(jù)vonde更新真實(shí)的瀏覽器dom。
接下來(lái)我們分析下updatecomponents是如何調(diào)用的,這就涉及到了vue經(jīng)典的watch機(jī)制(此處先簡(jiǎn)單介紹,下一篇會(huì)有較詳細(xì)的分析)。

//new Watcher時(shí)會(huì)先調(diào)用一次updateComponent,后續(xù)會(huì)監(jiān)聽(tīng)vm.data的變化
var Watcher = function Watcher (vm,expOrFn){
  if (typeof expOrFn === "function") {
    this.getter = expOrFn;
  }
  this.get();
}
Watcher.prototype.get = function get () {
   value = this.getter.call(vm, vm);
}

最后再講一下 vm._update方法的實(shí)現(xiàn)

Vue.prototype._update = function (vnode, hydrating) {
    var prevVnode = vm._vnode;
    vm._vnode = vnode;
    //判斷vnode是否初始化過(guò)
    if (!prevVnode) {
      // initial render
      vm.$el = vm.__patch__(
        //  vm.$el是vm對(duì)象掛載的節(jié)點(diǎn),本例是
// vm.$options._parentElm是組件掛載的節(jié)點(diǎn)(父節(jié)點(diǎn)),后面介紹組件時(shí)分析 vm.$el, vnode, hydrating, false /* removeOnly */, vm.$options._parentElm, vm.$options._refElm ); } else { // updates vm.$el = vm.__patch__(prevVnode, vnode); } } //vm.__pathch__方法 function function patch (oldVnode, vnode, hydrating, removeOnly, parentElm, refElm){ //根據(jù)vnode生成element并插入parentElm createElm(vnode, insertedVnodeQueue, parentElm, refElm); } //下面主要介紹初始化dom的實(shí)現(xiàn) function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) { //根據(jù)vnode節(jié)點(diǎn)生成瀏覽器Element對(duì)象 vnode.elm = nodeOps.createElement(tag, vnode); var children = vnode.children; //遞歸將vnode子節(jié)點(diǎn)生成Element對(duì)象 createChildren(vnode, children, insertedVnodeQueue); //將生成的vnode.elm插入到瀏覽器的父節(jié)點(diǎn)當(dāng)中 insert(parentElm, vnode.elm, refElm); } function createChildren (vnode, children, insertedVnodeQueue) { if (Array.isArray(children)) { for (var i = 0; i < children.length; ++i) { createElm(children[i], insertedVnodeQueue, vnode.elm, null, true); } //當(dāng)vnode是文本節(jié)點(diǎn)時(shí)停止遞歸 } else if (isPrimitive(vnode.text)) { nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(vnode.text)); } }

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/89430.html

相關(guān)文章

  • vue源碼閱讀之?dāng)?shù)據(jù)渲染過(guò)程

    摘要:圖在中應(yīng)用三數(shù)據(jù)渲染過(guò)程數(shù)據(jù)綁定實(shí)現(xiàn)邏輯本節(jié)正式分析從到數(shù)據(jù)渲染到頁(yè)面的過(guò)程,在中定義了一個(gè)的構(gòu)造函數(shù)。一、概述 vue已是目前國(guó)內(nèi)前端web端三分天下之一,也是工作中主要技術(shù)棧之一。在日常使用中知其然也好奇著所以然,因此嘗試閱讀vue源碼并進(jìn)行總結(jié)。本文旨在梳理初始化頁(yè)面時(shí)data中的數(shù)據(jù)是如何渲染到頁(yè)面上的。本文將帶著這個(gè)疑問(wèn)一點(diǎn)點(diǎn)追究vue的思路??傮w來(lái)說(shuō)vue模版渲染大致流程如圖1所...

    AlphaGooo 評(píng)論0 收藏0
  • Vue源碼解析(1)-模版渲染

    摘要:首先在里調(diào)用函數(shù)把獲取相應(yīng)傳遞過(guò)去里在原型上定義方法對(duì)瀏覽器的怪癖做兼容布爾值。對(duì)瀏覽器的怪癖做兼容布爾值。 首先在init.js里調(diào)用$mount函數(shù)把el #app獲取相應(yīng)dom傳遞過(guò)去 Vue.prototype._init = function (options) { ... if (vm.$options.el) { vm.$mount(vm.$opt...

    k00baa 評(píng)論0 收藏0
  • Vue源碼解析模版字符串轉(zhuǎn)AST語(yǔ)法樹(shù)

    摘要:通過(guò)對(duì)源碼閱讀,想寫一寫自己的理解,能力有限故從尤大佬第一次提交開(kāi)始讀,準(zhǔn)備陸續(xù)寫模版字符串轉(zhuǎn)語(yǔ)法樹(shù)語(yǔ)法樹(shù)轉(zhuǎn)函數(shù)雙向綁定原理虛擬比較原理其中包含自己的理解和源碼的分析,盡量通俗易懂由于是的最早提交,所以和最新版本有很多差異,后續(xù)將陸續(xù)補(bǔ)充, 通過(guò)對(duì) Vue2.0 源碼閱讀,想寫一寫自己的理解,能力有限故從尤大佬2016.4.11第一次提交開(kāi)始讀,準(zhǔn)備陸續(xù)寫: 模版字符串轉(zhuǎn)AST語(yǔ)法...

    chengjianhua 評(píng)論0 收藏0
  • Vue源碼解析模版字符串轉(zhuǎn)AST語(yǔ)法樹(shù)

    摘要:通過(guò)對(duì)源碼閱讀,想寫一寫自己的理解,能力有限故從尤大佬第一次提交開(kāi)始讀,準(zhǔn)備陸續(xù)寫模版字符串轉(zhuǎn)語(yǔ)法樹(shù)語(yǔ)法樹(shù)轉(zhuǎn)函數(shù)雙向綁定原理虛擬比較原理其中包含自己的理解和源碼的分析,盡量通俗易懂由于是的最早提交,所以和最新版本有很多差異,后續(xù)將陸續(xù)補(bǔ)充, 通過(guò)對(duì) Vue2.0 源碼閱讀,想寫一寫自己的理解,能力有限故從尤大佬2016.4.11第一次提交開(kāi)始讀,準(zhǔn)備陸續(xù)寫: 模版字符串轉(zhuǎn)AST語(yǔ)法...

    王偉廷 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<