- vue實例渲染的底層實現
- vue實例生命周期
- vue實例渲染的生命周期與鈎子函數詳解
一、vue實例渲染的底層實現
1.1實例掛載
在vue中實例掛載有兩種方法:第一種在實例化vue時以el屬性實現,第二種是通過vue.$mount()方法實現掛載。不管是哪種掛載都不影響vue實例化組件的執行流程和模式,只是通過vue.$mount()方法實現掛載可以更靈活的實現組件復用和掛載。
var vm = new Vue({ el:'掛載元素id',//實例化el屬性實現掛載 ... }) var vm1 = new Vue({...}); vm1.mount('掛載元素id');//vue.mount()方法實現掛載 //參數模型:# + id
1.2構建DOM抽象語法樹與template
在vue實例化中有一個非常關鍵的操作就是構建DOM抽象語法樹,基於抽象語法樹生成虛擬節點,然后再將數據渲染到虛擬節點上,再將完成數據渲染的節點添加到document中刷新頁面,呈現頁面效果。構建抽象語法樹有三種方式:1.基於綁定的實例化屬性el和$mount()方法構建;2.基於實例化屬性template添加的模板構建;3.基於ready()的參數構建。
這三種構建方式的優先級:render() > template > el
本質上的el與template與render()函數的參數都是一個原理,通過el就是將el指向的元素的DOM模型的outerHTML屬性值拿到,outerHTML屬性值與template的形式完全一樣就是html文本的字符串形式;然后將這個字符串形式的html文本轉換成js對象模型,render()函數中使用的就直接是js對象模型,接着通過js對象模型所表達的html結構轉換成AST(抽象語法樹)用於構建虛擬節點VNode;render()函數再在這個虛擬節點上渲染數據,完成數據渲染后就添加到html文檔中渲染到頁面。
通過元素在window上的id指向獲取到id匹配的元素節點對象的outerHTML屬性值:
<div id="app">我是一個div</div> <script> console.log(app.outerHTML);//"<div id="app">我是一div</div>" </script>
通過vue對象實例化屬性template構建vue實例:
<div id="app">我是一個div</div> <script> var vm = new Vue({ el:"#app", template:`<div>我是template模板構建的節點</div>` }) </script>
通過vue實例化將實例化對象屬性template的模板替代app指向的原節點,實質上是在vue實例構建過程中如果發現有template就不會再去獲取掛載節點的結構了。接着再來看看render()方法如何實現vue實例構建:
<div id="app">我是一個div</div> <script> var vm = new Vue({ el:"#app", template:`<div>我是template模板構建的節點</div>`, render(createElement){ return createElement("p"); } }) </script>
通過添加render()方法刷新頁面會發現頁面變成了空白,也就是說el掛載原節點和template模板構建的節點都沒生效,查看瀏覽器控制台可以看到在原app指向的節點的位置被一個空的p標簽替代了。這就是說render()函數的優先級大於template和el,但是要注意的是vue實例化必須是在通過el或者vue.mount()掛載才會去執行render()節點渲染方法,不然一個不掛載的vue實例有何必要渲染呢?
1.3基於JS對象模型的AST抽象語法樹構建及虛擬節點渲染:
render(createElement){ return createElement(ElementName,ElementProperty,ChildNode); }
這里不深入討論render的設計實現,也不討論虛擬節點的具體底層實現原理,也不深入探究render的復雜應用,僅僅對render()函數基於js對象模型構建AST抽象語法樹做出解析。
createElement:聲明工具函數的名稱;
ElementName:設定抽象語法樹根節點元素名稱;
ElementProperty:設定根節點元素的屬性;
ChildNode:設定子節點;
render(createElement){ return createElement("p",{ style:{ color:"red", fontSize:'18px' }, class:['classname1','classname2'] },"我是由render構建的p標簽"); }
基於data的構建方式:
<div id="app">我是一個div</div> <script> var vm = new Vue({ el:"#app", template:`<div>我是template模板構建的節點</div>`, data:{ classname1:true, classname2:false, text:'我時由render構建的p標簽' }, render(createElement){ return createElement("p",{ style:{ color:"red", fontSize:'18px' }, class:{//基於數據綁定class classname1:this.classname1, classname2:this.classname2 } },this.text); } }) </script>
使用createElement工具方法迭代子節點(在前面的子節點都直接使用字符串,就是說明其是文本節點,使用createElement可以創建元素子節點):
<div id="app">我是一個div</div> <script> var vm = new Vue({ el:"#app", template:`<div>我是template模板構建的節點</div>`, data:{ classname1:true, classname2:false, text:'我時由render構建的p標簽' }, render(createElement){ return createElement("p",{ style:{ color:"red", fontSize:'18px' }, class:{//基於數據綁定class classname1:this.classname1, classname2:this.classname2 } },[ '我是一個文本節點', createElement('h1','我是h1標簽'), createElement('h2',{ style:{ color:'orange' } },'我是h2標簽') ]); } }) </script>
其實本質上render()函數與頁面渲染中的抽象語法樹構建是異曲同工,一個是基於js對象參數構建,一個是通過html文檔構建;一個是在js中完成一個是在瀏覽器底層渲染模型中完成。
二、vue實例生命周期與鈎子函數
更多的鈎子函數相關內容可以參考這篇博客: https://www.jianshu.com/p/3e91a1c42397
三、vue實例渲染的生命周期與鈎子函數詳解
Vue 渲染底層原理之render函數·Vue 實例生命周期與鈎子函數 ______________________________________________________________________________ | Init Events & Lisfecyde:初始化創建vue實例需要的相關事件與實例化生命周 | 期需要的函數。 | |____①___beforeCreate():初始化vue實例之前執行的鈎子函數。 | | Init injections & reactivity:初始化實例,將實例參數:el、data、methods等 | 添加到實例對象上,為構建虛擬DOM做好准備。 | |____②___created():初始化vue實例之后執行的鈎子函數。 | 構 | 建 el、$mount(el):判斷實例是通過哪種掛載方法。 | 抽 判斷是否采用template模板,還是通過元素的outerHTML屬性構建抽象語法 | 象 樹。(所以在這之前需要判斷是通過哪種方式掛載,如果沒有模板就需要基於 | 語 掛載的node結構來構建抽象語法樹) | 法 | 樹 |____③___beforeMount():渲染頁面之前執行的鈎子函數。 | | Create vm.$el and replace “el” with it:基於抽象語法樹與數據構建虛擬node, | 並將虛擬node渲染到頁面,形成HTML文檔,如果之前在HTML中包含定義 | 結構的元素(包含自定義元素)會被替換。(渲染頁面包含三個步驟:構建虛 | 擬節點、刪除原文節點、插入新的文件節點) | |____④___mounted():渲染頁面之后執行的鈎子函數。 | |________________________________________________________________________ | | |____⑤___beforeUpdate():重新渲染之前觸發的鈎子函數。 | | | | Mounted(觸發重新渲染—數據迭代更新) | | | |____⑥___update():重新渲染之后觸發的渲染函數。 | | ||_________________________________________________________________________| | | |____⑦___beforeDestroy():實例銷毀前觸發的鈎子函數。 | | Teardown watchers, child components and event listeners:拆卸監 | 視器、子組件和事件偵聽器。) | Destroy【銷毀實例】 | |____⑧___destroyed():實例銷毀后執行的鈎子函數。