欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue 中的compile操作方法

 更新時(shí)間:2018年02月26日 10:30:47   作者:小烜同學(xué)  
這篇文章主要介紹了Vue 中的compile操作方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧

在 Vue 里,模板編譯也是非常重要的一部分,里面也非常復(fù)雜,這次探究不會(huì)深入探究每一個(gè)細(xì)節(jié),而是走一個(gè)全景概要,來(lái)吧,大家和我一起去一探究竟。

初體驗(yàn)

我們看了 Vue 的初始化函數(shù)就會(huì)知道,在最后一步,它進(jìn)行了 vm.$mount(el) 的操作,而這個(gè) $mount 在兩個(gè)地方定義過(guò),分別是在 entry-runtime-with-compiler.js(簡(jiǎn)稱(chēng):eMount) 和 runtime/index.js(簡(jiǎn)稱(chēng):rMount) 這兩個(gè)文件里,那么這兩個(gè)有什么區(qū)別呢?

// entry-runtime-with-compiler.js
const mount = Vue.prototype.$mount // 這個(gè) $mount 其實(shí)就是 rMount
Vue.prototype.$mount = function (
 el?: string | Element,
 hydrating?: boolean
): Component {
 const options = this.$options
 if (!options.render) {
 ...
 if(template) {
  const { render, staticRenderFns } = compileToFunctions(template, {
  shouldDecodeNewlines,
  shouldDecodeNewlinesForHref,
  delimiters: options.delimiters,
  comments: options.comments
  }, this)
  options.render = render
  options.staticRenderFns = staticRenderFns
 }
 ...
 }
 return mount.call(this, el, hydrating)
}

其實(shí) eMount 最后還是去調(diào)用的 rMount,只不過(guò)在 eMount 做了一定的操作,如果你提供了 render 函數(shù),那么它會(huì)直接去調(diào)用 rMount,如果沒(méi)有,它就會(huì)去找你有沒(méi)有提供 template,如果你沒(méi)有提供 template,它就會(huì)用 el 去查詢(xún) dom 生成 template,最后通過(guò)編譯返回了一個(gè) render 函數(shù),再去調(diào)用 eMount。

從上面可以看出,最重要的一部分就是 compileToFunctions 這個(gè)函數(shù),它最后返回了 render 函數(shù),關(guān)于這個(gè)函數(shù),它有點(diǎn)復(fù)雜,我畫(huà)了一張圖來(lái)看一看它的關(guān)系,可能會(huì)有誤差,希望大俠們可以指出。

編譯三步走

看一下這個(gè)編譯的整體過(guò)程,我們其實(shí)可以發(fā)現(xiàn),最核心的部分就是在這里傳進(jìn)去的 baseCompile 做的工作:

  • parse: 第一步,我們需要將 template 轉(zhuǎn)換成抽象語(yǔ)法樹(shù)(AST)。
  • optimizer: 第二步,我們對(duì)這個(gè)抽象語(yǔ)法樹(shù)進(jìn)行靜態(tài)節(jié)點(diǎn)的標(biāo)記,這樣就可以?xún)?yōu)化渲染過(guò)程。
  • generateCode: 第三步,根據(jù) AST 生成一個(gè) render 函數(shù)字符串。

好了,我們接下來(lái)就一個(gè)一個(gè)慢慢看。

解析器

在解析器中有一個(gè)非常重要的概念 AST,大家可以去自行了解一下。

在 Vue 中,ASTNode 分幾種不同類(lèi)型,關(guān)于 ASTNode 的定義在 flow/compile.js 里面,請(qǐng)看下圖:

我們用一個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明一下:

<div id="demo">
 <h1>Latest Vue.js Commits</h1>
 <p>{{1 + 1}}</p>
</div>

我們想一想這段代碼會(huì)生成什么樣的 AST 呢?

我們這個(gè)例子最后生成的大概就是這么一棵樹(shù),那么 Vue 是如何去做這樣一些解析的呢?我們繼續(xù)看。

