Vue渲染過程淺析
Vue 推薦在絕大多數(shù)情況下使用 template 來創(chuàng)建你的 HTML。但是模板畢竟是模板,不是真實的dom節(jié)點。從模板到真實dom節(jié)點還需要經(jīng)過一些步驟
- 把模板編譯為render函數(shù)
- 實例進行掛載, 根據(jù)根節(jié)點render函數(shù)的調(diào)用,遞歸的生成虛擬dom
- 對比虛擬dom,渲染到真實dom
- 組件內(nèi)部data發(fā)生變化,組件和子組件引用data作為props重新調(diào)用render函數(shù),生成虛擬dom, 返回到步驟3
第一步: 模板到render
在我們使用Vue的組件化進行開發(fā)應(yīng)用的時候, 如果仔細的查看我們要引入的組件, 例子如下
// App.vue
<template>
<div>
hello word
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
在我們的主入口main.js
import Vue from 'vue'
import App from './App'
console.log(App)
new Vue({
render: h => h(App)
}).$mount('#app')

我們能夠看到在我們引入的App這個模塊,里面是一個對象,對象里面存在一個方法叫做render。在說render函數(shù)之前,我們可以想一想,每一次加載一個組件,然后對模板進行解析,解析完后,生成Dom,掛載到頁面上。這樣會導(dǎo)致效率很低效。而使用Vue-cli進行組件化開發(fā),在我們引入組件的后,其實會有一個解析器(vue-loader)對此模板進行了解析,生成了render函數(shù)。當(dāng)然,如果沒有通過解析器解析為render函數(shù),也沒有關(guān)系,在組件第一次掛載的時候,Vue會自己進行解析。源碼請參考: https://github.com/vuejs/vue/blob/dev/src/platforms/web/entry-runtime-with-compiler.js
這樣,能保證組件每次調(diào)用的都是render函數(shù),使用render函數(shù)生成VNode。
第二步:虛擬節(jié)點VNode
我們把Vue的實例掛載到#app, 會調(diào)用實例里面的render方法,生成虛擬DOM。來看看什么是虛擬節(jié)點,把例子修改一下。
new Vue({
render: h => {
let root = h(App)
console.log('root:', root)
return root
}
}).$mount('#app')

上面生成的VNode就是虛擬節(jié)點,虛擬節(jié)點里面有一個屬性elm, 這個屬性指向真實的DOM節(jié)點。因為VNode指向了真實的DOM節(jié)點,那么虛擬節(jié)點經(jīng)過對比后,生成的DOM節(jié)點就可以直接進行替換。
這樣有什么好處呢?
一個組件對象,如果內(nèi)部的data發(fā)生變化,觸發(fā)了render函數(shù),重新生成了VNode節(jié)點。那么就可以直接找到所對應(yīng)的節(jié)點,然后直接替換。那么這個過程只會在本組件內(nèi)發(fā)生,不會影響其他的組件。于是組件與組件是隔離的。
例子如下:
// main.js
const root = new Vue({
data: {
state: true
},
mounted() {
setTimeout(() => {
console.log(this)
this.state = false
}, 1000)
},
render: function(h) {
const { state } = this // state 變化重新觸發(fā)render
let root = h(App)
console.log('root:', root)
return root
}
}).$mount('#app')
// App.vue
<script>
export default {
render: (h) => {
let app = h('h1', ['hello world'])
console.log('app:', app)
return app
}
}
</script>

