<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【Vuejs】1094- 你真的了解vue模版編譯么?

          共 1149字,需瀏覽 3分鐘

           ·

          2021-10-02 18:53

          前述

          本文的初衷是想讓更多的同學知道并了解vue模版編譯,所以文中主要以階段流程為主,不會涉及過多的底層代碼邏輯,請耐心觀看。

          思考

          html是標簽語言,只有JS才能實現(xiàn)判斷、循環(huán),而模版有指令、插值、JS表達式,能夠實現(xiàn)判斷、循環(huán)等,故模板不是html,因此模板一定是轉換為某種JS代碼,這種編譯又是如何進行的?

          解析

          模版編譯是將template編譯成render函數(shù)的過程,這個過程大致可以分成三個階段:

          模版編譯 vue2.0.png

          階段

          parse 解析器

          解析器主要就是將?模板字符串?轉換成?element ASTs

          模板字符串


          ???

          {{message}}



          element ASTs[1]

          AST是指抽象語法樹 和 Vnode 類似,都是使用JavaScript對象來描述節(jié)點的樹狀表現(xiàn)形式

          {
          ??tag:?"div"
          ??//?節(jié)點的類型(1標簽,2包含字面量表達式的文本節(jié)點,3普通文本節(jié)點或注釋節(jié)點)
          ??type:?1,
          ??//?靜態(tài)根節(jié)點
          ??staticRoot:?false,
          ??//?靜態(tài)節(jié)點
          ??static:?false,
          ??plain:?true,
          ??//?父節(jié)點元素描述對象的引用
          ??parent:?undefined,
          ??//?只有當節(jié)點類型為1,才會有attrsList屬性,它是一個對象數(shù)組,存儲著原始的html屬性名和值
          ??attrsList:?[],
          ??//?同上,區(qū)別是attrsMap是以鍵值對的方式保存html屬性名和值的
          ??attrsMap:?{},
          ??//?存儲著該節(jié)點所有子節(jié)點的元素描述對象
          ??children:?[
          ??????{
          ??????tag:?"p"
          ??????type:?1,
          ??????staticRoot:?false,
          ??????static:?false,
          ??????plain:?true,
          ??????parent:?{tag:?"div",?...},
          ??????attrsList:?[],
          ??????attrsMap:?{},
          ??????children:?[{
          ??????????type:?2,
          ??????????text:?"{{message}}",
          ??????????static:?false,
          ??????????//?當節(jié)點類型為2時,對象會包含的表達式
          ??????????expression:?"_s(message)"
          ??????}]
          ????}
          ??]
          }

          截取的規(guī)則

          主要是通過判斷模板中html.indexof('<')的值,來確定要截取標簽還是文本.

          模版編譯 vue2.0.png

          截取的過程

          字符串部分
          `

          {{message}}

          `
          截取過程部分

          第一次截取

          1. 判斷模板中html.indexof('<')的值, 為零 (注釋、條件注釋、doctype、開始標簽、結束標簽中的一種)
          2. 被起始標簽的正則匹配成功,獲取當前的標簽名為div,然后截掉匹配成功的'>

            {{message}}

        2. 截取掉開始標簽后,會使用匹配屬性的正則去匹配,如果匹配成功,則得到該標簽的屬性列表,如果匹配不成功,則該標簽的屬性列表為空數(shù)組
        3. 截掉屬性后,會使用匹配開始標簽結束的正則去匹配,得到它是否是自閉合標簽的信息,然后截掉匹配到的字符串得到新的字符串

          {{message}}

        4. 匹配到開始標簽,判斷當前節(jié)點是否存在根節(jié)點,不存在則會創(chuàng)建一個元素類型的樹節(jié)點,存在,則將其設置為currentParent的子節(jié)點,然后將當前節(jié)點壓入stack棧中
        5. /**
          ???總結為,匹配標簽,提取屬性,建立層級
          */
          //?經(jīng)過上面的匹配,剩下的字符串部分為:
          `

          {{message}}

          `

          第二次截取

          /**
          ????同上
          */
          //?經(jīng)過上面的匹配,剩下的字符串部分為:
          `{{message}}

          `

          第三次截取

          1. 判斷模板中html.indexof('<')的值, 大于等于零 (文本、表達式中的一種)

          2. 查詢最近的一個'<',并匹配其是否符合(起始標簽、結束標簽、注釋、條件注釋中的一種),匹配成功則結束遍歷,不成功繼續(xù)遍歷

            例如:

            a < b

            => 文本部分 a < b,命中結束標簽

            a => 文本部分 a,命中開始標簽

          /**
          ???總結為,判斷類型,截取文本
          */
          //?經(jīng)過上面的匹配,剩下的字符串部分為:
          `

          `

          第四次截取

          1. 判斷模板中html.indexof('<')的值, 為零 (注釋、條件注釋、doctype、開始標簽、結束標簽中的一種)
          2. 被結束標簽的正則匹配成功,然后截掉匹配成功的

            部分,得到新的字符串
          3. 匹配到結束標簽,會從棧中彈出一個節(jié)點'p',并將棧中的最后一個節(jié)點'div'設置為currentParent
          /**
          ????總結為,匹配標簽,確定層級
          */
          //?經(jīng)過上面的匹配,剩下的字符串部分為:
          ``

          第五次截取

          /**
          ????同上
          */
          結束
          解析器總結

          optimize 優(yōu)化器

          優(yōu)化器的作用主要是對生成的AST進行靜態(tài)內(nèi)容的優(yōu)化,標記靜態(tài)節(jié)點,為了每次重新渲染,不需要為靜態(tài)子樹創(chuàng)建新節(jié)點,可以跳過虛擬DOM中patch過程(即不需要參與第二次的頁面渲染了,大大提升了渲染效率)。

          靜態(tài)節(jié)點

          遍歷AST語法樹,找出所有的靜態(tài)節(jié)點并打上標記

          function?isStatic?(node)?{
          ????//?expression
          ????if?(node.type?===?2)?{
          ??????return?false
          ????}
          ????//?text
          ????if?(node.type?===?3)?{
          ??????return?true
          ????}
          ????/**
          ??????? 1. 不能使用動態(tài)綁定語法,即標簽上不能有v-、@、:開頭的屬性;
          ??????? 2. 不能使用v-if、v-else、v-for指令;
          ??????? 3. 不能是內(nèi)置組件,即標簽名不能是slot和component;
          ??????? 4. 標簽名必須是平臺保留標簽,即不能是組件;
          ??????? 5. 當前節(jié)點的父節(jié)點不能是帶有 v-for 的 template 標簽;
          ??????? 6. 節(jié)點的所有屬性的 key 都必須是靜態(tài)節(jié)點才有的 key,注:靜態(tài)節(jié)點的key是有限的,它只能是type,tag,attrsList,attrsMap,plain,parent,children,attrs之一;
          ????*/
          ????return?!!(node.pre?||?(
          ??????!node.hasBindings?&&
          ??????!node.if?&&?!node.for?&&
          ??????!isBuiltInTag(node.tag)?&&
          ??????isPlatformReservedTag(node.tag)?&&
          ??????!isDirectChildOfTemplateFor(node)?&&
          ??????Object.keys(node).every(isStaticKey)
          ????))
          }

          靜態(tài)根節(jié)點

          遍歷經(jīng)過上面步驟后的樹,找出靜態(tài)根節(jié)點,并打上標記

          優(yōu)化器總結

          generate 代碼生成器

          代碼生成器的作用是通過AST語法樹生成代碼字符串,代碼字符串被包裝進渲染函數(shù),執(zhí)行渲染函數(shù)后,可以得到一份vnode

          JS的with語法

          使用?with,能改變{}內(nèi)自由變量的查找方式,將{}內(nèi)自由變量,當做?obj?的屬性來查找,如果找不到匹配的obj屬性,就會報錯
          const?obj?=?{a:?100,?b:?200}
          with(obj)?{
          ?????console.log(a)
          ?????console.log(b)
          ?????//?console.log(c)?//?會報錯
          }

          代碼字符串

          解析parse生成的element ASTs,拼接成字符串

          with(this){return?_c('div',_c('p',[_v(message)])])}

          得到render函數(shù)

          /**?代碼字符串通過new?Function('代碼字符串')就可以得到當前組件的render函數(shù)?*/

          const?stringCode?=?`with(this){return?_c('div',_c('p',[_v(message)])])}`

          const?render?=?new?Function(stringCode)

          欲觀看不同指令、插值、JS表達式,可使用vue-template轉換

          const?compiler?=?require('vue-template-compiler')
          //?插值
          const?template?=?`

          {{message}}

          `
          const?result?=?compiler.compile(template)
          console.log(result.render)
          //?with(this){return?_c('p',[_v(_s(message))])}

          vue 源代碼找到縮寫函數(shù)的含義

          模板編譯的源碼可以在`vue-template-compiler`[2]包中查看

          function?installRenderHelpers(target)?{
          ????target._c?=?createElement
          ????//?標記v-once
          ????target._o?=?markOnce
          ????//?轉換成Number類型
          ????target._n?=?toNumber
          ????//?轉換成字符串
          ????target._s?=?toString
          ????//?渲染v-for
          ????target._l?=?renderList
          ????//?渲染普通插槽和作用域插槽
          ????target._t?=?renderSlot
          ????//?通過staticRenderFns渲染靜態(tài)節(jié)點
          ????target._m?=?renderStatic
          ????//?獲取過濾器
          ????target._f?=?resolveFilter
          ????//?檢查鍵盤事件keycode
          ????target._k?=?checkKeyCodes
          ????target._b?=?bindObjectProps
          ????//?創(chuàng)建文本vnode
          ????target._v?=?createTextVNode
          ????//?創(chuàng)建空vnode
          ????target._e?=?createEmptyVNode
          ????target._u?=?resolveScopedSlots
          ????target._g?=?bindObjectListeners
          ????//?處理修飾符
          ????target._p?=?prependModifier
          }

          綜述

          vue腳手架中會使用vue-loader在開發(fā)環(huán)境做模板編譯(預編譯)

          解析過程是一小段一小段的去截取字符串,然后維護一個stack用來保存DOM深度,當所有字符串都截取完之后也就解析出了一個完整的AST

          優(yōu)化過程是用遞歸的方式將所有節(jié)點打標記,表示是否是一個靜態(tài)節(jié)點,然后再次遞歸一遍把靜態(tài)根節(jié)點也標記出來

          代碼生成階段是通過遞歸生成函數(shù)執(zhí)行代碼的字符串,遞歸的過程根據(jù)不同的節(jié)點類型調(diào)用不同的生成方法

          參考資料

          [1]

          element ASTs: http://caibaojian.com/vue-design/appendix/ast.html

          [2]

          vue-template-compiler: https://github.com/vuejs/vue/blob/v2.6.10/packages/vue-template-compiler/build.js

          瀏覽 75
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天综合二网 | 亚洲av看 | 亚洲黄色片免费看 | 日韩高清无码毛片 | 亚州无码中文字幕日韩AV |