版本:3.2.31
Transition 组件的实现原理
Vue.js 3 内建的 Transition 组件可以为单个元素或单个组件添加过渡效果。它的核心实现原理如下:
- 当 DOM 元素被挂载时,将动效附加到该 DOM 元素上;
- 当 DOM 元素被卸载时,不要立即卸载 DOM 元素,而是等到附加到该 DOM 元素上的动效执行完成后再卸载它。
Transition 组件的基本结构
// packages/runtime-core/src/components/BaseTransition.ts
const BaseTransitionImpl: ComponentOptions = {name: `BaseTransition`,props: {mode: String,appear: Boolean,persisted: Boolean,// 省略部分代码},setup(props: BaseTransitionProps, { slots }: SetupContext) { // 省略部分代码}
}
export const BaseTransition = BaseTransitionImpl as any as {new (): {$props: BaseTransitionProps<any>}
}
从上面的代码可以看出,一个组件就是一个选项对象。Transition 组件上有 name、props、setup 等属性。其中 props 中的属性是用户使用 Transition 组件时需要传递给组件的 Props。setup 函数是组件选项,用于配置组合式API。
Transition 组件的 setup 函数
// packages/runtime-core/src/components/BaseTransition.ts
setup(props: BaseTransitionProps, { slots }: SetupContext) {const instance = getCurrentInstance()!const state = useTransitionState()let prevTransitionKey: anyreturn () => {// 通过默认插槽获取需要过渡的元素const children =slots.default && getTransitionRawChildren(slots.default(), true)if (!children || !children.length) {return}// 省略部分代码// at this point children has a guaranteed length of 1.const child = children[0]// 省略部分代码return child}
}
setup 函数是 Vue.js 3 新增的组件选项,它通常会返回一个函数或者返回一个对象。在 Transition 组件中,setup 函数返回的是一个函数,该函数将会直接作为组件的render函数,即该render函数将会渲染添加了过渡效果的元素节点。也就是说,Transition 组件本身不会渲染任何额外的内容,它只是通过默认插槽读取过渡元素,并渲染需要过渡的元素。
给过渡元素添加 transition 钩子函数
在 setup 函数中,我们会看到 resolveTransitionHooks 函数和 setTransitionHooks 函数的调用,如下面的代码所示:
// packages/runtime-core/src/components/BaseTransition.ts
setup(props: BaseTransitionProps, { slots }: SetupContext) {const instance = getCurrentInstance()!const state = useTransitionState()let prevTransitionKey: anyreturn () => {// 省略部分代码// in the case of <transition><keep-alive/></transition>, we need to// compare the type of the kept-alive children.// 获取被 <transition> 组件包裹的 <keep-alive/> 组件。然后需要对它们进行比较const innerChild = getKeepAliveChild(child)if (!innerChild) {return emptyPlaceholder(child)}// 初始化 transition 钩子函数const enterHooks =resolveTran