共计 2371 个字符,预计需要花费 6 分钟才能阅读完成。
1、KeepAlive 组件的实现原理
KeepAlive 的实质是缓存治理,再加上非凡的挂载 / 卸载逻
辑。
首先,KeepAlive 组件的实现须要渲染器层面的反对。这是因为
被 KeepAlive 的组件在卸载时,咱们不能真的将其卸载,否则就无奈
维持组件的以后状态了。正确的做法是,将被 KeepAlive 的组件从原
容器搬运到另外一个暗藏的容器中,实现“假卸载”。当被搬运到隐
藏容器中的组件须要再次被“挂载”时,咱们也不能执行真正的挂载
逻辑,而应该把该组件从暗藏容器中再搬运到原容器。这个过程对应
到组件的生命周期,其实就是 activated 和 deactivated。
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') | |
// KeepAlive 组件的实例上会被增加两个外部函数,别离是 _deActivate 和 _activate | |
// 这两个函数会在渲染器中被调用 | |
instance._deActivate = (vnode) => {move(vnode, storageContainer) | |
} | |
instance._activate = (vnode, container, anchor) => {move(vnode, container, anchor) | |
} | |
return () => { | |
// KeepAlive 的默认插槽就是要被 KeepAlive 的组件 | |
let rawVNode = slots.default() | |
// 如果不是组件,间接渲染即可,因为非组件的虚构节点无奈被 KeepAlive | |
if (typeof rawVNode.type !== 'object') {return rawVNode} | |
// 在挂载时先获取缓存的组件 vnode | |
const cachedVNode = cache.get(rawVNode.type) | |
if (cachedVNode) { | |
// 如果有缓存的内容,则阐明不应该执行挂载,而应该执行激活 | |
// 继承组件实例 | |
rawVNode.component = cachedVNode.component | |
// 在 vnode 上增加 keptAlive 属性,标记为 true,防止渲染器从新挂载它 | |
rawVNode.keptAlive = true | |
} else { | |
// 如果没有缓存,则将其增加到缓存中,这样下次激活组件时就不会执行新的挂载动作了 | |
cache.set(rawVNode.type, rawVNode) | |
} | |
// 在组件 vnode 上增加 shouldKeepAlive 属性,并标记为 true,防止渲染器真的将组件卸载 | |
rawVNode.shouldKeepAlive = true | |
// 将 KeepAlive 组件的实例也增加到 vnode 上,以便在渲染器中拜访 | |
rawVNode.keepAliveInstance = instance | |
// 渲染组件 vnode | |
return rawVNode | |
} | |
} | |
} |
2、异步组件的实现原理
function defineAsyncComponent(options) { | |
// options 能够是配置项,也能够是加载器 | |
if (typeof options === 'function') { | |
// 如果 options 是加载器,则将其格式化为配置项模式 | |
options = {loader: options,}; | |
} | |
const {loader} = options; | |
let InnerComp = null; | |
return { | |
name: 'AsyncComponentWrapper', | |
setup() {const loaded = ref(false); | |
// 代表是否超时,默认为 false,即没有超时 | |
const timeout = ref(false); | |
loader().then((c) => { | |
InnerComp = c; | |
loaded.value = true; | |
}); | |
let timer = null; | |
if (options.timeout) { | |
// 如果指定了超时时长,则开启一个定时器计时 | |
timer = setTimeout(() => { | |
// 超时后将 timeout 设置为 true | |
timeout.value = true; | |
}, options.timeout); | |
} | |
// 包装组件被卸载时革除定时器 | |
onUmounted(() => clearTimeout(timer)); | |
// 占位内容 | |
const placeholder = {type: Text, children: ''}; | |
return () => {if (loaded.value) { | |
// 如果组件异步加载胜利,则渲染被加载的组件 | |
return {type: InnerComp}; | |
} else if (timeout.value) { | |
// 如果加载超时,并且用户指定了 Error 组件,则渲染该组件 | |
return options.errorComponent ? {type: options.errorComponent} : placeholder; | |
} | |
return placeholder; | |
}; | |
}, | |
}; | |
} |
本文来自于 Vue.js 设计与实现电子版的学习
如果有须要,可增加 微信 mini_pear_libb,将会分享电子版
正文完