欧美xxxx做受欧美_www.xxxx精品_国产一区二区三区蝌蚪_国产原创欧美精品_国产福利精品一区_亚洲精品久久久久久下一站 _久久精品国产网站_久久99精品国产_久久综合给合久久狠狠狠97色69_91久久久久久久久久久

vue3編譯做了哪些優(yōu)化

來源:php中文網(wǎng) | 2022-12-19 18:04:15 |

本教程操作環(huán)境:windows7系統(tǒng)、vue3版,DELL G3電腦。

本文主要來分析 Vue3.0編譯階段做的優(yōu)化,在 patch階段是如何利用這些優(yōu)化策略來減少比對(duì)次數(shù)。由于組件更新時(shí)依然需要遍歷該組件的整個(gè) vnode樹,比如下面這個(gè)模板:


(資料圖)

<template>  <div id="container">    <p class="text">static text</p>    <p class="text">static text</p>    <p class="text">{{ message }}</p>    <p class="text">static text</p>    <p class="text">static text</p>  </div></template>

整個(gè) diff 過程如圖所示:

可以看到,因?yàn)檫@段代碼中只有一個(gè)動(dòng)態(tài)節(jié)點(diǎn),所以這里有很多 diff 和遍歷其實(shí)都是不需要的,這就會(huì)導(dǎo)致 vnode 的性能跟模版大小正相關(guān),跟動(dòng)態(tài)節(jié)點(diǎn)的數(shù)量無關(guān),當(dāng)一些組件的整個(gè)模版內(nèi)只有少量動(dòng)態(tài)節(jié)點(diǎn)時(shí),這些遍歷都是性能的浪費(fèi)。對(duì)于上述例子,理想狀態(tài)只需要 diff 這個(gè)綁定 message 動(dòng)態(tài)節(jié)點(diǎn)的 p 標(biāo)簽即可。

Vue.js 3.0通過編譯階段對(duì)靜態(tài)模板的分析,編譯生成了 Block tree

Block tree是一個(gè)將模板基于動(dòng)態(tài)節(jié)點(diǎn)指令切割的嵌套區(qū)塊,每個(gè)區(qū)塊內(nèi)部的節(jié)點(diǎn)結(jié)構(gòu)是固定的,而且每個(gè)區(qū)塊只需要以一個(gè) Array來追蹤自身包含的動(dòng)態(tài)節(jié)點(diǎn)。借助 Block treeVue.js 將 vnode 更新性能由與模版整體大小相關(guān)提升為與動(dòng)態(tài)內(nèi)容的數(shù)量相關(guān),這是一個(gè)非常大的性能突破。

PatchFlag

由于 diff算法無法避免新舊虛擬 DOM中無用的比較操作,Vue.js 3.0引入了 patchFlag,用來標(biāo)記動(dòng)態(tài)內(nèi)容。在編譯過程中會(huì)根據(jù)不同的屬性類型打上不同的標(biāo)識(shí),從而實(shí)現(xiàn)了快速 diff算法。PatchFlags的所有枚舉類型如下所示:

