组件是可复用的 Vue 实例,且带有一个名字
因为组件是可复用的 Vue 实例,所以它们与 new Vue
接收相同的选项,例如 data
、computed
、watch
、methods
以及生命周期钩子等。
Vue 组件的示例:
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
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>
data必须是一个函数
由于组件是复用的,所有一个组件的 data
选项必须是一个函数,这样确保每次的复用的data都是一个新的实例对象。
data: () => {
return {
count: 0
}
}
如果Vue的data不是一个函数的话,所有的组件共用一个data,只要一处发生了改变,该组件所有的复用都会发生改变。
prop传递数据
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' }
]
}
})
给博文组件传递一个标题,我们可以用一个 props
选项将其包含在该组件可接受的 prop 列表中:
Vue.component('blog-post', {
props: ['title'], ['id'],
template: `
<h3>{{id}} - {{ title }}</h3>
<button>
Enlarge text
</button>
`
})
一个组件默认可以拥有任意数量的 prop,任何值都可以传递给任何 prop。在上述模板中,你会发现我们能够在组件实例中访问这个值,就像访问 data
中的值一样。
可以像这样把数据作为一个自定义 attribute 传递进来, 我们可以使用 v-bind
来动态传递 prop。
<blog-post
v-for="(post, index) in posts"
:key="index"
:id="post.id"
:title="post.title"
></blog-post>
监听子组件事件
点击Enlarge text
按钮时,我们需要告诉父级组件放大所有博文的文本。幸好 Vue 实例提供了一个自定义事件的系统来解决这个问题。父级组件可以像处理 native DOM 事件一样通过 v-on
监听子组件实例的任意事件:
<blog-post
...
v-on:enlarge-text="postFontSize += 0.1"
></blog-post>
子组件可以通过调用内建的方法$emit
并传入事件名称来触发一个事件:
<button v-on:click="$emit('enlarge-text')">
Enlarge text
</button>
有了这个 v-on:enlarge-text="postFontSize += 0.1"
监听器,父级组件就会接收该事件并更新 postFontSize
的值。
事件传参
有的时候用一个事件来抛出一个特定的值是非常有用的。例如我们可能想让 <blog-post>
组件决定它的文本要放大多少。这时可以使用 $emit
的第二个参数来提供这个值:
<button v-on:click="$emit('enlarge-text', 0.1)">
Enlarge text
</button>
当在父级组件监听这个事件的时候,我们可以通过 $event 访问到被抛出的这个值:
<blog-post
...
v-on:enlarge-text="postFontSize += $event"
></blog-post>
如果这个事件处理函数是一个方法:
<blog-post
...
v-on:enlarge-text="onEnlargeText"
></blog-post>
那么这个值将会作为第一个参数传入这个方法:
methods: {
onEnlargeText: function (enlargeAmount) {
this.postFontSize += enlargeAmount
}
}
订阅-发布者
自定义事件只能父子组件通讯,当组件存在其他关系时,这种方式将不便获取数据。这是就需要另外一种方式
配置:
import Vue from "vue"
export const EventBus = new Vue()
发布事件组件
// 引入配置
import { EventBus } from ".../EvenBus"
// 发布函数,携带参数 $emit
let num = parseInt(Math.random() * 10)
EventBus.$emit("newRandom", num)
订阅者
// 引入配置
import { EventBus } from ".../EvenBus"
订阅者通过 $on
订阅函数, 在回调函数中获取数据
EventBus.$on("newRandom", (random) => {
this.randomNUm = random
})
slot插槽
组件中的diy,能够使用组件并且能够自己修改组件中某部分的内容
设置组件中的slot
<template>
<div>
<div class="header">
<h1>头部</h1>
<slot name="header">header</slot>
</div>
<div class="main">
<h1>内容</h1>
<slot :article="article"></slot>
</div>
<div class="footer">
<h1>底部</h1>
<slot name="footer">footer</slot>
</div>
</div>
</template>
<script>
export default {
data() {
return {
article: {
title: "标题",
content: "内容"
}
}
},
};
</script>
展示了不同的slot插槽方式
- 默认插槽
- 具名插槽
使用组件
<Article>
<!-- header的位置 -->
<template #default="{ article }">
<div>
<h2>{{ article.title }}</h2>
<h2>{{ article.content }}</h2>
</div>
</template>
<template #footer>
<div>2021年12月2日20:42:09</div>
</template>
</Article>
通过代码及截图能够看到slot插槽的用法
- 当没有写替换插槽内容时,会默认显示原本slot标签的内容如
name="header"
的slot - 给template模板标签指定
name="footer"
缩写成#footer
,指定替换name是footer的slot标签的内容 - 因为插槽是有作用域的,当使用作为组件时,作用域是在父级中无法获取到自身的data中的数据,可以通过绑定自定义属性进行传输,传输过来是一个对象,对象的属性才是传输的值,因此可以通过解构赋值的形式获取到数据。
动态组件
在一个多标签的界面中使用 is
attribute 来切换不同的组件:
<button @click="show = !show; com = show ? 'va' : 'vb'">hello</button>
<!-- 动态组件 -->
<component :is="com"></component>
<script>
export default {
components: {
'va' : Article,
'vb': imageVue
},
data() {
return {
show: true,
com: 'va'
};
},
};
</script>
当在这些组件之间切换的时候,你有时会想保持这些组件的状态,以避免反复重渲染导致的性能问题。
为了解决这个问题,我们可以用一个 <keep-alive>
元素将其动态组件包裹起来。
<!-- 失活的组件将会被缓存!-->
<keep-alive>
<component v-bind:is="currentTabComponent"></component>
</keep-alive>
异步组件:传送门
过渡动画
Vue 提供了 transition
的封装组件,在下列情形中,可以给任何元素和组件添加进入/离开过渡
- 条件渲染 (使用
v-if
) - 条件展示 (使用
v-show
) - 动态组件
- 组件根节点
Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。包括以下工具:
- 在 CSS 过渡和动画中自动应用 class
- 可以配合使用第三方 CSS 动画库,如 Animate.css
- 在过渡钩子函数中使用 JavaScript 直接操作 DOM
- 可以配合使用第三方 JavaScript 动画库,如 Velocity.js
示例:动态组件切换
<template>
<div id="app">
<button @click="show = !show; com = show ? 'va' : 'vb'">hello</button>
<transition name="fade" mode="out-in">
<!-- 动态组件 -->
<component :is="com"></component>
</transition>
</div>
</template>
<style>
// 组件进入时
.fade-enter-active
{
animation: identifier ease-in forwards 1s;
}
// 组件离开时
.fade-leave-active {
animation: identifier ease-in forwards 1s reverse;
}
@keyframes identifier {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
</style>
当切换组件时,能够看到组件先移出,后面另一个紧接着移入
讲解
过渡的类名
如果你使用一个没有名字的 <transition>
,则 v-
是这些类名的默认前缀。如果你使用了 <transition name="my-transition">
,那么 v-enter
会替换为 my-transition-enter
。
在进入/离开的过渡中,会有 6 个 class 切换。
v-enter
:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。v-enter-active
:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。v-enter-to
:2.1.8 版及以上定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时v-enter
被移除),在过渡/动画完成之后移除。v-leave
:定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。v-leave-active
:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。v-leave-to
:2.1.8 版及以上定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时v-leave
被删除),在过渡/动画完成之后移除。
多元素过渡
当有相同标签名的元素切换时,需要通过 key
attribute 设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容。
<transition>
<button v-if="isEditing" key="save">
Save
</button>
<button v-else key="edit">
Edit
</button>
</transition>
过渡模式
生效的进入和离开的过渡不能满足所有要求,所以 Vue 提供了过渡模式
in-out
:新元素先进行过渡,完成之后当前元素过渡离开。out-in
:当前元素先进行过渡,完成之后新元素过渡进入。