摘要:寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟專注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于版本如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊下面鏈接或者拉到下面關(guān)注公眾號(hào)也可以吧原理源碼版之節(jié)點(diǎn)數(shù)據(jù)拼接上一篇我們
寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟
專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧
研究基于 Vue版本 【2.5.17】
如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊 下面鏈接 或者 拉到 下面關(guān)注公眾號(hào)也可以吧
【Vue原理】Compile - 源碼版 之 generate 節(jié)點(diǎn)數(shù)據(jù)拼接
上一篇我們講了不同節(jié)點(diǎn)的拼接,這一篇需要詳細(xì)記錄的是 節(jié)點(diǎn)數(shù)據(jù)的拼接
節(jié)點(diǎn)數(shù)據(jù),包括有 props,attrs,事件等
上一篇我們?cè)?genElement 中看到過(guò),每個(gè)節(jié)點(diǎn)都需要去拼接節(jié)點(diǎn)數(shù)據(jù),使用的就是下面源碼中的 genData$2 這個(gè)方法
function genElement() { .....處理其他類型的節(jié)點(diǎn) var data = genData$2(el, state); var children = genChildren(el, state); code = `_c("${el.tag}", $ { data ? ("," + data) : "" }, $ { children ? ("," + children) : "" })` }genData$2
這個(gè)函數(shù)的源碼有點(diǎn)長(zhǎng),但是不用怕,都是處理各種屬性的判斷,所以內(nèi)容大約一致,不過(guò)里面涉及到具體的方法,會(huì)具體看
來(lái)吧,先過(guò)一遍把
function genData$2(el, state) { var data = "{"; // 先解析指令 var dirs = genDirectives(el, state); // 拼接上解析得到的指令字符串 if(dirs) { data += dirs + ","; } // 帶有 is 綁定的組件,直接使用組件則沒(méi)有 if(el.component) { data += `tag: ${el.tag} , ` } // 上一篇說(shuō)過(guò)的,dataGenFns 包含處理style,class的函數(shù) for(var i = 0; i < state.dataGenFns.length; i++) { data += state.dataGenFns[i](el); } // 全部屬性 if(el.attrs) { data += ` attrs:{ ${genProps(el.attrs)) } ,` } // 原生屬性 if(el.props) { data += ` domProps:{ ${genProps(el.props)} }, ` } // 事件 if(el.events) { data += genHandlers(el.events, false) + ","; } // 原生事件 if(el.nativeEvents) { data += genHandlers(el.nativeEvents, true) + ","; } // 沒(méi)有作用域的 slot if( el.slotTarget && !el.slotScope ) { data += ` slot: ${ el.slotTarget } ,` } // 作用域slot if(el.scopedSlots) { data += genScopedSlots(el.scopedSlots, state) + ","; } // 組件使用 v-model if(el.model) { data += `model:{ value:${el.model.value}, callback:${el.model.callback}, expression:${el.model.expression}, },` } data = data.replace(/,$/, "") + "}"; return data }
首先這個(gè)方法,最終返回的是一個(gè)對(duì)象的序列化字符串,比如這樣
" { a:b , c:d } "
所以頭尾都會(huì) 加上大括號(hào),然后屬性拼接xx:yy 的形式
下面我們就來(lái)一個(gè)個(gè)看對(duì)于不同屬性的處理
拼接指令function genDirectives(el, state) { var dirs = el.directives; if (!dirs) return var res = "directives:["; var hasRuntime = false; var i, l, dir, needRuntime; for (i = 0, l = dirs.length; i < l; i++) { dir = dirs[i]; needRuntime = true; // 獲取到特定的 Vue 指令處理方法 var gen = state.directives[dir.name]; // 如果這個(gè)函數(shù)存在,證明這個(gè)指令是內(nèi)部指令 if (gen) { needRuntime = gen(el, dir); } if (needRuntime) { hasRuntime = true; res += `{ name: ${dir.name}, rawName: ${dir.rawName} ${ dir.value ? ",value:" + dir.value +", expression:" + dir.value : "" } ${ dir.arg ? ( ",arg:" + dir.arg ) : "" } ${ dir.modifiers ? (",modifiers:" + JSON.stringify(dir.modifiers)) : "" } }, ` } } if (hasRuntime) { return res.slice(0, -1) + "]" } }
首先呢,我們要了解這個(gè)方法會(huì)返回什么字符串,比如
就會(huì)返回這樣的字符串
`directives:[{ name:"test", rawName:"v-test:a.b.c", value:222, expression:"arr", arg:"a", modifiers:{"b":true,"c":true} }]`
每一個(gè)指令,都會(huì)解析成一個(gè)對(duì)象字符串,然后拼接在字符串?dāng)?shù)組里面
那么下面就來(lái)詳細(xì)記錄幾個(gè)可能疑惑的點(diǎn)
函數(shù)中出現(xiàn)的 state.directives在上面文章中的 CodegenState 中,我們有寫(xiě)過(guò)這個(gè)
state.directives 是一個(gè)數(shù)組,包含了 Vue內(nèi)部指令的處理函數(shù),如下
v-on,v-bind,v-cloak,v-model ,v-text,v-html
函數(shù)中的變量 needRuntime一個(gè)標(biāo)志位,表示是否需要把指令的數(shù)據(jù)解析成一個(gè) 對(duì)象字符串,像這樣
`{ name:xxx, rawName:xxx, value:xxx, expression:xx, arg:xx, modifiers:xx }`
也就是說(shuō),這個(gè)指令是否需要被拼接成 render 字符串中
那么什么指令需要,什么指令不需要呢?自定義指令,都需要被解析,拼接在 render 字符串中
但是 Vue 的內(nèi)部指令,有的用,有的不用,所以就搞出一個(gè) needRunTime 來(lái)進(jìn)行判斷
Vue 的指令,先要獲取到特定的處理方法,賦值給 gen
gen 處理完返回 true,則表示需要 拼接上render,返回 false 或者不返回,則表示不需要拼接上
比如,v-model 指令的數(shù)據(jù)就需要拼接上 render,而 v-text,v-html 則不用
看下面的例子
比如上面的模板拼接成下面的字符串,發(fā)現(xiàn) v-html 并沒(méi)有出現(xiàn)在 directives 那個(gè)字符串?dāng)?shù)組中
`_c("div",{ directives:[{ name:"model", rawName:"v-model", value:arr, expression:"arr" }], domProps:{ "innerHTML":_s() } })`函數(shù)中的變量 hasRuntime
一個(gè)標(biāo)志位,表示是否需要把 return 指令字符串
genDirectives 處理的是一個(gè)指令數(shù)組,當(dāng)數(shù)組為空的時(shí)候,并不會(huì)有返回值
那么 render 字符串就不會(huì) 存在 directive 這一段字符串
如果指令不為空,那么 hasRunTime 設(shè)為 true,需要返回字符串
并且在 字符串尾部加上 ] , 這樣字符串?dāng)?shù)組就完整了
拼接組件這里的解析組件,解析的是帶有 is 屬性的綁定組件
很簡(jiǎn)單,就是拼接上一個(gè) tag 的屬性就ok 了
看例子
原有的標(biāo)簽名,被拼接在 tag 后面
` _c("test",{tag:"div"}) `拼接樣式
上篇文章也說(shuō)過(guò),state.dataGenFns 是一個(gè)數(shù)組
存放的是兩個(gè)函數(shù),一個(gè)是解析 class ,一個(gè)是解析 style 的
這里放下其中的源碼,非常的簡(jiǎn)單
解析 classfunction genData(el) { var data = ""; if (el.staticClass) { data += "staticClass:" + el.staticClass + ","; } if (el.classBinding) { data += "class:" + el.classBinding + ","; } return data }解析style
function genData$1(el) { var data = ""; if (el.staticStyle) { data += "staticStyle:" + el.staticStyle + ","; } if (el.styleBinding) { data += "style:(" + el.styleBinding + "),"; } return data }
實(shí)在是太簡(jiǎn)單的,就是直接拼接上幾個(gè)屬性而已啦
給例子就好了
`_c("div",{ staticClass:"a", class:name, staticStyle:{"height":"0"}, style:{width:0} }) `拼接屬性
屬性的拼接只有一個(gè)函數(shù),內(nèi)容也十分簡(jiǎn)單
function genProps(props) { var res = ""; for (var i = 0; i < props.length; i++) { var prop = props[i]; res += prop.name + ":" + prop.value + ","; } return res.slice(0, -1) }
你可以看到,雖然只有一個(gè)方法,但是在 genData$2 中,拼接的結(jié)果會(huì)有兩種
拼接到 el.attr 拼接到 el.props為什么會(huì)拼接到不同的地方?
因?yàn)榭吹氖悄銓傩?放的位置
如果你的屬性位置是 標(biāo)簽上,那么就會(huì)拼接到 attr 中
如果你的屬性位置是在 dom 上,那么就被拼接到 domProps 中
舉個(gè)例子
比如下面的模板,bbb 就是放在 標(biāo)簽上,aaa 就是放在 DOM 上
拼接的結(jié)果就是
` _c("div",{ attrs:{"bbb":"bbb"}, domProps:{"aaa":11} }) `
頁(yè)面標(biāo)簽看不到 aaa
可以在 dom 屬性中找到 aaa
拼接事件事件的拼接,內(nèi)容很多,打算放在另一篇文章詳細(xì)記錄
事件拼接還分為兩種,原生事件和 自定義事件,只是拼接為不同字符串而已,但是處理方法一樣
方法中涉及到各種 修飾符,哈哈,想知道到底為什么能寫(xiě)出這么方便的 api 呢哈哈
綁定按鍵,阻止默認(rèn)事件,直接這么寫(xiě)就行了
@keyup.enter.prevent="xxx"
歡迎觀看下篇文章
拼接普通Slot就是直接拼接上 slot 這個(gè)屬性
` _c("test",[_c("span",{ attrs:{"slot":"name"}, slot:"name" })] ) `
如果組件有slot,沒(méi)有 slot 這個(gè)屬性,那么就不會(huì)拼接上slot,后面會(huì)直接給個(gè)默認(rèn)名字 “default”
拼接作用域Slotfunction genScopedSlots(slots, state) { return ` scopedSlots:_u([${ Object.keys(slots).map(key =>{ return genScopedSlot(key, slots[key], state) }) .join(",") }]) ` } function genScopedSlot(key, el, state) { var fn = ` function(${el.slotScope}){ return ${ el.tag === "template" ? genChildren(el, state) : genElement(el, state) } } ` return `{ key:${key} , fn: ${fn} }` }
這個(gè)處理作用域 slot 的函數(shù)看起來(lái)好像有一點(diǎn)復(fù)雜,但是其實(shí)就是紙老虎
不怕,先看一個(gè)實(shí)例
拼接成字符串,是這樣的
` _c("div",{ scopedSlots:_u([{ key:"heder", fn:function(arr){return _c("div")} }]) }) `
這個(gè)函數(shù)遍歷的是 el.scopeSlots 這個(gè)數(shù)組,或許你不知道這個(gè)數(shù)組是什么內(nèi)容?
同樣給個(gè)例子,這里有兩個(gè) slot
經(jīng)過(guò) parse 解析之后成一個(gè) ast,是這樣的
{ tag:"test", scopedSlots:[{ slotScope: "arr" slotTarget: ""a"" tag: "div" },{ slotScope: "arr" slotTarget: ""b"" tag: "div" }] }
沒(méi)錯(cuò),遍歷的就是上面對(duì)象里面的 scopedSlots 數(shù)組,數(shù)組中的每一項(xiàng)都是一個(gè)多帶帶的 slot
然后會(huì)使用 genScopeSlot 去多帶帶處理一下,上面有放出源碼
處理完之后,形成一個(gè)新的數(shù)組,genScopeSlot 也沒(méi)什么好說(shuō)的
拼接分類型,需要判斷 slot 位置的標(biāo)簽是不是 template
如果是template,那么他的真實(shí)slot 是 template 的子節(jié)點(diǎn),直接獲取他的子節(jié)點(diǎn)
如果不是template,那么本身就是真實(shí)的slot
因?yàn)?template 是Vue 自帶的一個(gè) 模板節(jié)點(diǎn),是不存在的
拼接組件VModel沒(méi)錯(cuò),這里的 model,只是屬于 組件的 v-model
if (el.model) { data += `model: { value: $ { el.model.value }, callback: $ { el.model.callback }, expression: $ { el.model.expression }, }, ` }
官網(wǎng)說(shuō)了這個(gè)是怎么用的
一個(gè)組件上的 v-model 默認(rèn)會(huì)利用名為 value 的 prop 和名為 input 的事件
也就是說(shuō),起始就是給組件傳了一個(gè) value,綁定了一個(gè)事件 input
也沒(méi)有什么好講的,記錄下組件的 v-model 是這么拼接就好了
例子經(jīng)過(guò) parse 解析,得到 ast
{ tag: "test", model:{ callback: "function ($$v) {num=$$v}" expression: ""num"" value: "num" } }
拼接成字樣變成字符串了
` _c("test",{ model:{ value:num, callback:function ($$v) {num=$$v}, expression:"num" } }) `舉個(gè)栗子
屬性拼接呢,我們就講完了,最后我們來(lái)看一個(gè)例子吧
下面這個(gè)模板,我們把它拼接起來(lái)
解析成下面這個(gè) render 字符串,看懂了,你就掌握了 generate 的內(nèi)容了
以后你就可以去看別人用Vue 寫(xiě)的打包后的代碼了
甚至,你可以手動(dòng)還原他,如果你閑得很,你可以自己寫(xiě)個(gè)方法,傳入render 字符串,自動(dòng)還原成 template 模板
` _c("div", { attrs: { "b": "2" }, domProps: { "a": 11 } },[ _c("test", { scopedSlots: _u([{ key: "a", fn: function(arr) { return _c("strong") } }]), model: { value: (num), callback: function($$v) { num = $$v }, expression: "num" } },[_c("span")]) ]) `最后
鑒于本人能力有限,難免會(huì)有疏漏錯(cuò)誤的地方,請(qǐng)大家多多包涵,如果有任何描述不當(dāng)?shù)牡胤剑瑲g迎后臺(tái)聯(lián)系本人,領(lǐng)取紅包
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/106697.html
摘要:還原的難度就在于變成模板了,因?yàn)槠渌氖裁吹仁窃獠粍?dòng)的哈哈,可是直接照抄最后鑒于本人能力有限,難免會(huì)有疏漏錯(cuò)誤的地方,請(qǐng)大家多多包涵,如果有任何描述不當(dāng)?shù)牡胤剑瑲g迎后臺(tái)聯(lián)系本人,有重謝 寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺(jué)得排版...
摘要:寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟專注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于版本如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊下面鏈接或者拉到下面關(guān)注公眾號(hào)也可以吧原理白話版終于到了要講白話的時(shí)候了 寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究...
摘要:寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟專注源碼分享,文章分為白話版和源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于版本如果你覺(jué)得排版難看,請(qǐng)點(diǎn)擊下面鏈接或者拉到下面關(guān)注公眾號(hào)也可以吧原理源碼版之拼接綁定的事件今天我們 寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究...
摘要:一旦我們檢測(cè)到這些子樹(shù),我們可以把它們變成常數(shù),這樣我們就不需要了在每次重新渲染時(shí)為它們創(chuàng)建新的節(jié)點(diǎn)在修補(bǔ)過(guò)程中完全跳過(guò)它們。否則,吊裝費(fèi)用將會(huì)增加好處大于好處,最好總是保持新鮮。 寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 Vue版本 【2.5.17】 如果你覺(jué)得排版難看,...
摘要:頁(yè)面這個(gè)實(shí)例,按理就需要解析兩次,但是有緩存之后就不會(huì)理清思路也就是說(shuō),其實(shí)內(nèi)核就是不過(guò)是經(jīng)過(guò)了兩波包裝的第一波包裝在中的內(nèi)部函數(shù)中內(nèi)部函數(shù)的作用是合并公共和自定義,但是相關(guān)代碼已經(jīng)省略,另一個(gè)就是執(zhí)行第二波包裝在中,目的是進(jìn)行緩存 寫(xiě)文章不容易,點(diǎn)個(gè)贊唄兄弟 專注 Vue 源碼分享,文章分為白話版和 源碼版,白話版助于理解工作原理,源碼版助于了解內(nèi)部詳情,讓我們一起學(xué)習(xí)吧研究基于 ...
閱讀 959·2019-08-30 14:24
閱讀 998·2019-08-30 14:13
閱讀 1804·2019-08-29 17:21
閱讀 2690·2019-08-29 13:44
閱讀 1665·2019-08-29 11:04
閱讀 449·2019-08-26 10:44
閱讀 2571·2019-08-23 14:04
閱讀 913·2019-08-23 12:08