VUE - MVVM - part12 - props

看這篇之前,假如沒有看過之前的文章,移步拉到文章末端檢察之前的文章。

媒介

在上一步,我們完成 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)
                }
            }
        }
    }
})

從例子可知: subtest 的子組件,同時 test 組件向 sub 組件傳遞了 propsStaticTest/propsDynamicTest 兩個 props

所以我們這一步要做兩件事

  1. 完成子組件天生樹構造
  2. 完成 props ,從例子上能夠看出需要完成靜態和動態兩種 prop

VUE 中組件的天生

雖然在之前的步驟中,我們一向沒有涉及到模板,僅僅是把頁面的襯着籠統成一個函數,重要是為了把 MVVM 中的數據綁定歷程給詮釋清晰,然則父子組件的完成卻必需要經由歷程模板來聯絡,所以我們這裏簡樸的引見下 Vue 中由模板到天生頁面襯着函數的歷程

  1. 獲得模板(DOM 字符串)或是 render 函數
  2. 剖析模板,獲得 HTML 語法樹(AST),天生 render 函數。假如直接給的是 render 則沒有這個步驟
  3. render 函數天生 VNode 這就是假造樹了
  4. Vnode 作為參數傳入一個函數中,就可以獲得 html 襯着函數

ok 看起來和組件彷佛沒有什麼關聯,我們剖析下組件寫法

<sub propsStaticTest="propsStatiValue" :propsDynamicTest="dataTest.subTest">

由上面這個標籤我們能夠獲得什麼?

  1. 這是一個子組件,組件名:sub
  2. 傳遞了一個靜態的 proppropsStaticTest
  3. 傳遞了一個動態的 proppropsDynamicTest

靜態申明這個屬性不會發生變化,動態會,最顯著的區分就是:動態屬性有
:/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)會怎樣?

  1. 運用 extend 擴大 Vue
  2. 肯定父實例,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 但篇幅有點大了,下一步完成,也做個總結。

系列文章地點

  1. VUE – MVVM – part1 – defineProperty
  2. VUE – MVVM – part2 – Dep
  3. VUE – MVVM – part3 – Watcher
  4. VUE – MVVM – part4 – 優化Watcher
  5. VUE – MVVM – part5 – Observe
  6. VUE – MVVM – part6 – Array
  7. VUE – MVVM – part7 – Event
  8. VUE – MVVM – part8 – 優化Event
  9. VUE – MVVM – part9 – Vue
  10. VUE – MVVM – part10 – Computed
  11. VUE – MVVM – part11 – Extend
  12. VUE – MVVM – part12 – props
  13. VUE – MVVM – part13 – inject & 總結
    原文作者:aco
    原文地址: https://segmentfault.com/a/1190000014810301
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