中文字幕日韩精品一区二区免费_精品一区二区三区国产精品无卡在_国精品无码专区一区二区三区_国产αv三级中文在线

Vue3模板編譯優(yōu)化的示例分析

小編給大家分享一下Vue3模板編譯優(yōu)化的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

創(chuàng)新互聯(lián)從2013年創(chuàng)立,先為蕪湖等服務(wù)建站,蕪湖等地企業(yè),進行企業(yè)商務(wù)咨詢服務(wù)。為蕪湖企業(yè)網(wǎng)站制作PC+手機+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。

編譯入口

了解過 Vue3 的同學(xué)肯定知道 Vue3 引入了新的組合 Api,在組件 mount 階段會調(diào)用 setup 方法,之后會判斷 render  方法是否存在,如果不存在會調(diào)用 compile 方法將 template 轉(zhuǎn)化為 render。

// packages/runtime-core/src/renderer.ts const mountComponent = (initialVNode, container) => {   const instance = (     initialVNode.component = createComponentInstance(       // ...params     )   )   // 調(diào)用 setup   setupComponent(instance) }  // packages/runtime-core/src/component.ts let compile export function registerRuntimeCompiler(_compile) {   compile = _compile } export function setupComponent(instance) {   const Component = instance.type   const { setup } = Component   if (setup) {     // ...調(diào)用 setup   }   if (compile && Component.template && !Component.render) {    // 如果沒有 render 方法     // 調(diào)用 compile 將 template 轉(zhuǎn)為 render 方法     Component.render = compile(Component.template, {...})   } }

這部分都是 runtime-core 中的代碼,之前的文章有講過 Vue 分為完整版和 runtime 版本。如果使用 vue-loader 處理  .vue 文件,一般都會將 .vue 文件中的 template 直接處理成 render 方法。

//  需要編譯器 Vue.createApp({   template: '<div>{{ hi }}</div>' })  // 不需要 Vue.createApp({   render() {     return Vue.h('div', {}, this.hi)   } })

完整版與 runtime 版的差異就是,完整版會引入 compile 方法,如果是 vue-cli 生成的項目就會抹去這部分代碼,將 compile  過程都放到打包的階段,以此優(yōu)化性能。runtime-dom 中提供了 registerRuntimeCompiler 方法用于注入 compile  方法。

Vue3模板編譯優(yōu)化的示例分析

主流程

在完整版的 index.js 中,調(diào)用了 registerRuntimeCompiler 將 compile 進行注入,接下來我們看看注入的  compile 方法主要做了什么。

// packages/vue/src/index.ts import { compile } from '@vue/compiler-dom'  // 編譯緩存 const compileCache = Object.create(null)  // 注入 compile 方法 function compileToFunction(  // 模板   template: string | HTMLElement,   // 編譯配置   options?: CompilerOptions ): RenderFunction {   if (!isString(template)) {     // 如果 template 不是字符串     // 則認為是一個 DOM 節(jié)點,獲取 innerHTML     if (template.nodeType) {       template = template.innerHTML     } else {       return NOOP     }   }    // 如果緩存中存在,直接從緩存中獲取   const key = template   const cached = compileCache[key]   if (cached) {     return cached   }    // 如果是 ID 選擇器,這獲取 DOM 元素后,取 innerHTML   if (template[0] === '#') {     const el = document.querySelector(template)     template = el ? el.innerHTML : ''   }    // 調(diào)用 compile 獲取 render code   const { code } = compile(     template,     options   )    // 將 render code 轉(zhuǎn)化為 function   const render = new Function(code)();   // 返回 render 方法的同時,將其放入緩存   return (compileCache[key] = render) }  // 注入 compile registerRuntimeCompiler(compileToFunction)

在講 Vue2 模板編譯的時候已經(jīng)講過,compile 方法主要分為三步,Vue3 的邏輯類似:

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2. 模板編譯,將模板代碼轉(zhuǎn)化為 AST;

  3. 優(yōu)化 AST,方便后續(xù)虛擬 DOM 更新;

  4. 生成代碼,將 AST 轉(zhuǎn)化為可執(zhí)行的代碼;

