上善若水,水善利萬物而不爭。——《道德經》 簡介 在平時開發是經常用到一些父子組件通信,經常用到props、vuex等等,這里面記錄另外的三種方式v-model、sync是怎么使用,再說是怎么實現,其實v-model、sync都是語法糖。還有$attr、$listener實現父子組件通信。 使用方式 v-model 2.2.0+ 新增 v-mode1其實就是一個語法糖,默認會利用名為value的props和名為input的事件,但是像單選框、復選框等類型的輸入龍劍可能會講value特性用於不同的目的。 v-model的使用場景:當子組件需要改變父組件通過props傳入的值 父組件 父組件通過v-model綁定值 如需根據v-model傳入的值改變,而觸發其他更新請通過watch傳入的值 子組件 聲明model對象 設置事件event和prop字段 通過porps接受父組件傳送值 修改是通過this.$emit廣播事件 代碼示例: 父組件代碼 <template> <children v-model="message"></children> </template> <script> import children from "./children.vue"; export default { components: { children }, data() { return { message: "parent" }; }, watch: { // 監聽message變化 message(newV, oldV) { console.log(newV, oldV); } } }; </script> 子組件代碼 <template> <h1>{{ message }}</h1> </template> <script> export default { model: { prop: "message", //這個字段,是指父組件設置 v-model 時,將變量值傳給子組件的 msg event: "input" //這個字段,是指父組件監聽 parent-event 事件 }, props: { message: String //此處必須定義和model的prop相同的props,因為v-model會傳值給子組件 }, mounted() { //這里模擬異步將msg傳到父組件v-model,實現雙向控制 setTimeout(_ => { this.$emit("input", "children"); //將這個值通過 emit 觸發parent-event,將some傳遞給父組件的v-model綁定的變量 }, 1500); } }; </script> 上面這個示例是通過v-model實現的,下面不通過v-model實現同樣效果。 不使用 v-model 實現 代碼示例如下: 父組件代碼修改 <template> <Children :message="message" @input="(event) => { message = event }"/> </template> <script> // 不變 </script> 子組件代碼修改 <template> // 不變 </template> <script> export default { props: { message: String }, mounted() { setTimeout(() => { this.$emit("input", "children"); }, 1500); } }; </script> 只是把v-model拆分為props和@input事件,子組件不需要配置model,只需要接受props和通過this.$emit廣播事件就可以。 當然這個相對於v-model方法比較簡便,但是靈活度查很多,選擇使用那種看個人喜好。 在線地址: 不能放iframe只能放一放一個鏈接了本篇代碼實例 sync 2.3.0+ 新增 在有些情況下,我們可能需要對一個 prop 進行**“雙向綁定”。不幸的是,真正的雙向綁定**會帶來維護上的問題,因為子組件可以修改父組件,且在父組件和子組件都沒有明顯的改動來源。 這也是為什么我們推薦以 update:myPropName 的模式觸發事件取而代之。同時也可以通過sync修飾符來實現。 在上面代碼的基礎上大致修改如下: 父組件 通過修改觸發事件input為update:myPropName實現相同效果 子組件 通過修改this.$emit(update:myPropName) 代碼如下: 父組件代碼修改 // 修改如下 <Children :message="message" @update:input="(event) => { message = event }"/> 子組件代碼修改 // 其他不變 this.$emit("update:input", "children"); sync實現 上面的代碼可以通過sync簡寫為下面代碼: 父組件代碼修改 // 修改如下 <Children :messag.sync="message"/> 子組件代碼修改 // 其他不變 this.$emit("update:messag", "children"); 同時sync也支持對象,要配合v-bind實現可以簡寫為:,但是要注意這個對象如下兩條: 注意帶有 .sync 修飾符的 v-bind 不能和表達式一起使用 (例如 v-bind:title.sync=”doc.title + ‘!’” 是無效的)。取而代之的是,你只能提供你想要綁定的屬性名,類似 v-model。 將 v-bind.sync 用在一個字面量的對象上,例如 v-bind.sync=”{ title: doc.title }”,是無法正常工作的,因為在解析一個像這樣的復雜表達式的時候,有很多邊緣情況需要考慮。 attrs、listeners $attrs 2.4.0 新增 類型:{ [key: string]: string } 只讀 詳細: 包含了父作用域中不作為 prop 被識別 (且獲取) 的特性綁定 (class 和 style 除外)。當一個組件沒有聲明任何 prop 時,這里會包含所有父作用域的綁定 (class 和 style 除外),並且可以通過 v-bind="$attrs" 傳入內部組件——在創建高級別的組件時非常有用。 $listeners 2.4.0 新增 類型:{ [key: string]: Function | Array<Function> } 只讀 詳細: 包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監聽器。它可以通過 v-on="$listeners" 傳入內部組件——在創建更高層次的組件時非常有用。 實現通信 實現父子組件通信 父組件代碼 <template> <div class="parent"> <Children :message="message" @upDate="upDate" type="del" @input="(event) => { message = event }" /> </div> </template> <script> import Children from "./Children"; export default { components: { Children }, data() { return { message: "parent", type: "del" }; }, methods: { upDate (event) { console.log(event); this.type = event; } }, watch: { message: function() { console.log("更新message值為" + this.message); } } }; </script> 子組件代碼 <template> <div v-bind="$attrs" v-on="$listeners" class="children">{{message}} <span @click="$listeners.upDate('data')">{{$attrs.type}}</span></div> </template> <script> export default { props: { message: String }, mounted() { // console.log(this.$attrs); // console.log(this.$listeners); setTimeout(() => { this.$emit("input", "children"); this.$emit('upDate', 'add') }, 1500); } }; </script> 同時$attrs、$listeners都是可以跨域父子組件,可以父子子子組件傳遞,類似於react中的context,只是一部分設計理念相同。 總結 其實就是檢測到.sync修飾符,在complier階段會編譯生成多個prop,生成多個事件。其實像這個指令、修飾符、自定義指令都是在vue編譯是解析成為v8能執行的代碼。 無論是vue、babel、react的complier編譯階段大致分為三個階段: 通過詞法解析parse生成抽象AST或抽象代碼樹 優化AST,比如vue標記靜態節點,babal中抽取靜態代碼,這個階段被稱為optimize或者優化AST樹 在AST代碼的階段上,生成可執行代碼,這個過程可以叫做codegen v-model、sync都可以實現父子組件通信,並且可以在子組件中修改父組件傳入的值。在平常看法的時候進場可以用到這兩種方式,具體選擇那種方式看個人喜好。在element-ui這個input組件也用到相關的屬性。