在 parse 函數(shù)中,我們先是定義了非常多的全局屬性以及函數(shù),然后調(diào)用了 parseHTML 這么一個(gè)函數(shù),這也是 parse 最核心的函數(shù),這個(gè)函數(shù)會(huì)不斷的解析模板,填充 root,最后把 root(AST) 返回回去。

parseHTML

在這個(gè)函數(shù)中,最重要的是 while 循環(huán)中的代碼,而在解析過(guò)程中發(fā)揮重要作用的有這么幾個(gè)正則表達(dá)式。

const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
const ncname = '[a-zA-Z_][\\w\\-\\.]*'
const qnameCapture = `((?:${ncname}\\:)?${ncname})`
const startTagOpen = new RegExp(`^<${qnameCapture}`)
const startTagClose = /^\s*(\/?)>/
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)
const doctype = /^<!DOCTYPE [^>]+>/i
const comment = /^<!\--/
const conditionalComment = /^<!\[/

Vue 通過(guò)上面幾個(gè)正則表達(dá)式去匹配開(kāi)始結(jié)束標(biāo)簽、標(biāo)簽名、屬性等等。

關(guān)于 while 的詳細(xì)注解我放在我倉(cāng)庫(kù)里了,有興趣的可以去看看。

在 while 里,其實(shí)就是不斷的去用 html.indexOf('<') 去匹配,然后根據(jù)返回的索引的不同去做不同的解析處理:

  • __等于 0:__這就代表這是注釋、條件注釋、doctype、開(kāi)始標(biāo)簽、結(jié)束標(biāo)簽中的某一種
  • __大于等于 0:__這就說(shuō)明是文本、表達(dá)式
  • __小于 0:__表示 html 標(biāo)簽解析完了,可能會(huì)剩下一些文本、表達(dá)式

parse 函數(shù)就是不斷的重復(fù)這個(gè)工作,然后將 template 轉(zhuǎn)換成 AST,在解析過(guò)程中,其實(shí)對(duì)于標(biāo)簽與標(biāo)簽之間的空格,Vue 也做了優(yōu)化處理,有些元素之間的空格是沒(méi)用的。

compile 其實(shí)要說(shuō)要說(shuō)非常多的篇幅,但是這里只能簡(jiǎn)單的理一下思路,具體代碼還需要各位下去深扣。

優(yōu)化器

從代碼中的注釋我們可以看出,優(yōu)化器的目的就是去找出 AST 中純靜態(tài)的子樹(shù):

把純靜態(tài)子樹(shù)提升為常量,每次重新渲染的時(shí)候就不需要?jiǎng)?chuàng)建新的節(jié)點(diǎn)了

在 patch 的時(shí)候就可以跳過(guò)它們

optimize 的代碼量沒(méi)有 parse 那么多,我們來(lái)看看:

export function optimize (root: ?ASTElement, options: CompilerOptions) {
 // 判斷 root 是否存在
 if (!root) return
 // 判斷是否是靜態(tài)的屬性
 // 'type,tag,attrsList,attrsMap,plain,parent,children,attrs'
 isStaticKey = genStaticKeysCached(options.staticKeys || '')
 // 判斷是否是平臺(tái)保留的標(biāo)簽,html 或者 svg 的
 isPlatformReservedTag = options.isReservedTag || no
 // 第一遍遍歷: 給所有靜態(tài)節(jié)點(diǎn)打上是否是靜態(tài)節(jié)點(diǎn)的標(biāo)記
 markStatic(root)
 // 第二遍遍歷:標(biāo)記所有靜態(tài)根節(jié)點(diǎn)
 markStaticRoots(root, false)
}

下面兩段代碼我都剪切了一部分,因?yàn)橛悬c(diǎn)多,這里就不貼太多代碼了,詳情請(qǐng)參考我的倉(cāng)庫(kù)。

第一遍遍歷

function markStatic (node: ASTNode) {
 node.static = isStatic(node)
 if (node.type === 1) {
 ...
 }
}

