摘要:看這篇之前,如果沒(méi)有看過(guò)之前的文章,移步拉到文章末尾查看之前的文章。而該組件實(shí)例的父實(shí)例卻并不固定,所以我們將這些在使用時(shí)才能確定的參數(shù)在組件實(shí)例化的時(shí)候傳入。系列文章地址優(yōu)化優(yōu)化總結(jié)
看這篇之前,如果沒(méi)有看過(guò)之前的文章,移步拉到文章末尾查看之前的文章。
前言在上一步,我們實(shí)現(xiàn) extend 方法,用于擴(kuò)展 Vue 類(lèi),而我們知道子組件需要通過(guò) extend 方法來(lái)實(shí)現(xiàn),我們從測(cè)試?yán)觼?lái)入手,看看這一步我們需要實(shí)現(xiàn)什么:
let test = new Vue({ data() { return { dataTest: { subTest: 1 } } }, components: { sub: { props: { propsStaticTest: { default: "propsStaticTestDefault" }, propsDynamicTest: { default: "propsDynamicTestDefault" } }, watch: { "propsDynamicTest"(newValue, oldValue) { console.log("propsDynamicTest newValue = " + newValue) } } } } })
從例子可知: sub 是 test 的子組件,同時(shí) test 組件向 sub 組件傳遞了 propsStaticTest/propsDynamicTest 兩個(gè) props 。
所以我們這一步要做兩件事
實(shí)現(xiàn)子組件生成樹(shù)結(jié)構(gòu)
實(shí)現(xiàn) props ,從例子上可以看出需要實(shí)現(xiàn)靜態(tài)和動(dòng)態(tài)兩種 prop
VUE 中組件的生成雖然在之前的步驟中,我們一直沒(méi)有涉及到模板,僅僅是把頁(yè)面的渲染抽象成一個(gè)函數(shù),主要是為了把 MVVM 中的數(shù)據(jù)綁定過(guò)程給解釋清楚,但是父子組件的實(shí)現(xiàn)卻必須要通過(guò)模板來(lái)聯(lián)系,所以我們這里簡(jiǎn)單的介紹下 Vue 中由模板到生成頁(yè)面渲染函數(shù)的過(guò)程
得到模板(DOM 字符串)或是 render 函數(shù)
分析模板,得到 HTML 語(yǔ)法樹(shù)(AST),生成 render 函數(shù)。如果直接給的是 render 則沒(méi)有這個(gè)步驟
由 render 函數(shù)生成 VNode 這就是虛擬樹(shù)了
將 Vnode 作為參數(shù)傳入一個(gè)函數(shù)中,就能得到 html 渲染函數(shù)
ok 看起來(lái)和組件好像沒(méi)有什么關(guān)系,我們分析下組件寫(xiě)法
由上面這個(gè)標(biāo)簽我們可以得到什么?
這是一個(gè)子組件,組件名:sub
傳遞了一個(gè)靜態(tài)的 prop :propsStaticTest
傳遞了一個(gè)動(dòng)態(tài)的 prop :propsDynamicTest
靜態(tài)說(shuō)明這個(gè)屬性不會(huì)發(fā)生變化,動(dòng)態(tài)會(huì),最明顯的區(qū)別就是:動(dòng)態(tài)屬性有 :/v-bind 修飾
結(jié)合上面的第2個(gè)步驟,會(huì)分析出一些東西。僅僅針對(duì) props ,假設(shè)模板解析引擎會(huì)解析出下面這樣一個(gè)結(jié)構(gòu)
let propsOption = [{ key: "propsStaticTest", value: "propsStaticValue", isDynamic: false }, { key: "propsDynamicTest", value: "dataTest.subTest", isDynamic: true }]
注: 這里僅僅是我的假設(shè),方便理解,在 Vue 中的模板解析出來(lái)的內(nèi)容要比這個(gè)復(fù)雜。
ok 有了上面的鋪墊我們來(lái)實(shí)現(xiàn)父子組件和 props
父子組件實(shí)例初始化的實(shí)例我們需要做的僅僅就是保存組件之間的關(guān)系就行,ok 我們來(lái)實(shí)現(xiàn)它
class Vue extends Event { ··· _init(options) { let vm = this ··· // 獲取父節(jié)點(diǎn) let parent = vm.$options.parent // 將該節(jié)點(diǎn)放到父節(jié)點(diǎn)的 $children 列表中 if (parent) { parent.$children.push(vm) } // 設(shè)置父節(jié)點(diǎn)和根節(jié)點(diǎn) vm.$parent = parent vm.$root = parent ? parent.$root : vm // 初始化子節(jié)點(diǎn)列表 vm.$children = [] } }
我們需要做的僅僅就是給傳入 options 設(shè)置 parent ,就能明確組件之間的關(guān)系。
接著我們模擬一下當(dāng)模板編譯的時(shí)候碰到 的情況,具體的來(lái)說(shuō)就是會(huì)執(zhí)行以下代碼:
let testSubClass = Vue.extend(test.$options.components.sub) let testSub = new testSubClass({parent: test}) console.log(testSub.$parent === test) // true
ok 現(xiàn)在我們先不想模板編譯具體是如何進(jìn)行的,從這兩行代碼中,我們可以看出我們先使用了 extend 擴(kuò)展了 Vue 實(shí)例,生成一個(gè)子類(lèi)(testSubClass),接著我們實(shí)例化該類(lèi),傳入?yún)?shù)確定父實(shí)例。
想象下一,我們?yōu)槭裁匆謨刹桨褏?shù)傳入。
我們知道當(dāng)我們寫(xiě)好子組件的配置時(shí),子組件的內(nèi)部狀態(tài)就已經(jīng)確定了,所以我們可以根據(jù)這些固定的配置去擴(kuò)展 Vue 類(lèi)方便我們調(diào)用(使用的時(shí)候 new 一下就可以)。
而該組件實(shí)例的父實(shí)例卻并不固定,所以我們將這些在使用時(shí)才能確定的參數(shù)在組件實(shí)例化的時(shí)候傳入。
接著我們來(lái)想象一下,如果子組件(sub)里面還有子組件(sub-sub)會(huì)怎么樣?
使用 extend 擴(kuò)展 Vue 類(lèi)
確定父實(shí)例,new 的時(shí)候傳入,而這個(gè) parent 就是 sub
這樣調(diào)用過(guò)多次之后,一顆 Vue 的實(shí)例樹(shù)就生成了,每一個(gè)節(jié)點(diǎn)都保留著父實(shí)例的引用,子組件列表還有根實(shí)例。
希望你的腦子里已經(jīng)長(zhǎng)出了這顆樹(shù)~
ok 接下來(lái)我們來(lái)實(shí)現(xiàn) props
props希望你還記得下面這幾行代碼:
let propsOption = [{ key: "propsStaticTest", value: "propsStaticValue", isDynamic: false }, { key: "propsDynamicTest", value: "dataTest.subTest", isDynamic: true }]
這個(gè)是我們模擬模板編譯時(shí)關(guān)于 props 的部分產(chǎn)出,具體的來(lái)說(shuō)就是鍵值對(duì),以及是否有 :/v-bind 修飾,而我們知道在 Vue 中這個(gè)修飾符是表示是否是動(dòng)態(tài)綁定,所以我在這里使用 isDynamic 來(lái)標(biāo)志。
首先我們來(lái)獲取屬性的數(shù)據(jù),由于動(dòng)態(tài)綁定的 props 是取值路徑,所以我們得去父對(duì)象下獲取值。
let propsData = {} propsOption.forEach(item => { if (item.isDynamic) { // eg: "dataTest.subTest" => test.dataTest.subTest 將字符串轉(zhuǎn)換成取值 propsData[item.key] = item.value.split(".").reduce((obj, name) => obj[name], test) } else { propsData[item.key] = item.value } }) console.log(propsData) // { propsStaticTest: "propsStaticValue", propsDynamicTest: 1 }
ok 我們拿到中屬性對(duì)應(yīng)的值,接著把 propsData 給傳進(jìn)去
let testSub = new testSubClass({parent: test, propsData})
接著我們?cè)?_init 方法中來(lái)處理 props
_init(options) { ··· let props = vm._props = {} let propsData = vm.$options.propsData for (let key in vm.$options.props) { let value = propsData[key] // 如果沒(méi)有傳值,使用默認(rèn)值 if (!value) { value = vm.$options.props[key].default } props[key] = value } observe(props) for (let key in props) { proxy(vm, "_props", key) } ··· }
porps 的處理和 data 類(lèi)似,需要變成可監(jiān)聽(tīng)結(jié)構(gòu),代理到 this 對(duì)象下,無(wú)非 data 是從傳入的函數(shù)取值,而 props 從傳入的 propsData 中取值。
ok 直到現(xiàn)在為止,看起來(lái)都很美好,但是部分 props 是動(dòng)態(tài)的,父組件相應(yīng)值的變化是需要同步到子組件中的,但目前我們還沒(méi)有實(shí)現(xiàn)父組件和子組件的聯(lián)系,僅僅是把值給取出放在子組件內(nèi)而已。
其實(shí)一看到監(jiān)聽(tīng)變化就理所當(dāng)然的想到 Watcher,ok 我們用 Watcher 來(lái)實(shí)現(xiàn)它:
propsOption.forEach(item => { if (item.isDynamic) { new Watcher({}, () => { return item.value.split(".").reduce((obj, name) => obj[name], test) }, (newValue, oldValue) => { testSub[item.key] = newValue }) } })
ok 最后一步完成,完整的測(cè)試代碼
本來(lái)還想實(shí)現(xiàn)下 provide/inject 但篇幅有點(diǎn)大了,下一步實(shí)現(xiàn),也做個(gè)總結(jié)。
系列文章地址VUE - MVVM - part1 - defineProperty
VUE - MVVM - part2 - Dep
VUE - MVVM - part3 - Watcher
VUE - MVVM - part4 - 優(yōu)化Watcher
VUE - MVVM - part5 - Observe
VUE - MVVM - part6 - Array
VUE - MVVM - part7 - Event
VUE - MVVM - part8 - 優(yōu)化Event
VUE - MVVM - part9 - Vue
VUE - MVVM - part10 - Computed
VUE - MVVM - part11 - Extend
VUE - MVVM - part12 - props
VUE - MVVM - part13 - inject & 總結(jié)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/94884.html
摘要:通過(guò)裝作這些變化,我們實(shí)現(xiàn)了從而到達(dá)了數(shù)據(jù)變化觸發(fā)函數(shù)的過(guò)程。于此同時(shí),我們還實(shí)現(xiàn)了來(lái)擴(kuò)展這個(gè)可響應(yīng)的結(jié)構(gòu),讓這個(gè)對(duì)象擁有了觸發(fā)和響應(yīng)事件的能力。最后,根據(jù)我們的實(shí)現(xiàn),這是最終的產(chǎn)出,一個(gè)框架,了解一下系列文章地址優(yōu)化優(yōu)化總結(jié) 看這篇之前,如果沒(méi)有看過(guò)之前的文章,移步拉到文章末尾查看之前的文章。 provide / inject 在上一步我們實(shí)現(xiàn)了,父子組件,和 props 一樣 pr...
摘要:在中關(guān)于如何實(shí)現(xiàn)在網(wǎng)上可以搜出不少,在看了部分源碼后,梳理一下內(nèi)容。換個(gè)說(shuō)法,當(dāng)我們?nèi)≈档臅r(shí)候,函數(shù)自動(dòng)幫我們添加了針對(duì)當(dāng)前值的依賴(lài),當(dāng)這個(gè)值發(fā)生變化的時(shí)候,處理了這些依賴(lài),比如說(shuō)節(jié)點(diǎn)的變化。 在 VUE 中關(guān)于如何實(shí)現(xiàn)在網(wǎng)上可以搜出不少,在看了部分源碼后,梳理一下內(nèi)容。 首先,我們需要了解一下 js 中的一個(gè) API :Object.defineProperty(obj, prop,...
摘要:所以方法,是對(duì)默認(rèn)進(jìn)行擴(kuò)展,從而實(shí)現(xiàn)擴(kuò)展。這里我用了這個(gè)庫(kù)提供的合并方法,用來(lái)合并兩個(gè)對(duì)象,并不會(huì)修改原對(duì)象的內(nèi)容。測(cè)試符合我們的預(yù)期,方法也就實(shí)現(xiàn)了,下一步,實(shí)現(xiàn)父子組件。系列文章地址優(yōu)化優(yōu)化總結(jié) 看這篇之前,如果沒(méi)有看過(guò)之前的文章,移步拉到文章末尾查看之前的文章。 組件的擴(kuò)展 在 Vue 中有 extend 方法可以擴(kuò)展 Vue 的實(shí)例,在上一步中,有一些實(shí)現(xiàn)是必須要通過(guò)子父組件才...
摘要:了解之后我們來(lái)實(shí)現(xiàn)它,同樣的為了方便理解我寫(xiě)成了一個(gè)類(lèi)這里的一般是的實(shí)例將屬性代理到實(shí)例下的構(gòu)造函數(shù)我們實(shí)現(xiàn)了代理屬性和更新計(jì)算屬性的值,同時(shí)依賴(lài)沒(méi)變化時(shí),也是不會(huì)觸發(fā)的更新,解決了以上的個(gè)問(wèn)題。 看這篇之前,如果沒(méi)有看過(guò)之前的文章,移步拉到文章末尾查看之前的文章。 回顧 先捋一下,之前我們實(shí)現(xiàn)的 Vue 類(lèi),主要有一下的功能: 屬性和方法的代理 proxy 監(jiān)聽(tīng)屬性 watche...
摘要:關(guān)于中的的實(shí)現(xiàn),差不多也就這樣了,當(dāng)然這僅僅是基礎(chǔ)的實(shí)現(xiàn),而且視圖層層渲染抽象成一個(gè)函數(shù)。不同于中的實(shí)現(xiàn),這里少了很多各種標(biāo)記和應(yīng)用標(biāo)記的過(guò)程。 看這篇之前,如果沒(méi)有看過(guò)之前的文章,可拉到文章末尾查看之前的文章。 回顧 首先我們思考一下截止當(dāng)前,我們都做了什么 通過(guò) defineReactive 這個(gè)函數(shù),實(shí)現(xiàn)了對(duì)于數(shù)據(jù)取值和設(shè)置的監(jiān)聽(tīng) 通過(guò) Dep 類(lèi),實(shí)現(xiàn)了依賴(lài)的管理 通過(guò) Wa...
閱讀 4411·2021-11-22 13:52
閱讀 2587·2021-11-22 13:52
閱讀 3744·2021-11-19 09:59
閱讀 1265·2021-11-17 09:33
閱讀 2519·2019-08-30 10:53
閱讀 1358·2019-08-29 17:28
閱讀 1362·2019-08-29 17:03
閱讀 3148·2019-08-26 11:31