摘要:接下來我們分析下是如何調用的,這就涉及到了經典的機制此處先簡單介紹,下一篇會有較詳細的分析。
Vue demo
先給出vue簡單的使用demo,通過創建一個Vue的實例,
將被替換成template模版中的內容,a,b的值也會被換成data屬性的值var vm = new Vue({ el: "#app", template: `模版渲染`, data(){ return { a: 1, b: 2 } } }){{a}}
{}
btn1btn2str1str2str3
以下的分析代碼都經過作者簡化,只為簡單清楚的解析vue的實現邏輯
首先根據參數template屬性生成render函數
function Vue (options) { this._init(options); } Vue.prototype._init = function (options) { var vm = this; //參數el屬性存在,就調用mount方法;初始化時也可以不傳el屬性,后續調用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參數得到render方法 var render = ref.render; //由template參數得到最大靜態渲染樹 var staticRenderFns = ref.staticRenderFns; };
下面看下compileToFunctions生成render方法的具體實現
var ast = parse(template.trim(), options); optimize(ast, options); var code = generate(ast, options);
首先根據template字符串生成ast對象,parse函數主要是通過正則表達式將str轉換成
樹結構的對象,ast對象基本結構如下:
然后對ast對象進行優化,找出ast對象中所有的最大靜態子樹(可以簡單理解為不包含參數data屬性的dom節點,每次data數據改變導致頁面重新渲染的時候,最大靜態子樹不需要重新計算生成),基本實現邏輯如下:
function optimize (root, options) { //將ast對象的所有節點標記為是否靜態 markStatic(root); markStaticRoots(root, false); } function markStatic (node) { //當前節點是否靜態 node.static = isStatic(node); //遞歸標記node的子節點是否靜態 for (var i = 0, l = node.children.length; i < l; i++) { var child = node.children[i]; markStatic(child); //只要有一個子節點非靜態,父節點也非靜態 if (!child.static) { node.static = false; } } } function markStaticRoots (node, isInFor) { //將包含至少一個非文本子節點(node.type === 3代表文本節點)的節點標記為最大靜態樹的根節點 if (node.static && node.children.length && !( node.children.length === 1 && node.children[0].type === 3 )) { node.staticRoot = true; return } else { node.staticRoot = false; } //當前node節點不是靜態根節點,遞歸判斷子節點 if (node.children) { for (var i = 0, l = node.children.length; i < l; i++) { markStaticRoots(node.children[i], isInFor || !!node.for); } } }
本例中的最大靜態樹:
最終生成的渲染函數code如下
其中render是整個模版的渲染函數,staticrenderfns是靜態樹的渲染函數,staticrenderfns中的函數只會初始化一次,后續不需要再計算
render函數中用到的一些方法如下
function installRenderHelpers (target) { target._o = markOnce; target._n = toNumber; //轉換為string對象 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; //生成虛擬文本節點 target._v = createTextVNode; target._e = createEmptyVNode; target._u = resolveScopedSlots; target._g = bindObjectListeners; } //生成虛擬dom節點 vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); };
得到render函數后會繼續調用下面的方法
function mountComponent ( updateComponent = function () { vm._update(vm._render(), hydrating); }; //對vue實例新建一個Watcher監聽對象,每當vm.data數據有變化,Watcher監聽到后負責調用updateComponent進行dom更新 vm._watcher = new Watcher(vm, updateComponent, noop); )
render函數調用后(vm._render())會生成vnode對象,也就是大家熟知的虛擬dom樹,調用update方法就能根據vonde更新真實的瀏覽器dom。
接下來我們分析下updatecomponents是如何調用的,這就涉及到了vue經典的watch機制(此處先簡單介紹,下一篇會有較詳細的分析)。
//new Watcher時會先調用一次updateComponent,后續會監聽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方法的實現
Vue.prototype._update = function (vnode, hydrating) { var prevVnode = vm._vnode; vm._vnode = vnode; //判斷vnode是否初始化過 if (!prevVnode) { // initial render vm.$el = vm.__patch__( // vm.$el是vm對象掛載的節點,本例是 // vm.$options._parentElm是組件掛載的節點(父節點),后面介紹組件時分析 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){ //根據vnode生成element并插入parentElm createElm(vnode, insertedVnodeQueue, parentElm, refElm); } //下面主要介紹初始化dom的實現 function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) { //根據vnode節點生成瀏覽器Element對象 vnode.elm = nodeOps.createElement(tag, vnode); var children = vnode.children; //遞歸將vnode子節點生成Element對象 createChildren(vnode, children, insertedVnodeQueue); //將生成的vnode.elm插入到瀏覽器的父節點當中 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); } //當vnode是文本節點時停止遞歸 } else if (isPrimitive(vnode.text)) { nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(vnode.text)); } }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/89430.html
摘要:圖在中應用三數據渲染過程數據綁定實現邏輯本節正式分析從到數據渲染到頁面的過程,在中定義了一個的構造函數。一、概述 vue已是目前國內前端web端三分天下之一,也是工作中主要技術棧之一。在日常使用中知其然也好奇著所以然,因此嘗試閱讀vue源碼并進行總結。本文旨在梳理初始化頁面時data中的數據是如何渲染到頁面上的。本文將帶著這個疑問一點點追究vue的思路??傮w來說vue模版渲染大致流程如圖1所...
摘要:首先在里調用函數把獲取相應傳遞過去里在原型上定義方法對瀏覽器的怪癖做兼容布爾值。對瀏覽器的怪癖做兼容布爾值。 首先在init.js里調用$mount函數把el #app獲取相應dom傳遞過去 Vue.prototype._init = function (options) { ... if (vm.$options.el) { vm.$mount(vm.$opt...
摘要:通過對源碼閱讀,想寫一寫自己的理解,能力有限故從尤大佬第一次提交開始讀,準備陸續寫模版字符串轉語法樹語法樹轉函數雙向綁定原理虛擬比較原理其中包含自己的理解和源碼的分析,盡量通俗易懂由于是的最早提交,所以和最新版本有很多差異,后續將陸續補充, 通過對 Vue2.0 源碼閱讀,想寫一寫自己的理解,能力有限故從尤大佬2016.4.11第一次提交開始讀,準備陸續寫: 模版字符串轉AST語法...
摘要:通過對源碼閱讀,想寫一寫自己的理解,能力有限故從尤大佬第一次提交開始讀,準備陸續寫模版字符串轉語法樹語法樹轉函數雙向綁定原理虛擬比較原理其中包含自己的理解和源碼的分析,盡量通俗易懂由于是的最早提交,所以和最新版本有很多差異,后續將陸續補充, 通過對 Vue2.0 源碼閱讀,想寫一寫自己的理解,能力有限故從尤大佬2016.4.11第一次提交開始讀,準備陸續寫: 模版字符串轉AST語法...
閱讀 540·2021-08-31 09:45
閱讀 1647·2021-08-11 11:19
閱讀 883·2019-08-30 15:55
閱讀 821·2019-08-30 10:52
閱讀 2845·2019-08-29 13:11
閱讀 2924·2019-08-23 17:08
閱讀 2833·2019-08-23 15:11
閱讀 3066·2019-08-23 14:33