成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

深入Vue2.0底層思想–模板渲染

開(kāi)發(fā) 前端
Vue 2.0 中模板渲染與 Vue 1.0 完全不同,1.0 中采用的 DocumentFragment (想了解可以觀看這篇文章),而 2.0 中借鑒 React 的 Virtual DOM。基于 Virtual DOM,2.0 還可以支持服務(wù)端渲染(SSR),也支持 JSX 語(yǔ)法。

初衷

在使用vue2.0的過(guò)程,有時(shí)看API很難理解vue作者的思想,這促使我想要去深入了解vue底層的思想,了解完底層的一些思想,才能更好的用活框架,雖然網(wǎng)上已經(jīng)有很多源碼解析的文檔,但我覺(jué)得只有自己動(dòng)手了,才能更加深印象。

vue2.0和1.0模板渲染的區(qū)別

Vue 2.0 中模板渲染與 Vue 1.0 完全不同,1.0 中采用的 DocumentFragment (想了解可以觀看這篇文章),而 2.0 中借鑒 React 的 Virtual DOM。基于 Virtual DOM,2.0 還可以支持服務(wù)端渲染(SSR),也支持 JSX 語(yǔ)法。

知識(shí)普及

在開(kāi)始閱讀源碼之前,先了解一些相關(guān)的知識(shí):AST 數(shù)據(jù)結(jié)構(gòu),VNode 數(shù)據(jù)結(jié)構(gòu),createElement 的問(wèn)題,render函數(shù)。

AST 數(shù)據(jù)結(jié)構(gòu)

AST 的全稱是 Abstract Syntax Tree(抽象語(yǔ)法樹(shù)),是源代碼的抽象語(yǔ)法結(jié)構(gòu)的樹(shù)狀表現(xiàn)形式,計(jì)算機(jī)學(xué)科中編譯原理的概念。而vue就是將模板代碼映射為AST數(shù)據(jù)結(jié)構(gòu),進(jìn)行語(yǔ)法解析。