其實(shí) markStatic 就是一個(gè)遞歸的過(guò)程,不斷地去檢查 AST 上的節(jié)點(diǎn),然后打上標(biāo)記。

剛剛我們說(shuō)過(guò),AST 節(jié)點(diǎn)分三種,在 isStatic 這個(gè)函數(shù)中我們對(duì)不同類(lèi)型的節(jié)點(diǎn)做了判斷:

function isStatic (node: ASTNode): boolean {
 if (node.type === 2) { // expression
 return false
 }
 if (node.type === 3) { // text
 return true
 }
 return !!(node.pre || (
 !node.hasBindings && // no dynamic bindings
 !node.if && !node.for && // not v-if or v-for or v-else
 !isBuiltInTag(node.tag) && // not a built-in
 isPlatformReservedTag(node.tag) && // not a component
 !isDirectChildOfTemplateFor(node) &&
 Object.keys(node).every(isStaticKey)
 ))
}

可以看到 Vue 對(duì)下面幾種情況做了處理:

當(dāng)這個(gè)節(jié)點(diǎn)的 type 為 2,也就是表達(dá)式節(jié)點(diǎn)的時(shí)候,很明顯它不是一個(gè)靜態(tài)節(jié)點(diǎn),所以返回 false

當(dāng) type 為 3 的時(shí)候,也就是文本節(jié)點(diǎn),那它就是一個(gè)靜態(tài)節(jié)點(diǎn),返回 true

如果你在元素節(jié)點(diǎn)中使用了 v-pre 或者使用了 <pre> 標(biāo)簽,就會(huì)在這個(gè)節(jié)點(diǎn)上加上 pre 為 true,那么這就是個(gè)靜態(tài)節(jié)點(diǎn)

如果它是靜態(tài)節(jié)點(diǎn),那么需要它不能有動(dòng)態(tài)的綁定、不能有 v-if、v-for、v-else 這些指令,不能是 slot 或者 component 標(biāo)簽、不是我們自定義的標(biāo)簽、沒(méi)有父節(jié)點(diǎn)或者元素的父節(jié)點(diǎn)不能是帶 v-for 的 template、 這個(gè)節(jié)點(diǎn)的屬性都在 type,tag,attrsList,attrsMap,plain,parent,children,attrs 里面,滿(mǎn)足這些條件,就認(rèn)為它是靜態(tài)的節(jié)點(diǎn)。

接下來(lái),就開(kāi)始對(duì) AST 進(jìn)行遞歸操作,標(biāo)記靜態(tài)的節(jié)點(diǎn),至于里面做了哪些操作,可以到上面那個(gè)倉(cāng)庫(kù)里去看,這里就不展開(kāi)了。

第二遍遍歷

第二遍遍歷的過(guò)程是標(biāo)記靜態(tài)根節(jié)點(diǎn),那么我們對(duì)靜態(tài)根節(jié)點(diǎn)的定義是什么,首先根節(jié)點(diǎn)的意思就是他不能是葉子節(jié)點(diǎn),起碼要有子節(jié)點(diǎn),并且它是靜態(tài)的。在這里 Vue 做了一個(gè)說(shuō)明,如果一個(gè)靜態(tài)節(jié)點(diǎn)它只擁有一個(gè)子節(jié)點(diǎn)并且這個(gè)子節(jié)點(diǎn)是文本節(jié)點(diǎn),那么就不做靜態(tài)處理,它的成本大于收益,不如直接渲染。

同樣的,我們?cè)诤瘮?shù)中不斷的遞歸進(jìn)行標(biāo)記,最后在所有靜態(tài)根節(jié)點(diǎn)上加上 staticRoot 的標(biāo)記,關(guān)于這段代碼也可以去上面的倉(cāng)庫(kù)看一看。

代碼生成器

在這個(gè)函數(shù)中,我們將 AST 轉(zhuǎn)換成為 render 函數(shù)字符串,代碼量還是挺多的,我們可以來(lái)看一看。