export const enum PatchFlags {  TEXT = 1, // 動(dòng)態(tài)文本節(jié)點(diǎn)  CLASS = 1 << 1, // 動(dòng)態(tài)class  STYLE = 1 << 2, // 動(dòng)態(tài)style  PROPS = 1 << 3, // 除了class、style動(dòng)態(tài)屬性  FULL_PROPS = 1 << 4, // 有key,需要完整diff  HYDRATE_EVENTS = 1 << 5, // 掛載過事件的  STABLE_FRAGMENT = 1 << 6, // 穩(wěn)定序列,子節(jié)點(diǎn)順序不會(huì)發(fā)生變化  KEYED_FRAGMENT = 1 << 7, // 子節(jié)點(diǎn)有key的fragment  UNKEYED_FRAGMENT = 1 << 8, // 子節(jié)點(diǎn)沒有key的fragment  NEED_PATCH = 1 << 9, // 進(jìn)行非props比較, ref比較  DYNAMIC_SLOTS = 1 << 10, // 動(dòng)態(tài)插槽  DEV_ROOT_FRAGMENT = 1 << 11,   HOISTED = -1, // 表示靜態(tài)節(jié)點(diǎn),內(nèi)容變化,不比較兒子  BAIL = -2 // 表示diff算法應(yīng)該結(jié)束}

Block Tree

左側(cè)的 template經(jīng)過編譯后會(huì)生成右側(cè)的 render函數(shù),里面有 _openBlock_createElementBlock_toDisplayString_createElementVNode(createVnode) 等輔助函數(shù)。

let currentBlock = nullfunction _openBlock() {  currentBlock = [] // 用一個(gè)數(shù)組來收集多個(gè)動(dòng)態(tài)節(jié)點(diǎn)}function _createElementBlock(type, props, children, patchFlag) {  return setupBlock(createVnode(type, props, children, patchFlag));}export function createVnode(type, props, children = null, patchFlag = 0) {  const vnode = {    type,    props,    children,    el: null, // 虛擬節(jié)點(diǎn)上對(duì)應(yīng)的真實(shí)節(jié)點(diǎn),后續(xù)diff算法    key: props?.["key"],    __v_isVnode: true,    shapeFlag,    patchFlag   };  ...  if (currentBlock && vnode.patchFlag > 0) {    currentBlock.push(vnode);  }  return vnode;}function setupBlock(vnode) {  vnode.dynamicChildren = currentBlock;  currentBlock = null;  return vnode;}function _toDisplayString(val) {  return isString(val)    ? val    : val == null    ? ""    : isObject(val)    ? JSON.stringify(val)    : String(val);}

此時(shí)生成的 vnode 如下:

此時(shí)生成的虛擬節(jié)點(diǎn)多出一個(gè) dynamicChildren屬性,里面收集了動(dòng)態(tài)節(jié)點(diǎn) span

節(jié)點(diǎn) diff 優(yōu)化策略:

我們之前分析過,在 patch階段更新節(jié)點(diǎn)元素的時(shí)候,會(huì)執(zhí)行 patchElement函數(shù),我們?cè)賮砘仡櫼幌滤膶?shí)現(xiàn):

const patchElement = (n1, n2) => { // 先復(fù)用節(jié)點(diǎn)、在比較屬性、在比較兒子  let el = n2.el = n1.el;  let oldProps = n1.props || {}; // 對(duì)象  let newProps = n2.props || {}; // 對(duì)象  patchProps(oldProps, newProps, el);  if (n2.dynamicChildren) { // 只比較動(dòng)態(tài)元素    patchBlockChildren(n1, n2);  } else {    patchChildren(n1, n2, el); // 全量 diff  }}

我們?cè)谇懊娼M件更新的章節(jié)分析過這個(gè)流程,在分析子節(jié)點(diǎn)更新的部分,當(dāng)時(shí)并沒有考慮到優(yōu)化的場(chǎng)景,所以只分析了全量比對(duì)更新的場(chǎng)景。

而實(shí)際上,如果這個(gè) vnode是一個(gè) Block vnode,那么我們不用去通過 patchChildren全量比對(duì),只需要通過 patchBlockChildren去比對(duì)并更新 Block中的動(dòng)態(tài)子節(jié)點(diǎn)即可。由此可以看出性能被大幅度提升,從 tree級(jí)別的比對(duì),變成了線性結(jié)構(gòu)比對(duì)。

我們來看一下它的實(shí)現(xiàn):

const patchBlockChildren = (n1, n2) => {  for (let i = 0; i < n2.dynamicChildren.length; i++) {    patchElement(n1.dynamicChildren[i], n2.dynamicChildren[i])  }}

屬性 diff 優(yōu)化策略:

接下來我們看一下屬性比對(duì)的優(yōu)化策略:

