寫文章不容易,點個讚唄兄弟
專一 Vue 源碼分享,文章分爲白話版和 源碼版,白話版助於理解工做原理,源碼版助於瞭解內部詳情,讓咱們一塊兒學習吧
研究基於 Vue版本 【2.5.17】
若是你以爲排版難看,請點擊 下面連接 或者 拉到 下面關注公衆號也能夠吧node
終於到了要講 compile 白話的時候了,你們準備好了嗎,白話版確定不會很複雜啦,源碼版就不必定了。。。性能優化
源碼版我寫了9篇啊!每篇的篇幅都很長啊!!我都快寫奔潰了啊!!函數
都快堅持不下來了,我算了算, compile 的源碼版,我好像快寫了一個多月???性能
臥槽,居然寫了這麼久.....學習
好吧,如今開始咱們的正文優化
compile 的內容很是多,大體分爲三塊主要內容,我也稱他們是Vue的 渲染三巨頭spa
就是 parse,optimize,generate3d
雖然分爲三塊,可是要明確一點code
compile 的做用是解析模板,生成渲染模板的 render
通過 compile 以後,就會生成下面的 render
_c('div', [_c('span'), _v(num)])
而 render 的做用,也是爲了生成跟模板節點一一對應的 Vnode
{ tag: "div", children:[{ tag: "span", text: undefined },{ tag: undefined text: "111" }] }
下面咱們就來一個個看渲染三巨頭
這是 compile 的第一個步驟
接收 template 原始模板,按照模板的節點 和數據 生成對應的 ast
生成的 ast 是這樣,全部模板中出現的數據,你均可以在 ast 中找到
{ tag: "div", attrsMap: {test: "2"}, children:[{ tag: "span", children: [], attrsMap: {name: "1"} }] }
ast 是什麼?我的簡單理解的話
以數據的形式去描述一個東西的全部的特徵吧,說錯別打我
好比說ast 描述我
{ name: "神仙朱", sex: 1, desc: "一個靚仔" }
具體能夠查一下,相關內容挺多的
另外,這裏不會講細節,parse 是怎麼生成 ast 的,由於涉及不少源碼,放在源碼版了
這是 compile 的第二步
遍歷遞歸每個ast節點,標記靜態的節點(沒有綁定任何動態數據),
這樣就知道那部分不會變化,因而在頁面須要更新時,減小去比對這部分DOM
從而達到性能優化的目的
span 和 b 就是靜態節點,在 optimize 處理中,就會給他們添加 static 判斷是不是靜態節點
{ static: false, staticRoot: false, tag: "div", children: [{ staticRoot: true, tag: "span", children: [{ static: true, tag: "b" }] },{ static: false, text: "{{a}}" }] }
而你也看到一個屬性,staticRoot,這個是表示這個節點是不是靜態根節點的意思
用來標記 某部分靜態節點 最大的祖宗節點,後面更新的時候,只要碰到這個屬性,就知道他的全部子孫節點都是靜態節點了,而不須要每一個子孫節點都要判斷一次浪費時間
具體是怎麼作的,感興趣的話歡迎看之後的源碼版
這是 compile 的第三步
把前兩步生成完善的 ast 組裝成 render 字符串(這個 render 變成函數後是可執行的函數,不過如今是字符串的形態,後面會轉成函數)
通過前兩步變成 ast
{ static: false, staticRoot: false, tag: "div", children: [{ static: false, staticRoot: false, tag: "span", children: [{ static: false, text: "{{b}}" }] },{ static: false, text: "{{a}}" }] }
而後,generate 接收 ast,先處理最外層 ast,而後開始遞歸遍歷子節點,直到全部節點被處理完
這個過程當中,字符串會被一點一點拼接完成,好比上面的 ast 拼接結果就是下面這樣
_c 是生成節點對應的 Vnode 的一個函數
` _c('div', [ _c('span', [ _v(b) ]), _v(a) ]) `
一、一開始接收到 ast,處理最外層 ast 這個點,是 div,因而拼接獲得字符串
code = ` _c('div', [ `
二、遍歷 div 子節點,遇到 span,拼接在 div 的子節點數組中
code = `_c('div', [ _c('span', [ `
三、開始處理 span 的子節點 b,放進 span 的 子節點數組中
code = ` _c('div', [ _c('span', [ _v(b) `
四、span 子節點處理完,閉合 span 的 子節點數組
code = ` _c('div', [ _c('span', [ _v(b) ] `
五、繼續處理 span 同級 的子節點,是個文本節點,可是是動態值,變量是 a
code = `_c('div', [ _c('span', [ _v(b) ] , _v(a) `
六、全部子節點都處理完畢,閉合 div 的 子節點數組
code = ` _c('div', [ _c('span', [ _v(b) ] , _v(a) )] `
前面兩步把 template 解析生成了 render 字符串,可是須要執行的話,仍是須要轉換成函數的
怎麼轉呢?就是下面這樣
render = new Function(render)
而後 render 保存在實例上,具體位置是
vm.$options.render
至此,compile 全部的功能就完成了
而關於 render 的內容,好比說 render 中出現的各類函數是什麼,會專門放在 render 的文章去記錄