export function generate (
 ast: ASTElement | void,
 options: CompilerOptions
): CodegenResult {
 // 這就是編譯的一些參數(shù)
 const state = new CodegenState(options)
 // 生成 render 字符串
 const code = ast ? genElement(ast, state) : '_c("div")'
 return {
 render: `with(this){return $[code]}`,
 staticRenderFns: state.staticRenderFns
 }
}

可以看到在最后代碼生成階段,最重要的函數(shù)就是 genElement 這個(gè)函數(shù),針對(duì)不同的指令、屬性,我們會(huì)選擇不同的代碼生成函數(shù)。最后我們按照 AST 生成拼接成一個(gè)字符串,如下所示:

with(this){return _c('div',{attrs:{"id":"demo"}},[(1>0)?_c('h1',[_v("Latest Vue.js Commits")]):_e(),...}

在 render 這個(gè)函數(shù)字符串中,我們會(huì)看到一些函數(shù),那么這些函數(shù)是在什么地方定義的呢?我們可以在 core/instance/index.js 這個(gè)文件中找到這些函數(shù):

// v-once
target._o = markOnce
// 轉(zhuǎn)換
target._n = toNumber
target._s = toString
// v-for
target._l = renderList
// slot
target._t = renderSlot
// 是否相等
target._q = looseEqual
// 檢測(cè)數(shù)組里是否有相等的值
target._i = looseIndexOf
// 渲染靜態(tài)樹(shù)
target._m = renderStatic
// 過(guò)濾器處理
target._f = resolveFilter
// 檢查關(guān)鍵字
target._k = checkKeyCodes
// v-bind
target._b = bindObjectProps
// 創(chuàng)建文本節(jié)點(diǎn)
target._v = createTextVNode
// 創(chuàng)建空節(jié)點(diǎn)
target._e = createEmptyVNode
// 處理 scopeslot
target._u = resolveScopedSlots
// 處理事件綁定
target._g = bindObjectListeners
// 創(chuàng)建 VNode 節(jié)點(diǎn)
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)

在編譯結(jié)束后,我們根據(jù)不同的指令、屬性等等去選擇需要調(diào)用哪一個(gè)處理函數(shù),最后拼接成一個(gè)函數(shù)字符串。

我們可以很清楚的看到,最后生成了一個(gè) render 渲染字符串,那么我們要如何去使用它呢?其實(shí)在后面進(jìn)行渲染的時(shí)候,我們進(jìn)行了 new Function(render) 的操作,然后我們就能夠正常的使用 render 函數(shù)了。

總結(jié)

大流程走完之后,我相信大家會(huì)對(duì)編譯過(guò)程有一個(gè)比較清晰的認(rèn)識(shí),然后再去挖細(xì)節(jié)相信也會(huì)容易的多了,讀源碼,其實(shí)并不是一個(gè)為了讀而讀的過(guò)程,我們可以在源碼中學(xué)到很多我們可能在日常開(kāi)發(fā)中沒(méi)有了解到的知識(shí)。
至于最后代碼生成器中的那一大段代碼,我還沒(méi)有把它注釋好,后面應(yīng)該會(huì)將源碼注釋放到倉(cāng)庫(kù)里,不過(guò)我也相信大家也能夠順利的去讀懂源碼。

還有一點(diǎn)要提的是在 render 函數(shù)中,Vue 使用了 with 函數(shù),我們平時(shí)肯定沒(méi)見(jiàn)過(guò),因?yàn)楣俜讲煌扑]我們?nèi)ナ褂?with,我抱著這樣的想法去找了找原因,最后我在知乎上找到了尤大大的回答,這是鏈接,大家可以去了解下。

以上所述是小編給大家介紹的Vue 中的compile,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!

