Vue中父孫組件通訊

Vue中父子組件通訊有官方方案:props down ,events up。而且父子組件通訊這個話題也被講爛了。所以我準備說點別的,談談父孫組件通訊,並且把範圍局限在向下傳遞數據。

最直接的解決方案當然是把父孫組件通訊問題轉換為兩個父子組件通訊問題,這一點毛病都沒有。在子組件child上,我們不僅要聲明這個組件自己需要的props,還要把希望父組件parent傳下來控制孫子組件grandchild 的props聲明一遍。這樣確實可行,我也這麼用過。

當孫子組件grandchild 希望傳入的 props較多時,child需要聲明的props的組件也越來越多,為此可以使用mixin的形式把公有的props抽出來。另一個優化方案是使用$props,舉個element-ui中的例子:

<textarean v-elsen class="el-textarea__inner"n :value="currentValue"n @input="handleInput"n ref="textarea"n v-bind="$props"n :style="textareaStyle"n @focus="handleFocus"n @blur="handleBlur">n </textarea>n

這是把child組件所有的props都傳遞給了孫子組件grandchild ,無論孫子組件是否需要。而且子組件上會有一堆自己用不到只是傳下去的屬性,要注意這些屬性都被轉換為響應式的,而且都被代理到vm上,其實是一些無效的開銷。

再進一步的方案就要用到vue2.4提供的$attrs屬性了,它對應父組件傳遞下來但是子組件用不到的屬性。實現它似乎也不是很難,父組件傳下來的在vm.$options.propsData里,子組件需要的屬性在vm.$options.props里,求個差集就好了。這樣做的好處是子組件不用負責$attrs對應屬性的取值校驗、響應式處理、代理了,僅僅負責做個差集操作,而且是否做這個差集操作還可以被控制(inheritAttrs屬性),我們的模板可能是這樣的:

<field_enum_selectn v-model="model"n :candidate="candidate"n v-bind="$attrs"n >n <template v-if="$slots.default">n <slot></slot>n </template>n </field_enum_select>n

上面代碼還涉及了slot的傳遞,然而這不是本文要說明了。

到這裡就完了嗎?那我豈不是就是針對官方文檔加了個例子然後說明了一下這個屬性的好處? 到這就完了不是我的風格。父孫組件通訊,子組件難道不會插一腳嗎? 你可能會說,在子組件child內,我明確知道要傳給孫子組件grandchild哪些屬性啊,直接在模板里聲明就好了。我只能說too young too simple,說的就和所有場景下都能知道有哪些屬性似的,提醒一下,我用到了元組件component。那我們就把 $attrs子組件要傳下去的屬性merge一下唄:

export default{n methods:{n mergeAttrsConfig(config){n if(!config || typeof config !== object){n return this.$attrs || {};n }nn let target = {};n if(this.$attrs && typeof this.$attrs === object){n let attrKeys = Object.keys(this.$attrs);n attrKeys.forEach((key)=>{n let descriptor = Object.getOwnPropertyDescriptor(this.$attrs,key);n Object.defineProperty(target,key,descriptor);n });n }nn let configKeys = Object.keys(config);n configKeys.forEach((key)=>{n let descriptor = Object.getOwnPropertyDescriptor(config,key);n Object.defineProperty(target,key,descriptor)n });nn return target;n }n }n}n

我們通過v-bind綁定的就不是$attrs,而是$attrs和子組件需要傳遞的屬性merge的結果。

到這裡就完了嗎?算是吧,但是一個重要的props屬性我略過去了,就是那個v-model的value。value的通信似乎更好玩一點,有空填坑。


分割線後面問個問題蛤,Vue的子類註冊全局組件的時候為什麼要用Vue的extend方法而不是子類的靜態方法extend呢?

// _base指向Vuendefinition = this.options._base.extend(definition)n

推薦閱讀:

我還是想談談JS裡面的閉包
開箱即用的網站可訪問性提升指南
利用Dawn工程化工具實踐MobX數據流管理方案
Yarn vs npm:你需要知道的一切

TAG:前端开发 | Vuejs |