// packages/compiler-dom/src/index.ts import { baseCompile, baseParse } from '@vue/compiler-core' export function compile(template, options) {   return baseCompile(template, options) }  // packages/compiler-core/src/compile.ts import { baseParse } from './parse' import { transform } from './transform'  import { transformIf } from './transforms/vIf' import { transformFor } from './transforms/vFor' import { transformText } from './transforms/transformText' import { transformElement } from './transforms/transformElement'  import { transformOn } from './transforms/vOn' import { transformBind } from './transforms/vBind' import { transformModel } from './transforms/vModel'  export function baseCompile(template, options) {   // 解析 html,轉(zhuǎn)化為 ast   const ast = baseParse(template, options)   // 優(yōu)化 ast,標記靜態(tài)節(jié)點   transform(ast, {     ...options,     nodeTransforms: [       transformIf,       transformFor,       transformText,       transformElement,       // ... 省略了部分 transform     ],     directiveTransforms: {       on: transformOn,       bind: transformBind,       model: transformModel     }   })   // 將 ast 轉(zhuǎn)化為可執(zhí)行代碼   return generate(ast, options) }

計算 PatchFlag

這里大致的邏輯與之前的并沒有多大的差異,主要是 optimize 方法變成了 transform 方法,而且默認會對一些模板語法進行  transform。這些 transform 就是后續(xù)虛擬 DOM 優(yōu)化的關(guān)鍵,我們先看看 transform 的代碼 。

// packages/compiler-core/src/transform.ts export function transform(root, options) {   const context = createTransformContext(root, options)   traverseNode(root, context) } export function traverseNode(node, context) {   context.currentNode = node   const { nodeTransforms } = context   const exitFns = []   for (let i = 0; i < nodeTransforms.length; i++) {     // Transform 會返回一個退出函數(shù),在處理完所有的子節(jié)點后再執(zhí)行     const onExit = nodeTransforms[i](node, context)     if (onExit) {       if (isArray(onExit)) {         exitFns.push(...onExit)       } else {         exitFns.push(onExit)       }     }   }   traverseChildren(node, context)   context.currentNode = node   // 執(zhí)行所以 Transform 的退出函數(shù)   let i = exitFns.length   while (i--) {     exitFns[i]()   } }

我們重點看一下 transformElement 的邏輯:

// packages/compiler-core/src/transforms/transformElement.ts export const transformElement: NodeTransform = (node, context) => {   // transformElement 沒有執(zhí)行任何邏輯,而是直接返回了一個退出函數(shù)   // 說明 transformElement 需要等所有的子節(jié)點處理完后才執(zhí)行   return function postTransformElement() {     const { tag, props } = node      let vnodeProps     let vnodePatchFlag     const vnodeTag = node.tagType === ElementTypes.COMPONENT       ? resolveComponentType(node, context)       : `"${tag}"`          let patchFlag = 0     // 檢測節(jié)點屬性     if (props.length > 0) {       // 檢測節(jié)點屬性的動態(tài)部分       const propsBuildResult = buildProps(node, context)       vnodeProps = propsBuildResult.props       patchFlag = propsBuildResult.patchFlag     }      // 檢測子節(jié)點     if (node.children.length > 0) {       if (node.children.length === 1) {         const child = node.children[0]         // 檢測子節(jié)點是否為動態(tài)文本         if (!getStaticType(child)) {           patchFlag |= PatchFlags.TEXT         }       }     }      // 格式化 patchFlag     if (patchFlag !== 0) {         vnodePatchFlag = String(patchFlag)     }      node.codegenNode = createVNodeCall(       context,       vnodeTag,       vnodeProps,       vnodeChildren,       vnodePatchFlag     )   } }

buildProps 會對節(jié)點的屬性進行一次遍歷,由于內(nèi)部源碼涉及很多其他的細節(jié),這里的代碼是經(jīng)過簡化之后的,只保留了 patchFlag  相關(guān)的邏輯。