const patchElement = (n1, n2) => { // 先復(fù)用節(jié)點(diǎn)、在比較屬性、在比較兒子  let el = n2.el = n1.el;  let oldProps = n1.props || {}; // 對(duì)象  let newProps = n2.props || {}; // 對(duì)象  let { patchFlag, dynamicChildren } = n2    if (patchFlag > 0) {    if (patchFlag & PatchFlags.FULL_PROPS) { // 對(duì)所 props 都進(jìn)行比較更新      patchProps(el, n2, oldProps, newProps, ...)    } else {      // 存在動(dòng)態(tài) class 屬性時(shí)      if (patchFlag & PatchFlags.CLASS) {        if (oldProps.class !== newProps.class) {          hostPatchProp(el, "class", null, newProps.class, ...)        }      }      // 存在動(dòng)態(tài) style 屬性時(shí)      if (patchFlag & PatchFlags.STYLE) {        hostPatchProp(el, "style", oldProps.style, newProps.style, ...)      }            // 針對(duì)除了 style、class 的 props      if (patchFlag & PatchFlags.PROPS) {        const propsToUpdate = n2.dynamicProps!        for (let i = 0; i < propsToUpdate.length; i++) {          const key = propsToUpdate[i]          const prev = oldProps[key]          const next = newProps[key]          if (next !== prev) {            hostPatchProp(el, key, prev, next, ...)          }        }      }      if (patchFlag & PatchFlags.TEXT) { // 存在動(dòng)態(tài)文本        if (n1.children !== n2.children) {          hostSetElementText(el, n2.children as string)        }      }     } else if (dynamicChildren == null) {      patchProps(el, n2, oldProps, newProps, ...)    }  }}function hostPatchProp(el, key, prevValue, nextValue) {  if (key === "class") { // 更新 class     patchClass(el, nextValue)  } else if (key === "style") { // 更新 style    patchStyle(el, prevValue, nextValue)  } else if (/^on[^a-z]/.test(key)) {  // events  addEventListener    patchEvent(el, key, nextValue);  } else { // 普通屬性 el.setAttribute    patchAttr(el, key, nextValue);  }}function patchClass(el, nextValue) {  if (nextValue == null) {    el.removeAttribute("class"); // 如果不需要class直接移除  } else {    el.className = nextValue  }}function patchStyle(el, prevValue, nextValue = {}){  ...}function patchAttr(el, key, nextValue){  ...}

總結(jié): vue3會(huì)充分利用 patchFlagdynamicChildren做優(yōu)化。如果確定只是某個(gè)局部的變動(dòng),比如 style改變,那么只會(huì)調(diào)用 hostPatchProp并傳入對(duì)應(yīng)的參數(shù) style做特定的更新(靶向更新);如果有 dynamicChildren,會(huì)執(zhí)行 patchBlockChildren做對(duì)比更新,不會(huì)每次都對(duì) props 和子節(jié)點(diǎn)進(jìn)行全量的對(duì)比更新。圖解如下:

靜態(tài)提升

靜態(tài)提升是將靜態(tài)的節(jié)點(diǎn)或者屬性提升出去,假設(shè)有以下模板:

<div>  <span>hello</span>   <span a=1 b=2>{{name}}</span>  <a><span>{{age}}</span></a></div>

編譯生成的 render函數(shù)如下:

export function render(_ctx, _cache, $props, $setup, $data, $options) {  return (_openBlock(), _createElementBlock("div", null, [    _createElementVNode("span", null, "hello"),    _createElementVNode("span", {      a: "1",      b: "2"    }, _toDisplayString(_ctx.name), 1 /* TEXT */),    _createElementVNode("a", null, [      _createElementVNode("span", null, _toDisplayString(_ctx.age), 1 /* TEXT */)    ])  ]))}

我們把模板編譯成 render函數(shù)是這個(gè)醬紫的,那么問題就是每次調(diào)用 render函數(shù)都要重新創(chuàng)建虛擬節(jié)點(diǎn)。

開啟靜態(tài)提升 hoistStatic選項(xiàng)后

const _hoisted_1 = /*#__PURE__*/_createElementVNode("span", null, "hello", -1 /* HOISTED */)const _hoisted_2 = {  a: "1",  b: "2"}export function render(_ctx, _cache, $props, $setup, $data, $options) {  return (_openBlock(), _createElementBlock("div", null, [    _hoisted_1,    _createElementVNode("span", _hoisted_2, _toDisplayString(_ctx.name), 1 /* TEXT */),    _createElementVNode("a", null, [      _createElementVNode("span", null, _toDisplayString(_ctx.age), 1 /* TEXT */)    ])  ]))}

預(yù)解析字符串化

靜態(tài)提升的節(jié)點(diǎn)都是靜態(tài)的,我們可以將提升出來的節(jié)點(diǎn)字符串化。 當(dāng)連續(xù)靜態(tài)節(jié)點(diǎn)超過 10個(gè)時(shí),會(huì)將靜態(tài)節(jié)點(diǎn)序列化為字符串。

假如有如下模板:

<div>  <span>static</span>  <span>static</span>  <span>static</span>  <span>static</span>  <span>static</span>  <span>static</span>  <span>static</span>  <span>static</span>  <span>static</span>  <span>static</span></div>

開啟靜態(tài)提升 hoistStatic選項(xiàng)后

const _hoisted_1 = /*#__PURE__*/_createStaticVNode("<span>static</span><span>static</span><span>static</span><span>static</span><span>static</span><span>static</span><span>static</span><span>static</span><span>static</span><span>static</span>", 10)const _hoisted_11 = [  _hoisted_1]export function render(_ctx, _cache, $props, $setup, $data, $options) {  return (_openBlock(), _createElementBlock("div", null, _hoisted_11))}

函數(shù)緩存

假如有如下模板:

<div @click="event => v = event.target.value"></div>

編譯后:

const _hoisted_1 = ["onClick"]export function render(_ctx, _cache, $props, $setup, $data, $options) {  return (_openBlock(), _createElementBlock("div", {    onClick: event => _ctx.v = event.target.value  }, null, 8 /* PROPS */, _hoisted_1))}

每次調(diào)用 render的時(shí)候要?jiǎng)?chuàng)建新函數(shù),開啟函數(shù)緩存 cacheHandlers選項(xiàng)后,函數(shù)會(huì)被緩存起來,后續(xù)可以直接使用

