国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Vue編程三部曲之模型樹優(yōu)化實戰(zhàn)代碼

3403771864 / 400人閱讀

  實踐是所有展示最好的方法,因此我覺得可以不必十分細致的,但我們的展示卻是整體的流程、輸入和輸出?,F(xiàn)在我們就看看Vue 的指令、內(nèi)置組件等。也就是第二篇,模型樹優(yōu)化。

  分析了 Vue 編譯三部曲的第一步,「如何將 template 編譯成 AST ?」上一篇已經(jīng)介紹,但我們還是來總結(jié)回顧下,parse 的目的是將開發(fā)者寫的 template 模板字符串轉(zhuǎn)換成抽象語法樹 AST ,AST 就這里來說就是一個樹狀結(jié)構(gòu)的 JavaScript 對象,簡單來說就是這個模板,這個對象包含了每一個元素的上下文關(guān)系。當整個 parse 的過程是利用很多正則表達式順序解析模板,當解析到開始標簽、閉合標簽、文本的時候都會分別執(zhí)行對應的回調(diào)函數(shù),來達到構(gòu)造 AST 樹的目的。

  當我們的 template 被轉(zhuǎn)換為 AST 之后,接下來我們需要對這棵 AST 語法樹做優(yōu)化。

  為什么要做優(yōu)化?

  在源碼的注釋中找到了下面這段話:

  Goal of the optimizer: walk the generated template AST tree and detect sub-trees that are purely static, i.e. parts of the DOM that never needs to change. Once we detect these sub-trees, we can:

  Hoist them into constants, so that we no longer need to create fresh nodes for them on each re-render;

  Completely skip them in the patching process.

  簡單理解就是:

  永遠不需要變化的 DOM 就是靜態(tài)的。

  重新渲染時,作為常量,無需創(chuàng)建新節(jié)點;

  我們知道Vue 是一個數(shù)據(jù)驅(qū)動視圖的響應式框架,但是在開發(fā)者書寫的 template 中,也不是所有的數(shù)據(jù)都是響應式的,在首屏渲染完之后有不少數(shù)據(jù)在不斷的變化,既然數(shù)據(jù)在不斷的變壓也就表明DOM 不在變化,所以在后續(xù)的更新過程進行 patch時完全可以直接跳過他們的比對,從而來提升效率。

  接下來我們開始 optimize 源碼之旅!看看源碼中是如何去優(yōu)化模型樹的?

  optimize

  template 在經(jīng)過解析之后,就會進行優(yōu)化操作。首先這里有一個小邏輯,會判斷是否需要進行優(yōu)化?只有當options.optimize !== false時才會進行優(yōu)化。

  大家先看看這個幾個小問題:options.optimize為什么需要進行這樣的判斷了?并且如何能關(guān)閉模型樹優(yōu)化的操作了?什么情況下會關(guān)閉模型樹的優(yōu)化?

  var ast = parse(template.trim(), options);   if (options.optimize !== false) {   optimize(ast, options);   }

  在往下,進入到optimize函數(shù),代碼很清楚,優(yōu)化主要做兩件事情:

  markStatic$1(root) 標記靜態(tài)節(jié)點

  markStaticRoots(root, false) 標記靜態(tài)根


  function optimize (root, options) {   if (!root) { return }   isStaticKey = genStaticKeysCached(options.staticKeys || '');   isPlatformReservedTag = options.isReservedTag || no;   // 第一步:標記所有靜態(tài)節(jié)點。   markStatic$1(root);   // 第二步:標記靜態(tài)根   markStaticRoots(root, false);   }

  在進行優(yōu)化操作之前會有兩個變量的賦值。

  isStaticKey

  獲取 genStaticKeysCached 函數(shù)返回值, 獲取 makeMap 函數(shù)返回值引用 。

  isStaticKey = genStaticKeysCached(options.staticKeys || '');

  這里簡單了解一下涉及到的 makeMap 函數(shù):

  makeMap 函數(shù)首先根據(jù)一個字符串生成一個 map,然后根據(jù)該 map 產(chǎn)生一個新函數(shù),新函數(shù)接收一個字符串參數(shù)作為 key,如果這個 key 在 map 中則返回 true,否則返回 undefined。

  str 一個以逗號分隔的字符串 、expectsLowerCase 是否小寫

  makeMap 函數(shù)返回值是一個根據(jù)生成的 map 產(chǎn)生的函數(shù)

  function makeMap(str, expectsLowerCase) {   var map = Object.create(null);   var list = str.split(',');   for (var i = 0; i < list.length; i++) {   map[list[i]] = true;   }   return expectsLowerCase ?   function(val) {   return map[val.toLowerCase()];   } :   function(val) {   return map[val];   }   }   function genStaticKeys$1 (keys) {   return makeMap(   'type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap' +   (keys ? ',' + keys : '')   )   }   function cached (fn) {   var cache = Object.create(null);   return (function cachedFn (str) {   var hit = cache[str];   return hit || (cache[str] = fn(str))   })   }

  var genStaticKeysCached = cached(genStaticKeys$1);

  其實我們發(fā)過來看看上面的代碼,明白了嗎?這里大量的使用了閉包,保護和保存數(shù)據(jù)。這就表明這是在叼的框架,這些基礎(chǔ)思維就很顯而易見

  isStaticKey 的值就是利用 makeMap 的返回引用做值的判斷。判斷節(jié)點的屬性是否在相對于的范圍內(nèi):例如有這樣一個 template:

  <div></div>

  然后parse完之后變成這樣一個描述對象,所有屬性通過 isStaticKey 判斷之后,都在上面列出的屬性范圍中,都是靜態(tài)屬性,所以這就是一個靜態(tài)節(jié)點。


  {   "type": 1,   "tag": "div",   "attrsList": [],   "attrsMap": {},   "rawAttrsMap": {},   "children": [],   "start": 0,   "end": 11,   "plain": true   }

  另外一個屬性是 isPlatformReservedTag。

  isPlatformReservedTag

  isPlatformReservedTag 用于獲取編譯器選項 isReservedTag 的引用,檢查給定的字符是否是保留的標簽。

  isPlatformReservedTag = options.isReservedTag || no;

  isReservedTag函數(shù)如下,用這個函數(shù)來判斷是否是保留標簽,如果一個標簽是 html標簽或者是 svg標簽,那么這個標簽就是保留標簽。

  HTML 保留標簽

  'html,body,base,head,link,meta,style,title,'+ 'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,'+ 'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,'+ 'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,'+ 's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,'+ 'embed,object,param,source,canvas,script,noscript,del,ins,'+ 'caption,col,colgroup,table,thead,tbody,td,th,tr,'+ 'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,'+ 'output,progress,select,textarea,'+

  'details,dialog,menu,menuitem,summary,'+ 'content,element,shadow,template,blockquote,iframe,tfoot'

  SVG 保留標簽

  'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,'+ 'foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,'+ 'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view',

  var isReservedTag = function(tag) {   return isHTMLTag(tag) || isSVG(tag)   };

  并且在后續(xù)的節(jié)點標記中會被用到。我們在接著往下看,重點來了。

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


  function markStatic$1 (node) {   // ①   node.static = isStatic(node);   // ②   if (node.type === 1) {   if (   !isPlatformReservedTag(node.tag) &&   node.tag !== 'slot' &&   node.attrsMap['inline-template'] == null   ) {   return   }   for (var i = 0, l = node.children.length; i < l; i++) {   var child = node.children[i];   markStatic$1(child);   if (!child.static) {   node.static = false;   }   }   if (node.ifConditions) {   for (var i$1 = 1, l$1 = node.ifConditions.length; i$1 < l$1; i$1++) {   var block = node.ifConditions[i$1].block;   markStatic$1(block);   if (!block.static) {   node.static = false;   }   }   }   }   }

  判斷節(jié)點狀態(tài)并標記

  既然是判斷,當然第一步,就是判斷階段狀態(tài)并標記。在這給 AST 元素節(jié)點擴展了static屬性,通過 isStatic方法調(diào)用后返回值,確認哪些節(jié)點是靜態(tài)的,哪些是動態(tài)的。 

 node.static = isStatic(node);

  那在 Vue 中那些節(jié)點算是動態(tài)的,那些階段算是靜態(tài)的了?我們先回顧一下上一篇文章在講生成 AST 時,給每一個元素節(jié)點標記type類型,一種有type類型幾種?

  沒錯是三種。

  type = 1的基礎(chǔ)元素節(jié)點

  type = 2含有expression和tokens的文本節(jié)點

  type = 3的純文本節(jié)點或者是注釋節(jié)點

  child = {   type: 1,   tag:"div",   parent: null,   children: [],   attrsList: []   };   child = {   type: 2,   expression: res.expression,   tokens: res.tokens,   text: text   };   child = {   type: 3,   text: text   };   child = {   type: 3,   text: text,   isComment: true   };

  isStatic函數(shù)會根據(jù)元素的 type和元素的屬性進行節(jié)點動靜態(tài)的判斷。

  如果type = 2說明這一點是一個動態(tài)節(jié)點,因為包含表達式

  如果type = 3說明可能是純文本節(jié)點或者是注釋節(jié)點,可以標記為靜態(tài)節(jié)點

  如果元素節(jié)點有:

  pre 屬性,使用了 v-pre指令,標記為靜態(tài)節(jié)點

  如果沒有動態(tài)綁定,沒有使用v-if、v-for,不是內(nèi)置標簽(slot,component),是平臺保留標簽(HTML 標簽和 SVG 標簽),不是 template 標簽的直接子元素并且沒有包含在 for 循環(huán)中,節(jié)點包含的屬性只能有 isStaticKey 中指定的幾個,那么就標記為靜態(tài)節(jié)點。

  這樣就可以清楚的知道, Vue 會將一個節(jié)點標記為動態(tài)節(jié)點,什么時候會將一個節(jié)點標記為靜態(tài)節(jié)點。

  并且在這里也利用到了上面初始賦值的兩個變量,isPlatformReservedTag和 isStaticKey,分別用來判斷是否是平臺保留標簽(HTML 標簽和 SVG 標簽)和間距判斷節(jié)點的屬性只能有 isStaticKey 中指定的幾個。

  function isStatic(node) {   if (node.type === 2) {   return false   }   if (node.type === 3) {   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)   ))   }

  標記完節(jié)點,我們接下往下看。

  基礎(chǔ)元素節(jié)點的處理

  既然已經(jīng)判斷,現(xiàn)在就進入第二步,這里處理的是節(jié)點類型 type = 1的幾點。也就是我們的元素節(jié)點。

  對于我們的元素節(jié)點,如果不是平臺保留標簽(HTML 標簽和 SVG 標簽、不是 slot 標簽、節(jié)點是 inline-template那么就會直接返回。

  inline-template :內(nèi)聯(lián)模板,一般很少被用到,它是一個特殊的 attribute ,當出現(xiàn)在一個子組件上時,這個組件將會使用其里面的內(nèi)容作為模板,而不是將其作為被分發(fā)的內(nèi)容。這使得模板的撰寫工作更加靈活。但是,在 Vue 3.0 版本去掉了這個內(nèi)聯(lián)模板,原因在于 inline-template 會讓模板的作用域變得更加難以理解。其實最好就是,請在組件內(nèi)優(yōu)先選擇 template 選項或 .vue 文件里的一個 <template> 元素來定義模板。

  然后通過 node.children 找到子節(jié)點,遞歸子節(jié)點。如果子節(jié)點非靜態(tài),那么該節(jié)點也標注非靜態(tài) 。這塊設(shè)計的不太合理有更多好的優(yōu)化方案,在 Vue3.0 做了優(yōu)化,編譯階段對靜態(tài)模板的分析,編譯生成了 Block tree。Block tree 是一個將模版基于動態(tài)節(jié)點指令切割的嵌套區(qū)塊,每個區(qū)塊內(nèi)部的節(jié)點結(jié)構(gòu)是固定的,而且每個區(qū)塊只需要以一個 Array 來追蹤自身包含的動態(tài)節(jié)點。借助 Block tree,Vue.js 將 vnode 更新性能由與模版整體大小相關(guān)提升為與動態(tài)內(nèi)容的數(shù)量相關(guān),這是一個非常大的性能突破。

  if (!child.static) {   node.static = false;   }

  最后判斷如果節(jié)點的 ifConditions 不為空,則遍歷 ifConditions拿到所有條件中的 block,block 其實也就是它們對應的 AST 節(jié)點,遞歸執(zhí)行 markStatic。在這些遞歸過程中,一旦子節(jié)點有不是 static 的情況,則它的父節(jié)點的 static 均變成 false。

  ifConditions 是撒?

  ifConditions 其實是 if 條件的集合,例如有一個模板如下:

  <div>   <div v-if={show}>hello, {{ text }},{{ message }}</div>   <div v-else-if={show1}>hello, world</div>   <div v-else>撒也沒有!</div>   </div>

  那在 parse階段就會在的 AST 節(jié)點中就會給相對于元素的ifConditions添加關(guān)聯(lián)的所有判斷集合。

1.png

  并且每一個ifConditions元素 的block描述就是判斷的節(jié)點內(nèi)容。

2.png

  接下來看下 markStaticRoots。

  標記靜態(tài)根

  function markStaticRoots (node: ASTNode, isInFor: boolean) {   if (node.type === 1) {   if (node.static || node.once) {   node.staticInFor = isInFor   }   if (node.static && node.children.length && !(   node.children.length === 1 &&   node.children[0].type === 3   )) {   node.staticRoot = true   return   } else {   node.staticRoot = false   }   if (node.children) {   for (let i = 0, l = node.children.length; i < l; i++) {   markStaticRoots(node.children[i], isInFor || !!node.for)   }   }   if (node.ifConditions) {   for (let i = 1, l = node.ifConditions.length; i < l; i++) {   markStaticRoots(node.ifConditions[i].block, isInFor)   }   }   }   }

  標記靜態(tài)根節(jié)點,整體邏輯大致分為三步:

  第一步,已經(jīng)是 static 的節(jié)點或者是 v-once 指令的節(jié)點,設(shè)置 node.staticInFor = isInFor。

  第二步,對于 staticRoot 的判斷邏輯。

  第三步,遍歷 children 以及 ifConditions,遞歸執(zhí)行 markStaticRoots。

  注意這里的根節(jié)點不一定就是 template 最外層的節(jié)點,也可能是內(nèi)部的節(jié)點。

  什么節(jié)點會成為靜態(tài)根?

  從源碼來看,一個節(jié)點要想成為靜態(tài)根,必須滿足以下幾個條件:

  自生是一個靜態(tài)節(jié)點

  包含子元素

  子節(jié)點不能僅為一個文本節(jié)點(排除注釋節(jié)點,原因在于除非手動開啟保留注釋,否則注釋節(jié)點不會存在)

  為什么子節(jié)點不能僅為一個文本節(jié)點?

  Vue 官方說明是,如果子節(jié)點只有一個純文本節(jié)點,要是優(yōu)化就是消耗的成本比好處要多的多。簡單來說就是選擇成本少的。

  但我們也可以思考下,不優(yōu)化原因有哪些?

  標記靜態(tài)節(jié)點和靜態(tài)根節(jié)點有什么區(qū)別?

  回顧之前這兩個標記函數(shù),發(fā)現(xiàn)是先將每一個節(jié)點都處理了,給每一個節(jié)點都加上標記之后,然后利用節(jié)點的狀態(tài)來判斷根節(jié)點的狀態(tài)。這樣可以利用子節(jié)點反推根節(jié)點。這就好比:「一個組內(nèi)部大家都是前端開發(fā),那么間接可以推斷,這個組的小組長也是前端開發(fā)(當然不是絕對的哈,只是比方)」。

  靜態(tài)根節(jié)點和靜態(tài)節(jié)點有一種大包小感覺,利用靜態(tài)節(jié)點的標記函數(shù),間接給靜態(tài)根節(jié)點的標記函數(shù)服務(wù)。并且通過靜態(tài)節(jié)點的標記函數(shù)添加的 static 屬性,并不會在后續(xù) DOM 的處理和 render 上使用。但是通過靜態(tài)根節(jié)點的標記函數(shù)添加的 staticRoot 屬性會在 render中使用。

  總結(jié)

  至此分析完了 optimize 的過程。

  optimize前 AST 是這樣的:

3.png

  optimize后 AST 多了static和staticRoot標記:

4.png

  我們知道整個optimize 的過程,就是將整個AST 樹都進行深度遍歷,每一顆子樹都要去檢測它的是不是靜態(tài)節(jié)點,這個節(jié)是靜態(tài)節(jié)點表示生成的 DOM 永遠不需要改變,在優(yōu)化的時候極大的優(yōu)化作用,提升了運行效率。


文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/127987.html

相關(guān)文章

  • 你不知的DOM編程

    摘要:思路合并所有改變?nèi)缓笠淮涡蕴幚硎褂脤傩孕薷念惷啃薷漠斈阈枰獙υ剡M行一系列操作的時候,不妨按照如下步驟使元素脫離文檔流對其應用多重改變把元素帶回文檔中上面的這一套組合拳中,第一步和第三部分別會觸發(fā)一次重排。 前言:隨著vue,react, angular的流行,可能現(xiàn)在我們不必經(jīng)常的操作DOM,三大框架在副交互的操作中發(fā)揮著極大地優(yōu)勢。因為我們知道用腳本對DOM的操作非常昂貴,本文...

    Anshiii 評論0 收藏0
  • 你不知的DOM編程

    摘要:思路合并所有改變?nèi)缓笠淮涡蕴幚硎褂脤傩孕薷念惷啃薷漠斈阈枰獙υ剡M行一系列操作的時候,不妨按照如下步驟使元素脫離文檔流對其應用多重改變把元素帶回文檔中上面的這一套組合拳中,第一步和第三部分別會觸發(fā)一次重排。 前言:隨著vue,react, angular的流行,可能現(xiàn)在我們不必經(jīng)常的操作DOM,三大框架在副交互的操作中發(fā)揮著極大地優(yōu)勢。因為我們知道用腳本對DOM的操作非常昂貴,本文...

    warnerwu 評論0 收藏0
  • 從小白程序員一路晉升為大廠高級技術(shù)專家我看過哪些書籍?(建議收藏)

    摘要:大家好,我是冰河有句話叫做投資啥都不如投資自己的回報率高。馬上就十一國慶假期了,給小伙伴們分享下,從小白程序員到大廠高級技術(shù)專家我看過哪些技術(shù)類書籍。 大家好,我是...

    sf_wangchong 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<