我們可以看到,當(dāng)main.js中重新觸發(fā)render函數(shù)的時候,render方法里面有引用App.vue這個子組件。但是并沒有觸發(fā)App.vue組件的的render函數(shù)。
在一個組件內(nèi),什么情況會觸發(fā)render?。
如何才能觸發(fā)組件的render
數(shù)據(jù)劫持是Vue的一大特色,原理官方已經(jīng)講的很多了深入響應(yīng)式原理。在我們給組件的data的屬性進行的賦值的時候(set),此屬性如果在組件內(nèi)部初次渲染過程被引用(data的屬性被訪問,也就是數(shù)據(jù)劫持的get), 包括生命周期方法或者render方法。于是會觸發(fā)組件的update(beforeUpdate -> render -> updated)。
注: 為了防止data被多次set從而觸發(fā)多次update, Vue把update存放到異步隊列中。這樣就能保證多次data的set只會觸發(fā)一次update。
當(dāng)props會觸發(fā)組件的重新渲染是怎么發(fā)生的呢?
把父組件的data通過props傳遞給子組件的時候,子組件在初次渲染的時候生命周期或者render方法,有調(diào)用data相關(guān)的props的屬性, 這樣子組件也被添加到父組件的data的相關(guān)屬性依賴中,這樣父組件的data在set的時候,就相當(dāng)于觸發(fā)自身和子組件的update。
例子如下:
// main.vue
import Vue from 'vue'
import App from './App'
const root = new Vue({
data: {
state: false
},
mounted() {
setTimeout(() => {
this.state = true
}, 1000)
},
render: function(h) {
const { state } = this // state 變化重新觸發(fā)render
let root = h(App, { props: { status: state } })
console.log('root:', root)
return root
}
}).$mount('#app')
window.root = root
// App.vue
<script>
export default {
props: {
status: Boolean
},
render: function (h){
const { status } = this
let app = h('h1', ['hello world'])
console.log('app:', app)
return app
}
}
</script>
截圖如下:

在main.js中 state 狀態(tài)發(fā)生了變化,由false => true, 觸發(fā)了自身與子組件的render方法。
補充
上面的內(nèi)容是本人的一些使用心得,由于水平有限, 內(nèi)容有些錯誤或者表達不當(dāng)。多歡迎大神來指導(dǎo)?。。?/p>
PS:vue渲染過程的{{xxx}}顯示的解決辦法
這是由于瀏覽器的渲染機制導(dǎo)致的,瀏覽器是從頭到尾 如果你的js引用在底部,那么瀏覽器會先加載dom此時,你用于渲染的{{}}識別符,因為還沒讀到該識別符對應(yīng)的js文件,所以會被解析為字符串而顯示在頁面中,我們可以用過自定義屬性v-cloak解決,
實例對象對應(yīng)標(biāo)簽中加入 v-cloak:
<div id="wrap" v-cloak>
然后在css中給定義屬性選擇器
[v-cloak]{
display:none
}
vue實例創(chuàng)建完成后會把v-cloak去掉,在沒創(chuàng)建實例對象時,該標(biāo)簽內(nèi)的內(nèi)容都會被隱藏
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Vue動態(tài)創(chuàng)建注冊component的實例代碼
這篇文章主要給大家介紹了關(guān)于Vue動態(tài)創(chuàng)建注冊component的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用Vue具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06
使用FileReader API創(chuàng)建Vue文件閱讀器組件
這篇文章主要介紹了使用FileReader API創(chuàng)建一個Vue的文件閱讀器組件,需要的朋友可以參考下2018-04-04
Vue利用computed配合watch實現(xiàn)監(jiān)聽多個屬性的變化
這篇文章主要給大家介紹了在Vue中巧用computed配合watch實現(xiàn)監(jiān)聽多個屬性的變化的方法,文中有詳細的代碼示例供大家參考,具有一定的參考價值,需要的朋友可以參考下2023-10-10
代替Vue?Cli的全新腳手架工具create?vue示例解析
這篇文章主要為大家介紹了代替Vue?Cli的全新腳手架工具create?vue示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10
vue自定義組件實現(xiàn)v-model雙向綁定數(shù)據(jù)的實例代碼
vue中父子組件通信,都是單項的,直接在子組件中修改prop傳的值vue也會給出一個警告,接下來就用一個小列子一步一步實現(xiàn)了vue自定義的組件實現(xiàn)v-model雙向綁定,需要的朋友可以參考下2021-10-10
通過vue-cli來學(xué)習(xí)修改Webpack多環(huán)境配置和發(fā)布問題
這篇文章主要介紹了隨著Vue-cli來'學(xué)'并'改'Webpack之多環(huán)境配置和發(fā)布的相關(guān)知識,本文將會根據(jù)一些實際的業(yè)務(wù)需求,先學(xué)習(xí)vue-cli生成的模版,然后在進行相關(guān)修改,感興趣的朋友一起跟著小編學(xué)習(xí)吧2017-12-12
VUE中Echarts的resize事件報錯和移除windows的事件問題
這篇文章主要介紹了VUE中Echarts的resize事件報錯和移除windows的事件問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07