export function buildProps(   node: ElementNode,   context: TransformContext,   props: ElementNode['props'] = node.props ) {   let patchFlag = 0   for (let i = 0; i < props.length; i++) {     const prop = props[i]     const [key, name] = prop.name.split(':')     if (key === 'v-bind' || key === '') {       if (name === 'class') {        // 如果包含 :class 屬性,patchFlag | CLASS         patchFlag |= PatchFlags.CLASS       } else if (name === 'style') {        // 如果包含 :style 屬性,patchFlag | STYLE         patchFlag |= PatchFlags.STYLE       }     }   }    return {     patchFlag   } }

上面的代碼只展示了三種 patchFlag 的類型:

  • 節(jié)點只有一個文本子節(jié)點,且該文本包含動態(tài)的數(shù)據(jù)(TEXT = 1)

<p>name: {{name}}</p>
  • 節(jié)點包含可變的 class 屬性(CLASS = 1 << 1)

  • <div :class="{ active: isActive }"></div>

節(jié)點包含可變的 style 屬性(STYLE = 1 << 2)

<div :style="{ color: color }"></div>

可以看到 PatchFlags 都是數(shù)字 1 經(jīng)過 左移操作符 計算得到的。

export const enum PatchFlags {   TEXT = 1,             // 1, 二進制 0000 0001   CLASS = 1 << 1,       // 2, 二進制 0000 0010   STYLE = 1 << 2,       // 4, 二進制 0000 0100   PROPS = 1 << 3,       // 8, 二進制 0000 1000   ... }

從上面的代碼能看出來,patchFlag 的初始值為 0,每次對 patchFlag 都是執(zhí)行 |  (或)操作。如果當(dāng)前節(jié)點是一個只有動態(tài)文本子節(jié)點且同時具有動態(tài) style 屬性,最后得到的 patchFlag 為 5(二進制:0000 0101)。

<p :style="{ color: color }">name: {{name}}</p>

Vue3模板編譯優(yōu)化的示例分析

我們將上面的代碼放到 Vue3 中運行:

const app = Vue.createApp({   data() {     return {       color: 'red',       name: 'shenfq'     }   },   template: `<div>    <p :style="{ color: color }">name: {{name}}</p>   </div>` })  app.mount('#app')

最后生成的 render 方法如下,和我們之前的描述基本一致。

Vue3模板編譯優(yōu)化的示例分析

function render() {}

render 優(yōu)化

Vue3 在虛擬 DOM Diff 時,會取出 patchFlag 和需要進行的 diff 類型進行 &(與)操作,如果結(jié)果為 true  才進入對應(yīng)的 diff。

Vue3模板編譯優(yōu)化的示例分析

patchFlag 判斷

還是拿之前的模板舉例:

<p :style="{ color: color }">name: {{name}}</p>

如果此時的 name 發(fā)生了修改,p 節(jié)點進入了 diff 階段,此時會將判斷 patchFlag & PatchFlags.TEXT  ,這個時候結(jié)果為真,表明 p 節(jié)點存在文本修改的情況。

Vue3模板編譯優(yōu)化的示例分析

patchFlag

patchFlag = 5 patchFlag & PatchFlags.TEXT // 或運算:只有對應(yīng)的兩個二進位都為1時,結(jié)果位才為1。 // 0000 0101 // 0000 0001 // ------------ // 0000 0001  =>  十進制 1
if (patchFlag & PatchFlags.TEXT) {   if (oldNode.children !== newNode.children) {     // 修改文本     hostSetElementText(el, newNode.children)   } }

但是進行 patchFlag & PatchFlags.CLASS 判斷時,由于節(jié)點并沒有動態(tài) Class,返回值為 0,所以就不會對該節(jié)點的  class 屬性進行 diff,以此來優(yōu)化性能。

Vue3模板編譯優(yōu)化的示例分析

patchFlag

patchFlag = 5 patchFlag & PatchFlags.CLASS // 或運算:只有對應(yīng)的兩個二進位都為1時,結(jié)果位才為1。 // 0000 0101 // 0000 0010 // ------------ // 0000 0000  =>  十進制 0

以上是“Vue3模板編譯優(yōu)化的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!

網(wǎng)頁題目:Vue3模板編譯優(yōu)化的示例分析
文章源于:http://www.rwnh.cn/article6/gpoiog.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供微信公眾號、域名注冊、網(wǎng)站維護、虛擬主機品牌網(wǎng)站設(shè)計、網(wǎng)站改版

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)
珠海市| 太保市| 金门县| 安新县| 茌平县| 上高县| 太湖县| 华阴市| 图木舒克市| 长治县| 巍山| 论坛| 长春市| 河池市| 东宁县| 峨边| 郴州市| 望都县| 赤水市| 南溪县| 东乡族自治县| 凌源市| 连南| 青岛市| 万盛区| 武安市| 株洲市| 丹凤县| 新宾| 理塘县| 石门县| 环江| 修文县| 绥滨县| 康马县| 常州市| 平陆县| 建昌县| 南汇区| 三门峡市| 阿瓦提县|