自我介绍:大家好,我是吉帅振的网络日志(其他平台账号名字相同),互联网前端开发工程师,工作5年,去过上海和北京,经历创业公司,加入过阿里本地生活团队,现在郑州北游教育从事编程培训。
一、前言
多个平行组件条件渲染,当满足条件的时候会触发某个组件的挂载,而已渲染的组件当条件不满足的时候会触发组件的卸载,举个例子:
<comp-a v-if="flag"></comp-a>
<comp-b v-else></comp-b>
<button @click="flag=!flag">toggle</button>
当 flag 为 true 的时候,就会触发组件 A 的渲染,然后我们点击按钮把 flag 修改为 false,又会触发组件 A 的卸载,及组件 B 的渲染。Vue.js 提供了内置组件 KeepAlive,我们可以这么使用它:
<keep-alive>
<comp-a v-if="flag"></comp-a>
<comp-b v-else></comp-b>
<button @click="flag=!flag">toggle</button>
</keep-alive>
编译后的 render 函数:
import { resolveComponent as _resolveComponent, createVNode as _createVNode, createCommentVNode as _createCommentVNode, KeepAlive as _KeepAlive, openBlock as _openBlock, createBlock as _createBlock } from "vue"
export function render(_ctx, _cache, $props, $setup, $data, $options) {
const _component_comp_a = _resolveComponent("comp-a")
const _component_comp_b = _resolveComponent("comp-b")
return (_openBlock(), _createBlock(_KeepAlive, null, [
(_ctx.flag)
? _createVNode(_component_comp_a, { key: 0 })
: _createVNode(_component_comp_b, { key: 1 }),
_createVNode("button", {
onClick: $event => (_ctx.flag=!_ctx.flag)
}, "toggle", 8 /* PROPS */, ["onClick"])
], 1024 /* DYNAMIC_SLOTS */))
}
我们使用了 KeepAlive 组件对这两个组件做了一层封装,KeepAlive 是一个抽象组件,它并不会渲染成一个真实的 DOM,只会渲染内部包裹的子节点,并且让内部的子组件在切换的时候,不会走一整套递归卸载和挂载 DOM的流程,从而优化了性能。
二、KeepAlive 组件的定义
const KeepAliveImpl = {
name: `KeepAlive`,
__isKeepAlive: true,
inheritRef: true,
props: {
include: [String, RegExp, Array],
exclude: [String, RegExp, Array],
max: [String, Number]
},
setup(props, { slots }) {
const cache = new Map()
const keys = new Set()
let current = null
const instance = getCurrentInstance()
const parentSuspense = instance.suspense
const sharedContext = instance.ctx
const { renderer: { p: patch, m: move, um: _unmount, o: { createElement } } } = sharedContext
const storageContainer = createElement('div')
sharedContext.activate = (vnode, container, anchor, isSVG, optimized) => {
const instance = vnode.component
move(vnode, container, anchor, 0 /* ENTER */, parentSuspense)
patch(instance.vnode, vnode, container, anchor, instance, parentSuspense, isSVG, optimized)
queuePostRenderEffect(() => {
instance.isDeactivated = false
if (instance.a) {
invokeArrayFns(instance.a)
}
const vnodeHook = vnode.props && vnode.props.onVnodeMounted
if (vnodeHook) {
invokeVNodeHook(vnodeHook, instance.parent, vnode)
}
}, parentSuspense)
}
sharedContext.deactivate = (vnode) => {
const instance = vnod