export function render(_ctx, _cache, $props, $setup, $data, $options) {  return (_openBlock(), _createElementBlock("div", {    onClick: _cache[0] || (_cache[0] = event => _ctx.v = event.target.value)  }))}

總結(jié)

以上幾點(diǎn)即為 Vuejs在編譯階段做的優(yōu)化,基于上面幾點(diǎn),Vuejspatch過程中極大地提高了性能。

以上就是vue3編譯做了哪些優(yōu)化的詳細(xì)內(nèi)容,更多請(qǐng)關(guān)注php中文網(wǎng)其它相關(guān)文章!

關(guān)鍵詞: Vue.js

欧美xxxx做受欧美_www.xxxx精品_国产一区二区三区蝌蚪_国产原创欧美精品_国产福利精品一区_亚洲精品久久久久久下一站 _久久精品国产网站_久久99精品国产_久久综合给合久久狠狠狠97色69_91久久久久久久久久久
欧美成人精品福利| 亚洲电影在线免费观看| 69堂国产成人免费视频| 最新成人av在线| 成人精品国产福利| 国产午夜精品福利| 国产a区久久久| 国产视频亚洲色图| 99精品国产99久久久久久白柏| 精品成人免费观看| 国产91在线|亚洲| 亚洲欧美日韩国产综合在线| voyeur盗摄精品| 亚洲综合激情另类小说区| 欧美日韩日本视频| 青青草97国产精品免费观看无弹窗版| 欧美精品亚洲二区| 国产麻豆精品视频| 亚洲免费av在线| 欧美一区二区三区不卡| 国产乱一区二区| 亚洲情趣在线观看| 欧美一卡2卡3卡4卡| 国内成人免费视频| 亚洲视频免费在线| 欧美一区二区久久| 成人黄色av电影| 日韩国产高清影视| 国产精品美女久久久久久久久久久 | 婷婷成人激情在线网| 精品国产一二三区| 色吊一区二区三区| 国产成人h网站| 免费观看91视频大全| 亚洲人精品午夜| 欧美草草影院在线视频| 欧美日韩亚洲综合在线 欧美亚洲特黄一级| 美女一区二区三区| 亚洲靠逼com| 欧美激情在线观看视频免费| 日韩一区二区三区四区五区六区| 一本色道久久综合亚洲精品按摩| 国产一区二区三区日韩| 日本美女视频一区二区| 亚洲国产日韩综合久久精品| 国产精品久久久久久户外露出| 欧美一级欧美一级在线播放| 成人av在线网| 成人性生交大片免费看中文 | 日韩一级黄色大片| 欧美日韩三级视频| 欧美三级欧美一级| 4438x成人网最大色成网站| 欧洲av一区二区嗯嗯嗯啊| 国产成人精品免费网站| 成人免费视频视频在线观看免费 | 日韩电影免费一区| 五月天婷婷综合| 五月天亚洲精品| 麻豆国产一区二区| 国产一区二区精品久久| 国产精品一区在线观看乱码 | 欧美另类z0zxhd电影| 欧美日韩精品是欧美日韩精品| 欧洲生活片亚洲生活在线观看| 国产日韩影视精品| 99视频精品在线| www.亚洲精品| 在线观看视频一区二区| 欧美日韩精品一区二区三区| 日韩午夜电影在线观看| 精品久久久影院| 国产精品美女久久久久久久网站| 国产精品久久久久久亚洲毛片| 国产精品乱码人人做人人爱 | 一区二区三区成人| 天天色天天操综合| 国产伦精品一区二区三区在线观看 | 久久成人18免费观看| 国产精品一区在线| 在线视频观看一区| 亚洲精品在线网站| 一区二区三区四区高清精品免费观看 | 中文字幕一区二区三区四区| 一区二区三区四区不卡视频| 精品一区二区三区免费视频| 色悠久久久久综合欧美99| 欧美一区二区三区公司| 中文字幕一区二区三区在线观看 | 亚洲视频电影在线| 97精品久久久久中文字幕| 国内精品写真在线观看| 99久久免费视频.com| 欧美一级xxx| 亚洲欧美日韩国产中文在线| 日韩电影在线观看网站| 色综合久久66| 国产欧美一区二区精品久导航| 亚洲18色成人| 99re这里只有精品6| 欧美精品一区二区三区在线播放| 亚洲精品免费在线| 懂色av噜噜一区二区三区av| 91麻豆精品国产自产在线| 亚洲欧美日韩国产一区二区三区| 国产精品自拍一区| 欧美精品一区二区三区四区| 麻豆成人在线观看| 日韩欧美综合一区| 久久精品国产亚洲aⅴ| 日韩一区二区三区视频在线观看| 亚洲二区视频在线| 欧美午夜不卡在线观看免费| 亚洲色图欧美在线| 在线国产电影不卡| 亚洲bt欧美bt精品777| 欧美日韩国产一二三| 亚洲成人激情综合网| 欧美三区免费完整视频在线观看| 亚洲一区二区欧美激情| 欧美日韩国产一级二级| 午夜在线成人av| 精品国产污污免费网站入口 | 亚洲人成网站影音先锋播放| 99re热视频精品| 亚洲一区二区三区四区在线免费观看| 91在线精品一区二区| 一区二区国产视频| 欧美精三区欧美精三区| 精品亚洲国内自在自线福利| 国产精品午夜在线观看| 91精品91久久久中77777| 日本在线不卡一区| 国产日韩欧美精品一区| 色综合久久久久综合体| 男男gaygay亚洲| 中文字幕一区免费在线观看| 欧美三级电影网| 韩国欧美一区二区| 最近日韩中文字幕| 精品视频1区2区| 国精产品一区一区三区mba视频| 欧美激情艳妇裸体舞| 在线免费一区三区| 韩国v欧美v亚洲v日本v| 亚洲精品写真福利| 337p日本欧洲亚洲大胆精品 | 国产精品一区不卡| 亚洲国产综合人成综合网站| 日韩免费在线观看| 在线视频综合导航| 国产精品原创巨作av| 日韩av在线免费观看不卡| 欧美激情一区二区三区在线| 91精品国产乱| 91福利精品视频| 成人精品gif动图一区| 开心九九激情九九欧美日韩精美视频电影| 欧美精品一区二| 欧美一区在线视频| 日本韩国欧美一区| 成人性视频网站| 国产美女娇喘av呻吟久久| 日韩中文字幕亚洲一区二区va在线 | 亚洲欧美另类图片小说| 久久综合九色综合欧美亚洲| 欧美色中文字幕| 欧美亚洲综合另类| 91原创在线视频| 国产成人av福利| 国产精品综合一区二区| 精品影院一区二区久久久| 日本一区中文字幕| 亚洲国产精品自拍| 亚洲国产美女搞黄色| 亚洲一区在线观看免费| 亚洲精品欧美综合四区| 亚洲精品高清在线| 亚洲午夜影视影院在线观看| 亚洲成人三级小说| 午夜视频在线观看一区| 亚洲激情中文1区| 香蕉影视欧美成人| 免费人成精品欧美精品| 精品一区二区三区在线观看| 国产一区二区三区久久久| 国产91精品精华液一区二区三区| 成人av在线资源网站| 91成人免费在线视频| 91精品国产综合久久精品性色 | 国内成+人亚洲+欧美+综合在线| 日本欧美一区二区在线观看| 免费成人美女在线观看.| 国产精品一级黄| 欧美性生活久久| 精品福利一二区| 亚洲欧美精品午睡沙发| 青青草原综合久久大伊人精品 | 午夜精品久久久久久| 久久不见久久见免费视频1|