摘要:接下來(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: `模版渲染`, data(){ return { a: 1, b: 2 } } }){{a}}
{}
btn1btn2str1str2str3
以下的分析代碼都經(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
摘要:圖在中應(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所...
摘要:首先在里調(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...
摘要:通過(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ǔ)法...
摘要:通過(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ǔ)法...
閱讀 573·2021-08-31 09:45
閱讀 1688·2021-08-11 11:19
閱讀 918·2019-08-30 15:55
閱讀 854·2019-08-30 10:52
閱讀 2895·2019-08-29 13:11
閱讀 2959·2019-08-23 17:08
閱讀 2867·2019-08-23 15:11
閱讀 3104·2019-08-23 14:33