vue data 值如何渲染_Vue执行流程解析
引言
相信絕大多數的前端小伙伴已記不清做了多少項目,寫了多少代碼了,每個人如同教科書般地寫著Vue代碼:
// 單文件組件中常見代碼 export default {data () {return {msg: 'click me'}},methods: {say () {this.msg = 'well done'}} }// 入口文件中的常見代碼 new Vue({el: '#app',router: router,render: h => h(App) })一切都顯得那么自然。不過在百忙之中是否有小伙伴想過,一個小小的Vue實例怎么有這么大的能量,竟然可以構建出如此復雜的前端項目。那么Vue內部是如何運轉的呢,做了哪些事情呢,從今天開始跟著我一探究竟。
vue是可以運行在多平臺上的如瀏覽器,weex等,本文只分析vue在瀏覽器環境下的主線執行流程。初始化
我們先看一下Vue的構造函數:
// Vue構造函數 function Vue (options) {if (process.env.NODE_ENV !== 'production' &&!(this instanceof Vue)) {warn('Vue is a constructor and should be called with the `new` keyword')}// 執行初始化邏輯this._init(options) }從Vue的構造函數中可以看到,當我們執行new Vue()的時候,只執行了一個_init方法。_init會根據傳入的選項對vue進行初始化。props、data、生命周期,事件機制的初始化都是在此過程中完成的。
以data的初始化為例,vue會通過 Object.defineProperty 的方式將data的屬性定義到vue實例上。這也就解釋了為什么我們可以在vue中通過對 this.msg 進行賦值,可以修改data中屬性的值了。
以上對data的處理只是剛剛開始。為了能實現所謂的響應式或者數據驅動更新,vue又做了進一步的處理,具體做法是,創建一個observer對象,該對象與data綁定,通過 Object.defineProperty 將data中的所有的屬性轉換成getter/setter。當data中的屬性在vue實例中被訪問(會觸發getter),observer 對象就會把該屬性收集為watcher實例的依賴,之后當data中的屬性在vue實例中被改變(會觸發setter), observer 會通知依賴該屬性的 watcher 實例重新渲染頁面。
注:每個watcher都對應一個vue實例以上處理流程串在一起,vue就實現了通過修改 this.msg 從而觸發頁面的自動更新。
最后借用vue官網上的一張示意圖幫助大家再理解下這個處理過程:
模板解析
通過上面的分析,我們已經知道當數據發生變化時,會觸發頁面的重新渲染。接下來我們分析下vue是如何進行渲染的。
首先,vue會把將我們編寫的HTML模板解析成一個AST描述對象,該對象是通過children和parent鏈接而成的樹形結構,完整地描述了HTML標簽的所有信息。
例如有如下HTML模板:
<div id="app"><p>{{msg}}</p> </div>最終會解析成如下形式的AST對象:
{attrs: [{name: "id", value: ""app"", dynamic: undefined, start: 5, end: 13}],attrsList: [{name: "id", value: "app", start: 5, end: 13}],attrsMap: {id: "app"},children: [{attrsList: [],attrsMap: {},children: [],end: 33,parent: {type: 1, tag: "div", ...},plain: true,pre: undefined,rawAttrsMap:{},start: 19tag: "p",type: 1}],end: 263,parent: undefined,plain: false,rawAttrsMap:{id: {name: "id", value: "app", start: 5, end: 13}},start: 0tag: "div",type: 1 }然后 vue 根據AST對象生成 render 函數,該函數的函數體大致如下:
with(this){return _c('div', {attrs:{"id":"app"}}, [_c('p', [_v(_s(msg))])]) }也就是說,我們的模板最終在vue內部都是會以一個render函數的形式存在。
vue官網上對此也有提及,一般推薦大家使用template,el等方式來指定模板,此外還可以通過使用render來自定義個性化的編譯函數,不過vue內部最終都會解析成render函數。先虛后實
我們得到render函數之后,vue并未直接渲染成DOM樹,而是先通過render函數得到一個vnode。實際上這一步是非常有必要的,我們都知道頻繁大量地操作DOM節點是極耗性能的。vue在渲染之前通過對vnode的比較,可以大大規避非必要的DOM操作。下面是一個vnode大致結構:
{tag: "div",children: [{tag: "p", ...}],data: {attrs: {id: "app"}}elm: DOM節點(div#app),parent: undefined,context: Vue實例,... }最后,vue根據diff之后的結果,執行真正的dom節點的插入更新刪除等操作,同時觸發vue實例的生命周期鉤子函數。之后,vue要做的就是觀察數據的變化,進而決定是否重新渲染頁面了。
總結
以上就是vue在初始渲染過程中的主干流程,大體總結起來就是先對選項對象初始化,通過Object.defineProperty建立一套響應式系統,然后將模板解析成render函數,然后使用render函數生成vnode,在渲染前,對vnode進行diff操作,最后進行必要的渲染。
本文并未深入每個執行中的代碼細節,接下來會詳細對初始化、響應式實現原理、模板渲染、指令解析、vnode的diff等進行介紹,敬請期待。
推薦閱讀
- 如何使用 splitChunks 精細控制代碼分割
- 用了這么久的react/vue,你真的了解hooks嗎?
總結
以上是生活随笔為你收集整理的vue data 值如何渲染_Vue执行流程解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java判断括号是否闭合_【python
- 下一篇: vue可视化拖拽生成工具_vdesjs: