内建组件和模块主要包括KeepAlive组件、Teleport组件、Transition组件等
1、KeepAlive组件的实现原理
1.组件的激活与失活
KeepAlive组件可以避免频繁的创建、挂载、卸载组件的这一系列操作,如常用的Tab组件需要反复切换。
原理:卸载时,被KeepAlive的组件移动到隐藏的容器中,再次挂载时,把一次容器的组件放回原容器中。如图所示
初步实现如下所示
const KeepAlive = {
//KeepAlive标识
__isKeepAlive: true,
setup(props, {slots}) {
// 创建一个缓存对象
// key: vnode.type
// value: vnode
const cache = new Map()
// 当前 KeepAlive组件实例
const instance = currentInstance
// KeepAlive组件, 实例上存在KeepAliveCtx
// 该对象会暴露渲染器部分内部方法,其中 move函数移动DOM到另一个容器中
const {move, createElement} = instance.keepAliveCtx
// 创建隐藏容器
const storageContainer = createElement('div')
// _deActivate和_activate
instance._deActivate = (vnode) => {
move(vnode, storageContainer)
}
instance._activate = (vnode, container, anchor) => {
move(vnode, container, anchor)
}
return () => {
//被KeepAlive的组件‘
let rawVNode = slots.default()
// 如果不是组件直接渲染
if (typeof rawVNode.type !== 'object') {
return rawVNode
}
//在挂载时先获取缓存的组件vnode
const cachedVNode = cache.get(rawVNode.type)
if (cachedVNode) {
// 如果有缓存的内容,在执行激活操作
//继承组件实例
rawVNode.component = cachedVNode.component
// 标记true
rawVNode.keptAlive = true
} else {
cache.set(rawVNode.type, rawVNode)
}
rawVNode.shouldKeepAlive = true
rawVNode.keepAliveInstance = instance
return rawVNode
}
}
}
2.include和exclude
默认情况下,KeepAlive组件会缓存所有内部组件,但会出现不希望缓存所有内部组件,通过include来配置需要缓存的组件,通过exclude配置不缓存的组件
3.缓存管理
KeepAlive的缓存是通过一个Map对象进行实现。
综合上面两点的最终版本代码如下
const cache = new Map()
const KeepAlive = {
// 标识
__isKeepAlive: true,
props: {
include: RegExp,
exclude: RegExp
},
setup(props, { slots }) {
// 创建一个缓存对象
const instance = currentInstance
const { move, createElement } = instance.keepAliveCtx
const storageContainer = createElement('div')
instance._deActivate = (vnode) => {
move(vnode, storageContainer)
}
instance._activate = (vnode, container, anchor) => {
move(vnode, container, anchor)
}
return () => {
let rawVNode = slots.default()
if (typeof rawVNode.type !== 'object') {
return rawVNode
}
const name = rawVNode.type.name
if (
name &&
(
(props.include && !props.include.test(name)) ||
(props.exclude && props.exclude.test(name))
)
) {
return rawVNode
}
const cachedVNode = cache.get(rawVNode.type)
if (cachedVNode) {
rawVNode.component = cachedVNode.component
rawVNode.keptAlive = true
} else {
cache.set(rawVNode.type, rawVNode)
}
rawVNode.shouldKeepAlive = true
rawVNode.keepAliveInstance = instance
return rawVNode
}
}
}
2、Teleport组件的实现原理
1.Teleport组件解决的问题
主要用于:将指定内容渲染到特定的容器中
2.实现Teleport组件
Teleport组件需要有两个特殊选项__isTelsport和process
标识和进程,代码如下
const Teleport = {
__isTeleport: true,
process(n1, n2, container, anchor, internals) {
const { patch, patchChildren, move } = internals
if (!n1) {
// 挂载
const target = typeof n2.props.to === 'string'
? document.querySelector(n2.props.to)
: n2.props.to
n2.children.forEach(c => patch(null, c, target, anchor))
} else {
// 更新
patchChildren(n1, n2, container)
if (n2.props.to !== n1.props.to) {
const newTarget = typeof n2.props.to === 'string'
? document.querySelector(n2.props.to)
: n2.props.to
n2.children.forEach(c => move(c, newTarget))
}
}
}
}
3、Transition组件的实现原理
核心原理:
1.当DOM元素被挂载时,动效附加到该DOM元素上
2.当DOM元素被卸载时,不立即卸载DOM元素,而是等到附加到该DOM元素上的动效执行完成后再卸载
实现代码如下
const Transition = {
name: 'Transition',
setup(props, { slots }) {
return () => {
const innerVNode = slots.default()
innerVNode.transition = {
beforeEnter(el) {
el.classList.add('enter-from')
el.classList.add('enter-active')
},
enter(el) {
nextFrame(() => {
el.classList.remove('enter-from')
el.classList.add('enter-to')
el.addEventListener('transitionend', () => {
el.classList.remove('enter-to')
el.classList.remove('enter-active')
})
})
},
leave(el, performRemove) {
el.classList.add('leave-from')
el.classList.add('leave-active')
// document.body.offsetHeight
nextFrame(() => {
el.classList.remove('leave-from')
el.classList.add('leave-to')
el.addEventListener('transitionend', () => {
el.classList.remove('leave-to')
el.classList.remove('leave-active')
performRemove()
})
})
}
}
return innerVNode
}
}
}
总结
学习了三个内置组件
1、KeepAlive:避免组件的重复使用而导致的频繁挂载、卸载操作,可以标记需要和不需要缓存的组件
2、Teleport:把组件渲染到指定容器,可以作为渲染器的一部分存在
3、Transition:动效处理为原生DOM添加动效过渡