<!-- 子组件模板 --> <template> <input v-model="msg"> <button v-on:click="notify">Dispatch Event</button> </template> <!-- 父组件模板 --> <div> <p>Messages: {{ messages | json }}</p> <child></child> </div> // 注册子组件 // 将当前消息派发出去 Vue.component('child', { template: '#child-template', data: function () { return { msg: 'hello' } }, methods: { notify: function () { if (this.msg.trim()) { this.$dispatch('child-msg', this.msg) this.msg = '' } } } }) // 初始化父组件 // 将收到消息时将事件推入一个数组 var parent = new Vue({ el: '#events-example', data: { messages: [] }, // 在创建实例时 `events` 选项简单地调用 `$on` events: { 'child-msg': function (msg) { // 事件回调内的 `this` 自动绑定到注册它的实例上 this.messages.push(msg) } } })
使用 v-on 绑定自定义事件
上例非常好,不过从父组件的代码中不能直观的看到 "child-msg" 事件来自哪里。如果我们在模板中子组件用到的地方声明事件处理器会更好。为此子组件可以用 v-on 监听自定义事件:
<child v-on:child-msg="handleIt"></child>
这样就很清楚了:当子组件触发了 `”child-msg”` 事件,父组件的 `handleIt` 方法将被调用。所有影响父组件状态的代码放到父组件的 `handleIt` 方法中;子组件只关注触发事件。
子组件索引
尽管有 props 和 events,但是有时仍然需要在 JavaScript 中直接访问子组件。为此可以使用 v-ref 为子组件指定一个索引 ID。例如:
<div> <user-profile v-ref:profile></user-profile> </div> var parent = new Vue({ el: '#parent' }) // 访问子组件 var child = parent.$refs.profile
v-ref 和 v-for 一起用时,ref 是一个数组或对象,包含相应的子组件。
使用 Slot 分发内容
在使用组件时,常常要像这样组合它们:
<app> <app-header></app-header> <app-footer></app-footer> </app>
注意两点:
1.<app> 组件不知道它的挂载点会有什么内容,挂载点的内容是由 <app> 的父组件决定的。
2.<app> 组件很可能有它自己的模板。
为了让组件可以组合,我们需要一种方式来混合父组件的内容与子组件自己的模板。这个处理称为内容分发(或 “transclusion”,如果你熟悉 Angular)。Vue.js 实现了一个内容分发 API,参照了当前 Web 组件规范草稿,使用特殊的 <slot> 元素作为原始内容的插槽。
编译作用域
在深入内容分发 API 之前,我们先明确内容的编译作用域。假定模板为:
<child-component>
{{ msg }}
</child-component>
msg 应该绑定到父组件的数据,还是绑定到子组件的数据?答案是父组件。组件作用域简单地说是:
父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译
一个常见错误是试图在父组件模板内将一个指令绑定到子组件的属性/方法:
<!-- 无效 -->
<child-component v-show="someChildProperty"></child-component>
假定 someChildProperty 是子组件的属性,上例不会如预期那样工作。父组件模板不应该知道子组件的状态。
如果要绑定子组件内的指令到一个组件的根节点,应当在它的模板内这么做:
Vue.component('child-component', { // 有效,因为是在正确的作用域内 template: '<div v-show="someChildProperty">Child</div>', data: function () { return { someChildProperty: true } } })
类似地,分发内容是在父组件作用域内编译。
单个 Slot
父组件的内容将被抛弃,除非子组件模板包含 <slot>。如果子组件模板只有一个没有特性的 slot,父组件的整个内容将插到 slot 所在的地方并替换它。
<slot> 标签的内容视为回退内容。回退内容在子组件的作用域内编译,当宿主元素为空并且没有内容供插入时显示这个回退内容。
假定 my-component 组件有下面模板:
<div> <h1>This is my component!</h1> <slot> 如果没有分发内容则显示我。 </slot> </div>