看這篇之前,假如沒有看過之前的文章,移步拉到文章末端檢察之前的文章。
媒介
在上一步,我們完成 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 propsStaticTest="propsStatiValue" :propsDynamicTest="dataTest.subTest">
由上面這個標籤我們能夠獲得什麼?
- 這是一個子組件,組件名:
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
,就可以明白組件之間的關聯。
接着我們模仿一下當模板編譯的時刻遇到 <sub></sub> 的狀況,詳細的來講就是會實行以下代碼:
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 & 總結