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