Vue躬行記(5)——元件通訊
元件之間除了保持獨立之外,還需要相互通訊,本章將介紹幾種通訊的方式。
一、直接訪問
Vue提供了三個例項屬性,可直接訪問父元件、子元件和根例項,如下所列。
(1)$parent:父元件。
(2)$root:根例項,如果沒有父例項,那麼讀取的將是自身。
(3)$children:直接子元件,無法獲取隔代的子元件,並且不保證元件的順序,也非響應式。
下面用一個示例來演示它們的用法,首先建立兩個父子關係的元件parent和child;接著初始化根例項vm,掛載的<div>元素包含parent元件;最後為根例項和parent元件的資料物件新增name屬性。
<div id="container"> <parent></parent> </div> <script> Vue.component("child", { template: '<p>子元件</p>', mounted: function() { this.$parent.name; //"parent" this.$root.name; //"root" } }); Vue.component("parent", { data: function() { return { name: "parent" }; }, template: '<child></child>' }); var vm = new Vue({ el: "#container", data: { name: "root" } }); vm.$children; //[VueComponent] </script>
當執行vm.$children時,得到的是一個子元件陣列。在child元件的mounted鉤子中呼叫了例項的$parent和$root的name屬性,其值分別是“parent”和“root”。
大部分情況下,應當避免直接修改元件的內部,因為這麼做不僅讓父子元件緊密耦合,而且還難以追蹤是誰發起的變更。
二、ref和$refs
如果要直接訪問子元素或子元件,那麼除了使用上文的$children屬性之外,還能通過ref特性配合$refs屬性實現。
DOM元素或元件可通過宣告ref特性來指定一個索引識別符號,即註冊引用資訊。而父元件的$refs屬性則記錄了宣告過ref特性的子元素和子元件,它的值是一個物件,其鍵就是ref特性的值。下面是一個簡單的例子,註冊了父元件parent和子元件child,並且為child元件和<input>元素分別聲明瞭ref特性。
Vue.component("child", { template: '<input ref="txt" />', mounted: function() { this.$refs; //{txt: input} } }); Vue.component("parent", { template: '<child ref="child"></child>', mounted: function() { this.$refs; //{child: VueComponent} } });
在兩個元件的mounted鉤子中,讀取了各自例項的$refs屬性。如果要在parent元件中讀取child元件的$refs屬性,那麼可以像下面這樣。
this.$refs.child.$refs
注意,$refs不是響應式的,並且在渲染到頁面之前是無法訪問的,即不能在mounted之前的鉤子中使用。
當ref特性與v-for指令配合時,引用的將是一個數組,如下所示。
Vue.component("child", { data: function() { return { names: ["strick", "freedom"] }; }, template: `<div> <input v-for="item in names" ref="txt" /> </div>`, mounted: function() { this.$refs; //{txt: [input, input]} } });
三、自定義事件
元件支援自定義事件,並且能在子元件中觸發該事件,從而實現元件之間的通訊。假設有兩個父子關係的元件parent和child,在child元件上聲明瞭自定義的dot事件,而在<button>元素上添加了click事件,它們接收的事件處理程式都叫add,如下所示。
Vue.component("child", { template: '<button @click="add">提交</button>', methods: { add: function() { this.$emit("dot", 1, 2); } } }); Vue.component("parent", { template: '<child @dot="add"></child>', methods: { add: function(left, right) { console.log(left, right); //1 2 } } });
Vue提供的例項方法$emit(),它的第一個引數是要觸發的事件名稱,其餘引數都將回傳給該事件的處理程式。在子元件child的add()方法中向$emit()傳遞了三個引數(“dot”、1和2),父元件parent中的add()方法能接收從子元件傳遞過來的兩個數值(1和2)。
注意,自定義的事件名稱不要用駝峰的命名方式,因為它沒有等價的連字元分隔式的名稱,例如下面的addNumber和add-number是兩個事件。
<child @addNumber="handle"></child> <!-- 不同 --> <child @add-number="handle"></child>
在DOM模板中,由於事件名稱會被自動轉換為小寫,因此像下面這樣呼叫addNumber事件將會失敗。但在字串模板中不會受其影響,依然能呼叫成功。
this.$emit("addNumber");
1)v-model
元件也支援v-model指令,但需要做些配置。預設情況下,元件的v-model只監聽value特性的變化以及input事件。如果要定製,那麼可以使用model選項,並且需要在props中新增要監聽的特性。下面註冊一個checkbox元件,並讓它的v-model指令關聯模板內的複選框的checked特性和change事件。
Vue.component("checkbox", { model: { prop: "checked", event: "change" }, props: { checked: Boolean }, template: '<input type="checkbox" :checked="checked" @change="dot" />', methods: { dot: function(e) { console.log(e.target.checked); } } });
在將v-model作用於checkbox元件上後(如下所示),每次點選渲染出的複選框,就會在控制檯輸出當前的選中狀態。
<div id="container"> <checkbox v-model="current"></checkbox> </div> <script> var vm = new Vue({ el: "#container", data: { current: true } }); </script>
&n