相關(guān)文章

  • Vue中?引入使用?babel-polyfill?兼容低版本瀏覽器的方法

    Vue中?引入使用?babel-polyfill?兼容低版本瀏覽器的方法

    最近在項(xiàng)目中使用 webpack 打包后升級(jí),用戶(hù)反饋使用瀏覽器(chrome 45)訪(fǎng)問(wèn)白屏。經(jīng)過(guò)排查發(fā)現(xiàn):由于 chrome 45 無(wú)法兼容 ES6 語(yǔ)法導(dǎo)致的,接下來(lái)給大家介紹下Vue中?引入使用?babel-polyfill?兼容低版本瀏覽器方法,需要的朋友可以參考下
    2023-02-02
  • vue axios數(shù)據(jù)請(qǐng)求get、post方法及實(shí)例詳解

    vue axios數(shù)據(jù)請(qǐng)求get、post方法及實(shí)例詳解

    axios是一個(gè)基于Promise,同時(shí)支持瀏覽器端和Node.js的HTTP庫(kù),常用于Ajax請(qǐng)求。這篇文章主要介紹了vue axios數(shù)據(jù)請(qǐng)求get、post方法的使用 ,需要的朋友可以參考下
    2018-09-09
  • vue3 element-plus el-tree自定義圖標(biāo)方式

    vue3 element-plus el-tree自定義圖標(biāo)方式

    這篇文章主要介紹了vue3 element-plus el-tree自定義圖標(biāo)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • watch里面的deep和immediate作用及說(shuō)明

    watch里面的deep和immediate作用及說(shuō)明

    這篇文章主要介紹了watch里面的deep和immediate作用及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • vuejs通過(guò)filterBy、orderBy實(shí)現(xiàn)搜索篩選、降序排序數(shù)據(jù)

    vuejs通過(guò)filterBy、orderBy實(shí)現(xiàn)搜索篩選、降序排序數(shù)據(jù)

    這篇文章主要為大家詳細(xì)介紹了vuejs通過(guò)filterBy、orderBy實(shí)現(xiàn)搜索篩選、降序排序數(shù)據(jù)實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • 利用Vue3+Element?Plus封裝公共表格組件(帶源碼)

    利用Vue3+Element?Plus封裝公共表格組件(帶源碼)

    最近公司項(xiàng)目中頻繁會(huì)使用到table表格,而且前端技術(shù)這一塊也用到了vue3來(lái)開(kāi)發(fā),所以基于element plus table做了一個(gè)二次封裝的組件,這篇文章主要給大家介紹了關(guān)于利用Vue3+Element?Plus封裝公共表格組件的相關(guān)資料,需要的朋友可以參考下
    2023-11-11
  • 利用nginx部署vue項(xiàng)目的全過(guò)程

    利用nginx部署vue項(xiàng)目的全過(guò)程

    今天要用到服務(wù)器nginx,還需要把自己的vue的項(xiàng)目部署到服務(wù)器上去所以就寫(xiě)一下記錄下來(lái),下面這篇文章主要給大家介紹了關(guān)于利用nginx部署vue項(xiàng)目的相關(guān)資料,需要的朋友可以參考下
    2022-03-03
  • Vue.js結(jié)合Ueditor富文本編輯器的實(shí)例代碼

    Vue.js結(jié)合Ueditor富文本編輯器的實(shí)例代碼

    本篇文章主要介紹了Vue.js結(jié)合Ueditor的項(xiàng)目實(shí)例代碼,這里整理了詳細(xì)的代碼,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-07-07
  • vue 導(dǎo)航守衛(wèi)和axios攔截器有哪些區(qū)別

    vue 導(dǎo)航守衛(wèi)和axios攔截器有哪些區(qū)別

    這篇文章主要介紹了vue 導(dǎo)航守衛(wèi)和axios攔截器有哪些區(qū)別,幫助大家更好的理解和使用vue,感興趣的朋友可以了解下
    2020-12-12
  • 最適應(yīng)的vue.js的form提交涉及多種插件【推薦】

    最適應(yīng)的vue.js的form提交涉及多種插件【推薦】

    這篇文章主要介紹了最適應(yīng)的vue.js的form提交涉及多種插件,涉及到 vue.js動(dòng)態(tài)添加css樣式 ,tab切換 ,touch,表單提交,驗(yàn)證,toast,數(shù)據(jù)雙向綁定等。需要的朋友可以參考下
    2018-08-08

最新評(píng)論