组件:组件是可复用的 Vue 实例,所以它们与 new Vue 接收相同的选项,例如 data、computed、watch、methods 以及生命周期钩子等。仅有的例外是像 el 这样根实例特有的选项。
为了能在模板中使用,这些组件必须先注册以便 Vue 能够识别。这里有两种组件的注册类型:全局注册和局部注册。
全局注册:
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', { //全局注册一个组件,第一个参数为组件名称
data: function () { //data必须为一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
<div id="components-demo">
<button-counter></button-counter> //可以将组件进行任意次数的复用
<button-counter></button-counter>
<button-counter></button-counter>
</div>
new Vue({ el: '#components-demo' })
局部注册:
var ComponentA = { /* ... */ }
var ComponentB = { /* ... */ }
var ComponentC = { /* ... */ }
//然后在 components 选项中定义你想要使用的组件:
new Vue({
el: '#app',
components: {
'component-a': ComponentA,
'component-b': ComponentB
}
})
父组件向子组件传值: 通过v-bind属性绑定
Vue.component('blog-post', {
props: ['title'], //Prop 是你可以在组件上注册的一些自定义 attribute
template: '<h3>{{ title }}</h3>'
})
new Vue({
el: '#blog-post-demo',
data: {
posts: [
{ id: 1, title: 'My journey with Vue' },
{ id: 2, title: 'Blogging with Vue' },
{ id: 3, title: 'Why Vue is so fun' }
]
}
})
<blog-post
v-for="post in posts"
v-bind:key="post.id"
v-bind:title="post.title"
></blog-post>
子组件向父组件传值: 通过v-on事件绑定
new Vue({
el: '#blog-demo',
data: {
posts: [/* ... */],
postFontSize: 1 //本例:点击子组件按钮,放大父组件postFontSize值
}
})
<div id="blog-demo">
<div :style="{ fontSize: postFontSize + 'em' }">
<blog-post v-for="post in posts" :key="post.id" :post="post" @enlarge-text="postFontSize += 0.1"></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')"> //通过$emit()方法并传入事件名称来触发一个事件
Enlarge text
</button>
<div v-html="post.content"></div>
</div>
`
})
组件插槽:
Vue.component('alert-box', {
template: `
<div class="demo-alert-box">
<strong>Error!</strong>
<slot></slot> //使用了插槽
</div>
`
})
<alert-box>
Something bad happened.
</alert-box>
插槽内可以包含任何模板代码,包括 HTML,甚至其它的组件,如果 template 中没有包含一个 元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
该插槽跟模板的其它地方一样可以访问相同的实例 property (也就是相同的“作用域”),而不能访问 的作用域
<navigation-link url="/profile">
Logged in as {{ user.name }}---{{url}} //可以访问user.name 不能访问url,因为url是传递给组件的,而不是在组件内部定义的
</navigation-link>
后备内容:
//在一个 <submit-button> 组件中:
<button type="submit">
<slot>Submit</slot> //submit是后备内容
</button>
//调用该组件
<submit-button></submit-button>
//后备内容“Submit”将会被渲染:
<button type="submit">
Submit
</button>
//但是如果我们提供内容:
<submit-button>
Save
</submit-button>
//则这个提供的内容将会被渲染从而取代后备内容:
<button type="submit">
Save
</button>
具名插槽:
<div class="container">
<header>
<slot name="header"></slot> //插槽名字是header
</header>
<main>
<slot></slot> //插槽默认名称为default
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
<base-layout>
<template v-slot:header> //使用 v-slot 指令,并以 v-slot 的参数的形式提供其名称
<h1>Here might be a page title</h1>
</template>
<p>A paragraph for the main content.</p> //没有被包裹在带有 v-slot 的 <template> 中的内容都会被视为默认插槽的内容
<p>And another one.</p>
<template #footer> //v-slot: 可以缩写成#,该缩写只在其有参数的时候才可用
<p>Here's some contact info</p>
</template>
</base-layout>
//任何一种写法都会渲染出:
<div class="container">
<header>
<h1>Here might be a page title</h1>
</header>
<main>
<p>A paragraph for the main content.</p>
<p>And another one.</p>
</main>
<footer>
<p>Here's some contact info</p>
</footer>
</div>
作用域插槽:让插槽内容能够访问子组件中才有的数据
<span>
<slot v-bind:user="user"> //将 user 作为 <slot> 元素的一个 attribute 绑定上去
{{ user.lastName }}
</slot>
</span>
<current-user>
<template v-slot:default="slotProps"> //在父级作用域中,我们可以使用带值的 v-slot 来定义我们提供的插槽 prop 的名字:
{{ slotProps.user.firstName }}
</template>
</current-user>
//不带参数的 v-slot 被假定对应默认插槽,即可以写成下面:
<current-user v-slot="slotProps">
{{ slotProps.user.firstName }}
</current-user>
动态组件:
<component v-bind:is="currentTabComponent"></component>
//在上述示例中,currentTabComponent 可以包括已注册组件的名字,或一个组件的选项对象
prop:
<!-- 给prop传入一个静态的值 -->
<blog-post title="My journey with Vue"></blog-post>
<!-- 动态赋予一个变量的值 -->
<blog-post v-bind:title="post.title"></blog-post>
<!-- 动态赋予一个复杂表达式的值 -->
<blog-post v-bind:title="post.title + ' by ' + post.author.name"></blog-post>
<!-- 即便 `42` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<blog-post v-bind:likes="42"></blog-post>
<!-- 即便 `false` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<blog-post v-bind:is-published="false"></blog-post>
<!-- 即便数组是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<blog-post v-bind:comment-ids="[234, 266, 273]"></blog-post>
<!-- 即便对象是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<blog-post v-bind:author="{ name: 'Veronica', company: 'Veridian Dynamics'}"></blog-post>
自定义事件: 事件名称使用短横线格式