我們看一下 Vue 2.0 源碼中 AST 數(shù)據(jù)結(jié)構(gòu) 的定義:

  1. declare type ASTNode = ASTElement | ASTText | ASTExpression 
  2.  
  3. declare type ASTElement = { // 有關(guān)元素的一些定義 
  4.  
  5.   type: 1; 
  6.  
  7.   tag: string; 
  8.  
  9.   attrsList: Array{ name: string; value: string }>; 
  10.  
  11.   attrsMap: { [key: string]: string | null }; 
  12.  
  13.   parent: ASTElement | void; 
  14.  
  15.   children: ArrayASTNode>; 
  16.  
  17.   //...... 
  18.  
  19.  
  20. declare type ASTExpression = { 
  21.  
  22.   type: 2; 
  23.  
  24.   expression: string; 
  25.  
  26.   text: string; 
  27.  
  28.   static?: boolean; 
  29.  
  30.  
  31. declare type ASTText = { 
  32.  
  33.   type: 3; 
  34.  
  35.   text: string; 
  36.  
  37.   static?: boolean; 
  38.  
  39.  

我們看到 ASTNode 有三種形式:ASTElement,ASTText,ASTExpression。用屬性 type 區(qū)分。

VNode數(shù)據(jù)結(jié)構(gòu)

下面是 Vue 2.0 源碼中 VNode 數(shù)據(jù)結(jié)構(gòu) 的定義 (帶注釋的跟下面介紹的內(nèi)容有關(guān)):

  1. constructor { 
  2.  
  3.   this.tag = tag   //元素標(biāo)簽 
  4.  
  5.   this.data = data  //屬性 
  6.  
  7.   this.children = children  //子元素列表 
  8.  
  9.   this.text = text 
  10.  
  11.   this.elm = elm  //對(duì)應(yīng)的真實(shí) DOM 元素 
  12.  
  13.   this.ns = undefined 
  14.  
  15.   this.context = context 
  16.  
  17.   this.functionalContext = undefined 
  18.  
  19.   this.key = data && data.key 
  20.  
  21.   this.componentOptions = componentOptions 
  22.  
  23.   this.componentInstance = undefined 
  24.  
  25.   this.parent = undefined 
  26.  
  27.   this.raw = false 
  28.  
  29.   this.isStatic = false //是否被標(biāo)記為靜態(tài)節(jié)點(diǎn) 
  30.  
  31.   this.isRootInsert = true 
  32.  
  33.   this.isComment = false 
  34.  
  35.   this.isCloned = false 
  36.  
  37.   this.isOnce = false 
  38.  
  39.  

真實(shí)DOM存在什么問(wèn)題,為什么要用虛擬DOM

我們?yōu)槭裁床恢苯邮褂迷?DOM 元素,而是使用真實(shí) DOM 元素的簡(jiǎn)化版 VNode,最大的原因就是 document.createElement 這個(gè)方法創(chuàng)建的真實(shí) DOM 元素會(huì)帶來(lái)性能上的損失。我們來(lái)看一個(gè) document.createElement 方法的例子

  1. let div = document.createElement('div'); 
  2.  
  3. for(let k in div) { 
  4.  
  5.   console.log(k); 
  6.  
  7.  

打開(kāi) console 運(yùn)行一下上面的代碼,會(huì)發(fā)現(xiàn)打印出來(lái)的屬性多達(dá) 228 個(gè),而這些屬性有 90% 多對(duì)我們來(lái)說(shuō)都是無(wú)用的。VNode 就是簡(jiǎn)化版的真實(shí) DOM 元素,關(guān)聯(lián)著真實(shí)的dom,比如屬性elm,只包括我們需要的屬性,并新增了一些在 diff 過(guò)程中需要使用的屬性,例如 isStatic。

render函數(shù)

這個(gè)函數(shù)是通過(guò)編譯模板文件得到的,其運(yùn)行結(jié)果是 VNode。render 函數(shù) 與 JSX 類(lèi)似,Vue 2.0 中除了 Template 也支持 JSX 的寫(xiě)法。大家可以使用 Vue.compile(template)方法編譯下面這段模板。

  1. div id="app"
  2.  
  3.   header> 
  4.  
  5.     h1>I am a template!/h1> 
  6.  
  7.   /header> 
  8.  
  9.   p v-if="message"
  10.  
  11.     {{ message }} 
  12.  
  13.   /p> 
  14.  
  15.   p v-else
  16.  
  17.     No message. 
  18.  
  19.   /p> 
  20.  
  21. /div>  

方法會(huì)返回一個(gè)對(duì)象,對(duì)象中有 render 和 staticRenderFns 兩個(gè)值。看一下生成的 render函數(shù)

  1. (function() { 
  2.  
  3.   with(this){ 
  4.  
  5.     return _c('div',{   //創(chuàng)建一個(gè) div 元素 
  6.  
  7.       attrs:{"id":"app"}  //div 添加屬性 id 
  8.  
  9.       },[ 
  10.  
  11.         _m(0),  //靜態(tài)節(jié)點(diǎn) header,此處對(duì)應(yīng) staticRenderFns 數(shù)組索引為 0 的 render 函數(shù) 
  12.  
  13.         _v(" "), //空的文本節(jié)點(diǎn) 
  14.  
  15.         (message) //三元表達(dá)式,判斷 message 是否存在 
  16.  
  17.          //如果存在,創(chuàng)建 p 元素,元素里面有文本,值為 toString(message) 
  18.  
  19.         ?_c('p',[_v("\n    "+_s(message)+"\n  ")]) 
  20.  
  21.         //如果不存在,創(chuàng)建 p 元素,元素里面有文本,值為 No message. 
  22.  
  23.         :_c('p',[_v("\n    No message.\n  ")]) 
  24.  
  25.       ] 
  26.  
  27.     ) 
  28.  
  29.   } 
  30.  
  31. })  

要看懂上面的 render函數(shù),只需要了解 _c,_m,_v,_s 這幾個(gè)函數(shù)的定義,其中 _c 是 createElement(創(chuàng)建元素),_m 是 renderStatic(渲染靜態(tài)節(jié)點(diǎn)),_v 是 createTextVNode(創(chuàng)建文本dom),_s 是 toString (轉(zhuǎn)換為字符串)

除了 render 函數(shù),還有一個(gè) staticRenderFns 數(shù)組,這個(gè)數(shù)組中的函數(shù)與 VDOM 中的 diff 算法優(yōu)化相關(guān),我們會(huì)在編譯階段給后面不會(huì)發(fā)生變化的 VNode 節(jié)點(diǎn)打上 static 為 true 的標(biāo)簽,那些被標(biāo)記為靜態(tài)節(jié)點(diǎn)的 VNode 就會(huì)單獨(dú)生成 staticRenderFns 函數(shù)

  1. (function() { //上面 render 函數(shù) 中的 _m(0) 會(huì)調(diào)用這個(gè)方法 
  2.  
  3.   with(this){ 
  4.  
  5.     return _c('header',[_c('h1',[_v("I'm a template!")])]) 
  6.  
  7.   } 
  8.  
  9. })  

模板渲染過(guò)程(重要的函數(shù)介紹)

了解完一些基礎(chǔ)知識(shí)后,接下來(lái)我們講解下模板的渲染過(guò)程

 

$mount 函數(shù),主要是獲取 template,然后進(jìn)入 compileToFunctions 函數(shù)。

compileToFunctions 函數(shù),主要將 template 編譯成 render 函數(shù)。首先讀緩存,沒(méi)有緩存就調(diào)用 compile 方法拿到 render 函數(shù) 的字符串形式,再通過(guò) new Function 的方式生成 render 函數(shù)。

  1. // 有緩存的話就直接在緩存里面拿 
  2.  
  3. const key = options && options.delimiters 
  4.  
  5.             ? String(options.delimiters) + template 
  6.  
  7.             : template 
  8.  
  9. if (cache[key]) { 
  10.  
  11.     return cache[key
  12.  
  13.  
  14. const res = {} 
  15.  
  16. const compiled = compile(template, options) // compile 后面會(huì)詳細(xì)講 
  17.  
  18. res.render = makeFunction(compiled.render) //通過(guò) new Function 的方式生成 render 函數(shù)并緩存 
  19.  
  20. const l = compiled.staticRenderFns.length 
  21.  
  22. res.staticRenderFns = new Array(l) 
  23.  
  24. for (let i = 0; i  l; i++) { 
  25.  
  26.     res.staticRenderFns[i] = makeFunction(compiled.staticRenderFns[i]) 
  27.  
  28.  
  29. ...... 
  30.  
  31.  
  32. return (cache[key] = res) // 記錄至緩存中  

compile 函數(shù)就是將 template 編譯成 render 函數(shù)的字符串形式,后面一小節(jié)我們會(huì)詳細(xì)講到。

完成render方法的生成后,會(huì)進(jìn)入 _mount 中進(jìn)行DOM更新。該方法的核心邏輯如下:

  1. // 觸發(fā) beforeMount 生命周期鉤子 
  2.  
  3. callHook(vm, 'beforeMount'
  4.  
  5. // 重點(diǎn):新建一個(gè) Watcher 并賦值給 vm._watcher 
  6.  
  7. vm._watcher = new Watcher(vm, function updateComponent () { 
  8.  
  9.   vm._update(vm._render(), hydrating) 
  10.  
  11. }, noop) 
  12.  
  13. hydrating = false 
  14.  
  15. // manually mounted instance, call mounted on self 
  16.  
  17. // mounted is called for render-created child components in its inserted hook 
  18.  
  19. if (vm.$vnode == null) { 
  20.  
  21.   vm._isMounted = true 
  22.  
  23.   callHook(vm, 'mounted'
  24.  
  25.  
  26. return vm  

首先會(huì)new一個(gè)watcher對(duì)象(主要是將模板與數(shù)據(jù)建立聯(lián)系),在watcher對(duì)象創(chuàng)建后,會(huì)運(yùn)行傳入的方法 vm._update(vm._render(), hydrating) 。其中的vm._render()主要作用就是運(yùn)行前面compiler生成的render方法,并返回一個(gè)vNode對(duì)象。vm.update() 則會(huì)對(duì)比新的 vdom 和當(dāng)前 vdom,并把差異的部分渲染到真正的 DOM 樹(shù)上。

推薦個(gè)圖,響應(yīng)式工程流程

 

(想深入了解watcher的背后實(shí)現(xiàn)原理的,可以觀看這篇文章 Vue2.0 源碼閱讀:響應(yīng)式原理)

compile

上文中提到 compile 函數(shù)就是將 template 編譯成 render 函數(shù) 的字符串形式。

  1. export function compile ( 
  2.  
  3.   template: string, 
  4.  
  5.   options: CompilerOptions 
  6.  
  7. ): CompiledResult { 
  8.  
  9.   const AST = parse(template.trim(), options) //1. parse 
  10.  
  11.   optimize(AST, options)  //2.optimize 
  12.  
  13.   const code = generate(AST, options) //3.generate 
  14.  
  15.   return { 
  16.  
  17.     AST, 
  18.  
  19.     render: code.render, 
  20.  
  21.     staticRenderFns: code.staticRenderFns 
  22.  
  23.   } 
  24.  
  25.  

這個(gè)函數(shù)主要有三個(gè)步驟組成:parse,optimize 和 generate,分別輸出一個(gè)包含 AST,staticRenderFns 的對(duì)象和 render函數(shù) 的字符串。

parse 函數(shù),主要功能是將 template字符串解析成 AST。前面定義了ASTElement的數(shù)據(jù)結(jié)構(gòu),parse 函數(shù)就是將template里的結(jié)構(gòu)(指令,屬性,標(biāo)簽等)轉(zhuǎn)換為AST形式存進(jìn)ASTElement中,最后解析生成AST。

optimize 函數(shù)(src/compiler/optimizer.js)主要功能就是標(biāo)記靜態(tài)節(jié)點(diǎn),為后面 patch 過(guò)程中對(duì)比新舊 VNode 樹(shù)形結(jié)構(gòu)做優(yōu)化。被標(biāo)記為 static 的節(jié)點(diǎn)在后面的 diff 算法中會(huì)被直接忽略,不做詳細(xì)的比較。

generate 函數(shù)(src/compiler/codegen/index.js)主要功能就是根據(jù) AST 結(jié)構(gòu)拼接生成 render 函數(shù)的字符串。

  1. const code = AST ? genElement(AST) : '_c("div")' 
  2.  
  3. staticRenderFns = prevStaticRenderFns 
  4.  
  5. onceCount = prevOnceCount 
  6.  
  7. return { 
  8.  
  9.     render: `with(this){return ${code}}`, //最外層包一個(gè) with(this) 之后返回 
  10.  
  11.     staticRenderFns: currentStaticRenderFns 
  12.  
  13.  

其中 genElement 函數(shù)(src/compiler/codegen/index.js)是會(huì)根據(jù) AST 的屬性調(diào)用不同的方法生成字符串返回。

  1. function genElement (el: ASTElement): string { 
  2.  
  3.   if (el.staticRoot && !el.staticProcessed) { 
  4.  
  5.     return genStatic(el) 
  6.  
  7.   } else if (el.once && !el.onceProcessed) { 
  8.  
  9.     return genOnce(el) 
  10.  
  11.   } else if (el.for && !el.forProcessed) { 
  12.  
  13.     return genFor(el) 
  14.  
  15.   } else if (el.if && !el.ifProcessed) { 
  16.  
  17.     return genIf(el) 
  18.  
  19.   } else if (el.tag === 'template' && !el.slotTarget) { 
  20.  
  21.     return genChildren(el) || 'void 0' 
  22.  
  23.   } else if (el.tag === 'slot') { 
  24.  
  25.   } 
  26.  
  27.     return code 
  28.  
  29.   } 
  30.  
  31.  

以上就是 compile 函數(shù)中三個(gè)核心步驟的介紹,compile 之后我們得到了 render 函數(shù) 的字符串形式,后面通過(guò) new Function 得到真正的渲染函數(shù)。數(shù)據(jù)發(fā)現(xiàn)變化后,會(huì)執(zhí)行 Watcher 中的 _update 函數(shù)(src/core/instance/lifecycle.js),_update 函數(shù)會(huì)執(zhí)行這個(gè)渲染函數(shù),輸出一個(gè)新的 VNode 樹(shù)形結(jié)構(gòu)的數(shù)據(jù)。然后在調(diào)用 patch 函數(shù),拿這個(gè)新的 VNode 與舊的 VNode 進(jìn)行對(duì)比,只有發(fā)生了變化的節(jié)點(diǎn)才會(huì)被更新到真實(shí) DOM 樹(shù)上。

patch

patch.js 就是新舊 VNode 對(duì)比的 diff 函數(shù),主要是為了優(yōu)化dom,通過(guò)算法使操作dom的行為降到最低,diff 算法來(lái)源于 snabbdom,是 VDOM 思想的核心。snabbdom 的算法為了 DOM 操作跨層級(jí)增刪節(jié)點(diǎn)較少的這一目標(biāo)進(jìn)行優(yōu)化,它只會(huì)在同層級(jí)進(jìn)行, 不會(huì)跨層級(jí)比較。

想更加深入VNode diff算法原理的,可以觀看(解析vue2.0的diff算法)

總結(jié)

  • compile 函數(shù)主要是將 template 轉(zhuǎn)換為 AST,優(yōu)化 AST,再將 AST 轉(zhuǎn)換為 render函數(shù);
  • render函數(shù) 與數(shù)據(jù)通過(guò) Watcher 產(chǎn)生關(guān)聯(lián);
  • 在數(shù)據(jù)發(fā)生變化時(shí)調(diào)用 patch 函數(shù),執(zhí)行此 render 函數(shù),生成新 VNode,與舊 VNode 進(jìn)行 diff,最終更新 DOM 樹(shù)。 
責(zé)任編輯:龐桂玉 來(lái)源: 前端大全
相關(guān)推薦

2016-11-03 13:19:38

vue.jsjavascript前端

2021-08-27 12:59:59

React前端命令

2019-03-20 11:20:31

VueWeb 前端

2020-09-14 08:56:30

Vue模板

2024-10-17 16:39:18

2020-08-10 18:03:54

Cache存儲(chǔ)器CPU

2022-11-04 09:43:05

Java線程

2010-09-13 13:21:29

CSS排版

2011-02-15 11:46:41

2022-02-18 09:39:51

Vue3.0Vue2.0Script Set

2017-05-03 17:00:16

Android渲染機(jī)制

2010-09-13 09:17:27

DIV頁(yè)面

2020-03-17 08:36:22

數(shù)據(jù)庫(kù)存儲(chǔ)Mysql

2020-03-26 16:40:07

MySQL索引數(shù)據(jù)庫(kù)

2017-08-31 11:28:47

Slice底層實(shí)現(xiàn)

2018-01-11 14:58:40

2015-12-23 14:39:04

云計(jì)算2.0應(yīng)用業(yè)務(wù)

2022-08-09 11:46:58

Vue遞歸組件

2017-03-12 10:15:18

瀏覽器DOM樹(shù)CSSOM樹(shù)

2023-07-20 10:04:37

底層路由配置
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产激情自拍视频 | 手机av在线 | 成人在线免费av | 亚洲狠狠 | 国产免费一级片 | 九九九视频在线观看 | 99精品国产一区二区三区 | 日韩在线观看中文字幕 | 久久亚洲国产 | 99久久精品免费看国产四区 | 99精品欧美一区二区三区 | 国产高清久久久 | 亚洲激情网站 | 亚洲视频中文字幕 | 亚洲一区二区在线播放 | 亚洲一区精品在线 | 中文字幕一区二区三区四区五区 | 91在线免费视频 | h肉视频| japanhdxxxx裸体| 亚洲视频精品在线 | 久综合| 精品国产一区二区三区免费 | 亚洲精品日韩一区二区电影 | 日韩视频国产 | 亚洲欧美aⅴ| 国产精品女人久久久 | 欧美成人手机在线 | 国产视频在线一区二区 | 免费观看毛片 | 伊人久久综合 | 91视频官网 | 亚洲精品第一国产综合野 | 国产精品一区二区不卡 | 欧美日高清 | 黄色av网站免费看 | 国产精品久久久久aaaa九色 | 亚洲欧美日韩久久 | 日本又色又爽又黄的大片 | 成人毛片视频免费 | 中文字幕人成人 |