组件基础
组件(component),是具有 name 名称的可复用 Vue 实例:当前示例中是 <button-counter>
。我们可以使用 new Vue
创建出一个 Vue 根实例,然后将这个组件作为其中的一个自定义元素(custom element)。
由于组件是可复用的 Vue 实例,它们接收的选项,和在 new Vue
时候的选项相同,例如 data
, computed
, watch
, methods
和生命周期钩子。唯一的例外是,类似 el
这样,根实例上特有(root-specific)的选项。
data
必须是一个函数
组件的 data
选项必须是一个函数,以便每个实例都可以维护「函数返回的数据对象」的彼此独立的数据副本,
如果 Vue 没有遵循这个规定,点击其中一个按钮,会影响其他所有用到此 data 的组件实例,
组件注册有两种方式:全局注册和局部注册。
全局方式注册的组件,可以用于之后创建的所有(通过 new Vue
创建的)Vue 根实例,以及 Vue 实例组件树中所有子组件的内部。
使用 props 向子组件传递数据
new Vue({ el: '#blog-post-demo', data: { posts: [ { id: 1, title: '我的 Vue 旅程' }, { id: 2, title: '用 Vue 写博客' }, { id: 3, title: 'Vue 如此有趣' }, ] } }) <blog-post v-for="post in posts" v-bind:key="post.id" v-bind:title="post.title" ></blog-post>
Vue.component('blog-post', { props: ['title'], template: '<h3>{{ title }}</h3>' })
使用 events 向父组件发送消息
new Vue({ el: '#blog-posts-events-demo', data: { posts: [/* ... */], postFontSize: 1 //定义一个对象用作字体大小 } }) <div id="blog-posts-events-demo"> <div :style="{ fontSize: postFontSize + 'em' }"> //将style属性动态的绑定起来,一旦数据传入可以及时更新 <blog-post v-for="post in posts" v-bind:key="post.id" v-bind:post="post" v-on:enlarge-text="postFontSize += 0.1" //监听enlarge-text事件,触发则执行。 ></blog-post> </div> </div>
Vue.component('blog-post', { props: ['post'], template: ` <div class="blog-post"> <h3>{{ post.title }}</h3> <button v-on:click="$emit('enlarge-text')> //添加点击事件,并且将此事件返回给父组件的enlarge-text事件 放大文本 </button> <div v-html="post.content"></div> </div> ` })
在 event 事件中发送一个值
有时,想在 event 事件中发送一个特定的值。例如,我们可能想要在 <blog-post>
组件自身内部,去控制放大文本字号的间隔。在这种情况下,我们可以使用 $emit
的第二个参数来提供字号间隔值:
<button v-on:click="$emit('enlarge-text', 0.1)"> 放大文本 </button> <blog-post ... v-on:enlarge-text="postFontSize += $event" //此时$event就是传入的值0.1 ></blog-post>
或者,如果事件处理函数是一个方法:
<blog-post ... v-on:enlarge-text="onEnlargeText" ></blog-post> methods: { onEnlargeText: function (enlargeAmount) { //此时$event传入的值会作为方法的第一个参数,这里是enlargeAmount this.postFontSize += enlargeAmount } }
在组件中使用 v-model
自定义事件(custom event),还可以用来创建出「实现 v-model
机制的自定义输入框(custom input)」
<input v-model="searchText">
等同于如下: //完全意义上的等同 <input v-bind:value="searchText" //绑定value和searchText的值(即随着searchText实时更新value) v-on:input="searchText = $event.target.value" //将value值赋值给searchText > //以上便实现了searchText和value的双向绑定
而用于一个组件时,也是如此。
为了组件内部能够有效运行,组件内的 <input>
必须:
将 value
属性绑定到 value
prop
在 input
输入框中,在自定义的 input
事件中,发送一个新的值
这里就是上面所描述的:
Vue.component('custom-input', { props: ['value'], //2.接受父组件传递的value值 template: ` <input v-bind:value="value" //3.将收到的value赋值给input框的value v-on:input="$emit('input', $event.target.value) //4.监听input,并向父组件返回名为input的事件以及当前DOM的value > ` })
<custom-input v-bind:value="searchText" //1.将searchText绑定到value v-on:input="searchText = $event" //5.接受子组件触发input,将子组件传递的value赋值给searchText ></custom-input>
//以上五点即有效实现了子组件和父组件完全复刻,也就是说v-modle在父组件也可以用了
所以,父组件用更简洁的形式替代如下。
<custom-input v-model="searchText"></custom-input>
使用 slots 进行内容分发
通过 Vue 的 <slot>
自定义元素,可以非常简单的实现这个任务:
Vue.component('alert-box', { template: ` <div class="demo-alert-box"> <strong>Error!</strong> <slot></slot> </div> ` }) <alert-box> 发生一些错误。 </alert-box>
会被渲染为如下结果:
动态组件
通过向 Vue 的 <component>
元素传入 is
特性,
<!-- Component changes when currentTabComponent changes --> <component v-bind:is="currentTabComponent"></component>
通过改变:is的值就可以改变组件的选择。 //话说这难道不是vue-router?
DOM 模板解析注意事项
有些 HTML 元素,例如 <ul>
, <ol>
, <table>
和 <select>
这些元素,会对于出现在其内部的元素有所限制;而另一些 HTML 元素,例如 <li>
, <tr>
和 <option>
这些元素,只可以出现在前面那些元素的内部。
由于这种 HTML 运行机制,在这些受限制的元素内部使用组件,会导致一些问题。
自定义组件 <blog-post-row>
会被当作无效内容,提升到 table 元素之外,从而导致最终渲染输出后的错误。幸运的是,is
特性提供了一种解决方案:
<table> <tr is="blog-post-row"></tr> </table>
应当注意,在使用以下字符串模板之一的场景中,这些限制将不再适用:
字符串模板(例如 template: '...'
)
单文件 (.vue
) 组件
<script type="text/x-template">