在这些情形中,可以给我们任何元素和组件添加过渡效果:条件渲染,即使用 v-if、条件展示,即使用 v-show、动态组件、组件根节点。
先来看下 transition 组件的实现:transition 也是抽象组件,但它是 web 平台独有的,因此在 web 路径中将其注册到全局中;它也有独有的 render 函数,并且也利用了默认插槽,看下 render 函数实现逻辑:
1、通过默认插槽获取到子节点,如果没有子节点直接 return;
2、过滤掉文本节点或者空白文本节点;
3、如果有多个子节点会报警告:transition 只能包裹一个子节点,如果有多个子节点用 transition-group 包裹;
4、对 mode 属性进行判断;
5、拿第一个子节点;
6、如果 transition 组件作为组件根节点,然后其父占位符节点又被 transiiton 包裹,则当前的 transition 组件被跳过;
7、找到非抽象节点的 vnode;
8、构造 child 的 key;
9、通过 transition 组件节点上的属性和事件提取数据,赋值给 child.data.transition 属性中;
10、返回第一个子节点的 vnode;
以下面代码为例:
let vm = new Vue({
el: '#app',
template: '<div id="demo">' +
'<button v-on:click="show = !show">' +
'Toggle' +
'</button>' +
'<transition :appear="true" name="fade">' +
'<p v-if="show">hello</p>' +
'</transition>' +
'</div>',
data() {
return {
show: true
}
}
})
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
得到的 child.data 如下:
{
transition: {
appear: true,
name: 'fade'
}
}
管理和控制过渡动画流程的代码写在 web 平台的 module 中,在 patch 过程中会执行所有 module 钩子,跟 transition 相关的 module 钩子有三个,create 和 activate 钩子执行时会进入 entering 动画,在 remove 钩子执行时会进入到 leaving 动画。
entering 动画的核心逻辑在 enter 函数中:
依然在 enter 函数中,以下是执行的逻辑:
对于过渡类名方面,startClass
定义进入过渡的开始状态,在元素被插入时生效,在下一个帧移除;activeClass
定义过渡的状态,在元素整个过渡过程中作用,在元素被插入时生效,在 transition/animation
完成之后移除;toClass
定义进入过渡的结束状态,在元素被插入一帧后生效 (与此同时 startClass
被删除),在 <transition>/animation
完成之后移除。
对于过渡钩子函数方面,beforeEnterHook
是过渡开始前执行的钩子函数,enterHook
是在元素插入后或者是 v-show
显示切换后执行的钩子函数。afterEnterHook
是在过渡动画执行完后的钩子函数。
cb 作为过渡动画结束的回调函数的逻辑:
cb.cancelled 为 true 的情况是在没有执行 cb 时,即 el._enterCb 或者 el._leaveCb 为真,然后进入到 leaving 或者 enter 动画时。 即一个过渡动画还没有完整显示/隐藏,在 enterHook 或者 leaveHook 没有传入 done 参数时,即没有立即告诉 Vue 过渡动画结束,那么就会默认等到动画的完成时间,在这个时间内又手动点击按钮将其隐藏/显示,就会让 cb.cancelled 为 true,那么 enterCancelled 或者 leaveCancelled 事件就会被执行。
总结:Transition 组件是一个内置抽象组件,它的实现通过自定义 render 函数并且利用了插槽; 真正执行动画的是我们写的 CSS 或者 JS 钩子函数,而 transition 只是帮我们很好的管理 CSS 添加/删除,以及钩子函数的执行时机。