Ein Artikel über die KeepAlive-Komponente in Vue
Kürzlich habe ich über Vue-bezogene Wissenspunkte gelesen. Als ich die KeepAlive-Komponente sah, war ich neugierig, dass sie beim Wechseln zwischen Komponenten nicht neu gerendert wird, also habe ich mir das genauer angesehen. (Lernvideo-Sharing: vue-Video-Tutorial)
Wenn Sie auch daran interessiert sind, zu erfahren, wie die konkrete interne Umsetzung umgesetzt wird oder Sie ein gewisses Verständnis haben, sich aber nicht ausreichend auskennen, dann können Sie es auch gemeinsam festigen
Tipps: Interview wie dieses Dann können Sie andere lautstark nach diesem Wissenspunkt fragen?
Was ist KeepAlive? KeepAlive-Funktion ist dem HTTP-Protokoll entlehnt. Im HTTP-Protokoll wird KeepAlive auch als dauerhafte Verbindung bezeichnet. Seine Funktion besteht darin, mehrere Anfragen/Antworten zu ermöglichen Verwenden Sie dieselbe HTTP-Verbindung, wodurch der zusätzliche Leistungsaufwand behoben wird, der durch die häufige Zerstörung und Erstellung von HTTP-Verbindungen entsteht. Auf die gleiche Weise ist die KeepAlive-Komponente in Vue auch darauf ausgelegt, zu verhindern, dass eine Komponente häufig zerstört/neu erstellt wird, und so Leistungseinbußen zu vermeiden.
// App.vue <Test :msg="curTab" v-if="curTab === 'Test'"></Test> <HelloWorld :msg="curTab" v-if="curTab === 'HelloWorld'"></HelloWorld> <div @click="toggle">toggle</div>
<KeepAlive>
是一个内置组件,它的功能是在多个组件间动态切换时缓存被移除的组件实例。KeepAlive 功能
KeepAlive 一词借鉴于 HTTP 协议,在 HTTP 协议里面 KeepAlive 又称持久连接,作用是允许多个请求/响应共用同一个 HTTP 连接,解决了频繁的销毁和创建 HTTP 连接带来的额外性能开销。而同理 Vue 里的 KeepAlive 组件也是为了避免一个组件被频繁的销毁/重建,避免了性能上的开销。
<KeepAlive> <component :is="curTab === 'Test' ? Test : HelloWorld" :msg="curTab"></component> </KeepAlive> <div @click="toggle">toggle</div>
上述代码可以看到,如果我们频繁点击 toggle 时会频繁的渲染 Test/HelloWorld 组件,当用户频繁的点击时 Test 组件需要频繁的销毁/渲染,这就造成很大的渲染性能损失。
所以为了解决这种性能开销,你需要知道是时候使用 KeepAlive 组件。
const KeepAliveImpl: ComponentOptions = { name: "KeepAlive", // 标识这是一个 KeepAlive 组件 __isKeepAlive: true, // props props: { exclude: [String, Array, RegExp], include: [String, Array, RegExp], max: [String, Number] } } // isKeepAlive export const isKeepAlive = (vnode: VNode): boolean => (vnode.type as any).__isKeepAlive
可以看这个录屏,在首次加载后再次频繁的切换并没有重新销毁与挂载,而仅仅是将组件进行了失活(而不是销毁),渲染时只需要重新激活就可以,而不需重新挂载,如果要渲染的组件很大,那就能有不错的性能优化。
想要体验的话可以去看看这个例子?官方demo,其中数据会被缓存这个也需要在开发使用中去注意到的
如何实现
实现原理其实很简单,其实就是缓存管理和特定的销毁和渲染逻辑,使得它不同于其他组件。
KeepAlive 组件在卸载组件时并不能真的将其卸载,而是将其放到一个隐藏的容器里面,当被激活时再从隐藏的容器中拿出来挂载到真正的 dom 上就行,这也就对应了 KeepAlive 的两个独特的生命周期activated
和deactivated
。
先来简单了解下组件的挂载过程
所以在 KeepAlive 内的子组件在 mount 和 unmount 的时候会执行特定的渲染逻辑,从而不会去走挂载和销毁逻辑
具体实现(实现一个小而简单的 KeepAlive)
KeepAlive 组件的属性
// setup 接着上面的代码 // 获取到当前 KeepAlive 组件实例 const instance = getCurrentInstance()! as any; // 拿到 ctx const sharedContext = instance.ctx as KeepAliveContext; // cache 缓存 // key: vnode.key | vnode.type value: vnode const cache: Cache = new Map() // 需要拿到某些的 renderer 操作函数,需要自己特定执行渲染和卸载逻辑 const { renderer: { p: patch, m: move, um: _unmount, o: { createElement } } } = sharedContext // 隐藏的容器,用来存储需要隐藏的 dom const storeageContainer = createElement('div') // 存储当前的子组件的缓存 key let pendingKey: CacheKey | null = null sharedContext.activate = (vnode, container, anchor) => { // KeepAlive 下组件激活时执行的 move 逻辑 move(vnode, container, anchor, 0 /* ENTER */) } sharedContext.deactivate = (vnode) => { // KeepAlive 下组件失活时执行的 move 逻辑 move(vnode, storeageContainer, null, 1 /* LEAVE */) } return () => { // 没有子组件 if (!slots.default) { return null; } const children = slots.default() as VNode[]; const rawNode = children[0]; let vnode = rawNode; const comp = vnode.type as ConcreteComponent; const name = comp.displayName || comp.name const { include, exclude } = props; // 没有命中的情况 if ( (include && (!name || !matches(include, name))) || (exclude && name && matches(exclude, name)) ) { // 直接渲染子组件 return rawNode; } // 获取子组件的 vnode key const key = vnode.key == null ? comp : vnode.key; // 获取子组件缓存的 vnode const cachedVNode = cache.get(key); pendingKey = key; // 命中缓存 if (cachedVNode) { vnode.el = cachedVNode.el; // 继承组件实例 vnode.component = cachedVNode.component; // 在 vnode 上更新 shapeFlag,标记为 COMPONENT_KEPT_ALIVE 属性,防止渲染器重新挂载 vnode.shapeFlag |= ShapeFlags.COMPONENT_KEPT_ALIVE } else { // 没命中将其缓存 cache.set(pendingKey, vnode) } // 在 vnode 上更新 shapeFlag,标记为 COMPONENT_SHOULD_KEEP_ALIVE 属性,防止渲染器将组件卸载了 vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE // 渲染组件 vnode return vnode; }
KeepAlive 组件的 setup 逻辑以及渲染逻辑(重点看)
function mountComponent() { // ... if (isKeepAlive(initialVNode)) { ;(instance.ctx as KeepAliveContext).renderer = internals } }
KeepAlive组件 mount 时挂载 renderer 到 ctx 上
在 KeepAlive 组件内会从 sharedContext 上的 renderer 上拿到一些方法比如 move、createElement 等
const processComponent = ( n1: VNode | null, n2: VNode, container: RendererElement, anchor: RendererNode | null, ) => { if (n1 == null) { // 在 KeepAlive 组件渲染时会对子组件增加 COMPONENT_KEPT_ALIVE 标志 // 挂载子组件时会判断是否 COMPONENT_KEPT_ALIVE ,如果是不会调用 mountComponent 而是直接执行 activate 方法 if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) { ;(parentComponent!.ctx as KeepAliveContext).activate( n2, container, anchor ) } // ... } }
子组件执行特定的销毁和渲染逻辑
首先从上面可以看到,在渲染 KeepAlive 组件时会对其子组件的 vnode 上增加对应的 shapeFlag 标志
比如COMPONENT_KEPT_ALIVE
标志,组件挂载的时候告诉渲染器这个不需要 mount 而需要特殊处理
const unmount: UnmountFn = (vnode) => { // ... // 在 KeepAlive 组件渲染时会对子组件增加 COMPONENT_SHOULD_KEEP_ALIVE 标志 // 然后在子组件卸载时并不会真实的卸载而是调用 KeepAlive 的 deactivate 方法 if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) { ;(parentComponent!.ctx as KeepAliveContext).deactivate(vnode) return } }
同理COMPONENT_SHOULD_KEEP_ALIVE
标志也是用来在组件卸载的时候告诉渲染器这个不需要 unmount 而需要特殊处理。
export function onActivated( hook: Function, target?: ComponentInternalInstance | null ) { // 注册 activated 的回调函数到当前的 instance 的钩子函数上 registerKeepAliveHook(hook, LifecycleHooks.ACTIVATED, target) } export function onDeactivated( hook: Function, target?: ComponentInternalInstance | null ) { // 注册 deactivated 的回调函数到当前的 instance 的钩子函数上 registerKeepAliveHook(hook, LifecycleHooks.DEACTIVATED, target) }
如何挂载
activated
和deactivated
Sie können dem obigen Code entnehmen, dass die Test-/HelloWorld-Komponente häufig gerendert wird, wenn wir häufig auf „Umschalten“ klicken. Wenn der Benutzer häufig klickt, muss die Testkomponente häufig zerstört/gerendert werden, was zu großen Verlusten führt bei der Rendering-Leistung. Um diesen Leistungsaufwand zu beheben, müssen Sie wissen, dass es an der Zeit ist, die KeepAlive-Komponente zu verwenden.
// renderer.ts // mount 函数逻辑 const mountComponent = (initialVNode, container, anchor, parentComponent, parentSuspense, isSVG, optimized ) => { // ... const instance: ComponentInternalInstance = compatMountInstance || (initialVNode.component = createComponentInstance( initialVNode, parentComponent, parentSuspense )) // 执行 setup setupComponent(instance) } // setupcomponent 处理 setup 函数值 export function setupComponent( instance: ComponentInternalInstance, isSSR = false ) { // ... const isStateful = isStatefulComponent(instance) // ... const setupResult = isStateful // setupStatefulComponent 函数主要功能是设置当前的 instance ? setupStatefulComponent(instance, isSSR) : undefined // ... } function setupStatefulComponent( instance: ComponentInternalInstance ){ if (setup) { //设置当前实例 setCurrentInstance(instance) // 执行组件内 setup 函数,执行 onActivated 钩子函数进行回调函数收集 const setupResult = callWithErrorHandling( setup, instance, ErrorCodes.SETUP_FUNCTION, [__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext] ) // currentInstance = null; unsetCurrentInstance() } }
🎜Wie man es implementiert🎜🎜🎜Das Implementierungsprinzip ist eigentlich sehr einfach. Es handelt sich um „Cache-Verwaltung und spezifische Zerstörungs- und Rendering-Logik“, wodurch es sich von anderen Komponenten unterscheidet. 🎜🎜KeepAlive-Komponente kann beim 🎜Deinstallieren der Komponente nicht wirklich deinstalliert werden🎜 Bei Aktivierung wird sie aus dem versteckten Container entnommen und auf dem echten Dom montiert 🎜, das entspricht auch zu den beiden einzigartigen Lebenszyklen von KeepAlive aktiviert
und deaktiviert
. 🎜🎜
🎜🎜Lassen Sie uns zunächst kurz den Komponentenmontageprozess verstehen🎜🎜🎜
Die 🎜-Unterkomponente in KeepAlive führt also beim Mounten und Unmounten eine bestimmte Rendering-Logik aus, sodass die Mount- und Zerstörungslogik nicht ausgeführt wird 🎜🎜🎜Konkrete Implementierung (Implementierung eines kleinen und einfachen KeepAlive)🎜🎜KeepAlive-Komponenteneigenschaften🎜🎜🎜sharedContext.activate = (vnode, container, anchor) => {
// KeepAlive 下组件激活时执行的 move 逻辑
move(vnode, container, anchor, 0 /* ENTER */)
// 把回调推入到 postFlush 的异步任务队列中去执行
queuePostRenderEffect(() => {
if (instance.a) {
// a是 activated 钩子的简称
invokeArrayFns(instance.a)
}
})
}
sharedContext.activate = (vnode, container, anchor) => {
// KeepAlive 下组件失活时执行的 move 逻辑
move(vnode, container, anchor, 0 /* ENTER */)
queuePostRenderEffect(() => {
if (instance.da) {
// da是 deactivated 钩子的简称
invokeArrayFns(instance.da)
}
})
}
export const enum LifecycleHooks {
// ... 其他生命周期声明
DEACTIVATED = 'da',
ACTIVATED = 'a',
}
export interface ComponentInternalInstance {
// ... 其他生命周期
[LifecycleHooks.ACTIVATED]: Function[]
[LifecycleHooks.DEACTIVATED]: Function[]
}
Nach dem Login kopierenNach dem Login kopierenKeepAlive-Komponenten-Setup-Logik und Rendering-Logik (Fokus)🎜 🎜🎜sharedContext.activate = (vnode, container, anchor) => {
// instance 是子组件实例
const instance = vnode.component!
// ...
// dev环境下设置, 自己模拟写的
devtools.emit('component:added', instance.appContext.app, instance.uid, instance.parent ? instance.parent.uid: undefined, instance)
// 官方添加
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
// Update components tree
devtoolsComponentAdded(instance)
}
}
// 同理 sharedContext.deactivates 上也要添加,不然不会显示在组件树上
Nach dem Login kopierenNach dem Login kopierenMounten Sie den Renderer beim Mounten auf ctx 🎜🎜🎜🎜 in der KeepAlive-Komponente Die KeepAlive-Komponente ruft einige Methoden vom Renderer auf sharedContext ab, z. B. move, createElement usw. 🎜sharedContext.activate = (vnode, container, anchor) => {
// ...
// props 改变需要重新 patch(update)
patch(
instance.vnode,
vnode,
container,
anchor,
instance,
parentSuspense,
isSVG,
vnode.slotScopeIds,
optimized
)
}
Nach dem Login kopierenNach dem Login kopierenDie Unterkomponente führt eine spezifische Zerstörungs- und Rendering-Logik aus🎜🎜🎜🎜Wie Sie oben sehen können, wird beim Rendern der KeepAlive-Komponente zunächst das entsprechende ShapeFlag-Flag zum Vnode der Unterkomponente hinzugefügt🎜🎜🎜 Beispielsweise , teilt das COMPONENT_KEPT_ALIVE
-Flag dem Renderer beim Mounten der Komponente mit, dass sie nicht gemountet werden muss, sondern eine spezielle Verarbeitung erfordert Komponente, wenn sie deinstalliert wird. Der Renderer muss nicht ausgehängt werden, sondern erfordert eine spezielle Verarbeitung. 🎜rrreeeSo mounten Sie aktiviert
und deaktiviert</ Code> Lebenszyklus (Sie müssen sich nicht auf den Lebenszyklus konzentrieren) 🎜🎜🎜🎜Zunächst werden diese beiden Lebenszyklen in der KeepAlive-Komponente eindeutig deklariert und direkt exportiert und verwendet. 🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>export function onActivated(
hook: Function,
target?: ComponentInternalInstance | null
) {
// 注册 activated 的回调函数到当前的 instance 的钩子函数上
registerKeepAliveHook(hook, LifecycleHooks.ACTIVATED, target)
}
export function onDeactivated(
hook: Function,
target?: ComponentInternalInstance | null
) {
// 注册 deactivated 的回调函数到当前的 instance 的钩子函数上
registerKeepAliveHook(hook, LifecycleHooks.DEACTIVATED, target)
}</pre><div class="contentsignin">Nach dem Login kopieren</div></div><div class="contentsignin">Nach dem Login kopieren</div></div><p>然后因为这两个生命周期会注册在 setup 里面,所以只要执行 setup 就会将两个生命周期的回调函数注册到当前的 instance 实例上</p><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>// renderer.ts
// mount 函数逻辑
const mountComponent = (initialVNode,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
) => {
// ...
const instance: ComponentInternalInstance =
compatMountInstance ||
(initialVNode.component = createComponentInstance(
initialVNode,
parentComponent,
parentSuspense
))
// 执行 setup
setupComponent(instance)
}
// setupcomponent 处理 setup 函数值
export function setupComponent(
instance: ComponentInternalInstance,
isSSR = false
) {
// ...
const isStateful = isStatefulComponent(instance)
// ...
const setupResult = isStateful
// setupStatefulComponent 函数主要功能是设置当前的 instance
? setupStatefulComponent(instance, isSSR)
: undefined
// ...
}
function setupStatefulComponent(
instance: ComponentInternalInstance
){
if (setup) {
//设置当前实例
setCurrentInstance(instance)
// 执行组件内 setup 函数,执行 onActivated 钩子函数进行回调函数收集
const setupResult = callWithErrorHandling(
setup,
instance,
ErrorCodes.SETUP_FUNCTION,
[__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
)
// currentInstance = null;
unsetCurrentInstance()
}
}</pre><div class="contentsignin">Nach dem Login kopieren</div></div><div class="contentsignin">Nach dem Login kopieren</div></div><p>最后在执行<code>sharedContext.activate
和sharedContext.deactivate
的时候将注册在实例上的回调函数取出来直接执行就OK了,执行时机在 postRender 之后sharedContext.activate = (vnode, container, anchor) => {
// KeepAlive 下组件激活时执行的 move 逻辑
move(vnode, container, anchor, 0 /* ENTER */)
// 把回调推入到 postFlush 的异步任务队列中去执行
queuePostRenderEffect(() => {
if (instance.a) {
// a是 activated 钩子的简称
invokeArrayFns(instance.a)
}
})
}
sharedContext.activate = (vnode, container, anchor) => {
// KeepAlive 下组件失活时执行的 move 逻辑
move(vnode, container, anchor, 0 /* ENTER */)
queuePostRenderEffect(() => {
if (instance.da) {
// da是 deactivated 钩子的简称
invokeArrayFns(instance.da)
}
})
}
export const enum LifecycleHooks {
// ... 其他生命周期声明
DEACTIVATED = 'da',
ACTIVATED = 'a',
}
export interface ComponentInternalInstance {
// ... 其他生命周期
[LifecycleHooks.ACTIVATED]: Function[]
[LifecycleHooks.DEACTIVATED]: Function[]
}
Nach dem Login kopierenNach dem Login kopieren
以下是关于上述demo如何实现的简化流程图

需要注意的知识点
1、什么时候缓存
KeepAlive 组件的onMounted
和onUpdated
生命周期时进行缓存
2、什么时候取消缓存
缓存数量超过设置的 max 时
- 监听 include 和 exclude 修改的时候,会读取缓存中的知进行判断是否需要清除缓存
修剪缓存的时候也要 unmount(如果该缓存不是当前组件)或者 resetShapeFlag 将标志为从 KeepAlive 相关 shapeFlag 状态重置为 STATEFUL_COMPONENT 状态(如果该缓存是当前组件,但是被exclude了),当然 unmount 函数内包含 resetShapeFlag 操作
3、缓存策略
KeepAlive 组件的缓存策略是 LRU(last recently used)缓存策略
核心思想在于需要把当前访问或渲染的组件作为最新一次渲染的组件,并且该组件在缓存修剪过程中始终是安全的,即不会被修剪。
看下面的图更加直观,图片来源一篇讲keepAlive 缓存优化的文章

4、如何添加到 vue devtools 组件树上
sharedContext.activate = (vnode, container, anchor) => {
// instance 是子组件实例
const instance = vnode.component!
// ...
// dev环境下设置, 自己模拟写的
devtools.emit('component:added', instance.appContext.app, instance.uid, instance.parent ? instance.parent.uid: undefined, instance)
// 官方添加
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
// Update components tree
devtoolsComponentAdded(instance)
}
}
// 同理 sharedContext.deactivates 上也要添加,不然不会显示在组件树上
Nach dem Login kopierenNach dem Login kopieren5、缓存的子组件 props 更新处理
当子组件有 prop 更新时是需要重新去 patch 的,所以在 activate 的时候需要重新执行 patch 进行子组件更新
sharedContext.activate = (vnode, container, anchor) => {
// ...
// props 改变需要重新 patch(update)
patch(
instance.vnode,
vnode,
container,
anchor,
instance,
parentSuspense,
isSVG,
vnode.slotScopeIds,
optimized
)
}
Nach dem Login kopierenNach dem Login kopieren
Das obige ist der detaillierte Inhalt vonEin Artikel über die KeepAlive-Komponente in Vue. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

KeepAlive-Komponenteneigenschaften🎜🎜🎜
sharedContext.activate = (vnode, container, anchor) => { // KeepAlive 下组件激活时执行的 move 逻辑 move(vnode, container, anchor, 0 /* ENTER */) // 把回调推入到 postFlush 的异步任务队列中去执行 queuePostRenderEffect(() => { if (instance.a) { // a是 activated 钩子的简称 invokeArrayFns(instance.a) } }) } sharedContext.activate = (vnode, container, anchor) => { // KeepAlive 下组件失活时执行的 move 逻辑 move(vnode, container, anchor, 0 /* ENTER */) queuePostRenderEffect(() => { if (instance.da) { // da是 deactivated 钩子的简称 invokeArrayFns(instance.da) } }) } export const enum LifecycleHooks { // ... 其他生命周期声明 DEACTIVATED = 'da', ACTIVATED = 'a', } export interface ComponentInternalInstance { // ... 其他生命周期 [LifecycleHooks.ACTIVATED]: Function[] [LifecycleHooks.DEACTIVATED]: Function[] }
Nach dem Login kopierenNach dem Login kopierenKeepAlive-Komponenten-Setup-Logik und Rendering-Logik (Fokus)🎜 🎜🎜
sharedContext.activate = (vnode, container, anchor) => { // instance 是子组件实例 const instance = vnode.component! // ... // dev环境下设置, 自己模拟写的 devtools.emit('component:added', instance.appContext.app, instance.uid, instance.parent ? instance.parent.uid: undefined, instance) // 官方添加 if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { // Update components tree devtoolsComponentAdded(instance) } } // 同理 sharedContext.deactivates 上也要添加,不然不会显示在组件树上
Nach dem Login kopierenNach dem Login kopierenMounten Sie den Renderer beim Mounten auf ctx 🎜🎜🎜🎜 in der KeepAlive-Komponente Die KeepAlive-Komponente ruft einige Methoden vom Renderer auf sharedContext ab, z. B. move, createElement usw. 🎜
sharedContext.activate = (vnode, container, anchor) => { // ... // props 改变需要重新 patch(update) patch( instance.vnode, vnode, container, anchor, instance, parentSuspense, isSVG, vnode.slotScopeIds, optimized ) }
Nach dem Login kopierenNach dem Login kopierenDie Unterkomponente führt eine spezifische Zerstörungs- und Rendering-Logik aus🎜🎜🎜🎜Wie Sie oben sehen können, wird beim Rendern der KeepAlive-Komponente zunächst das entsprechende ShapeFlag-Flag zum Vnode der Unterkomponente hinzugefügt🎜🎜🎜 Beispielsweise , teilt das
COMPONENT_KEPT_ALIVE
-Flag dem Renderer beim Mounten der Komponente mit, dass sie nicht gemountet werden muss, sondern eine spezielle Verarbeitung erfordert Komponente, wenn sie deinstalliert wird. Der Renderer muss nicht ausgehängt werden, sondern erfordert eine spezielle Verarbeitung. 🎜rrreeeSo mounten Sie
aktiviert
unddeaktiviert</ Code> Lebenszyklus (Sie müssen sich nicht auf den Lebenszyklus konzentrieren) 🎜🎜🎜🎜Zunächst werden diese beiden Lebenszyklen in der KeepAlive-Komponente eindeutig deklariert und direkt exportiert und verwendet. 🎜<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>export function onActivated( hook: Function, target?: ComponentInternalInstance | null ) { // 注册 activated 的回调函数到当前的 instance 的钩子函数上 registerKeepAliveHook(hook, LifecycleHooks.ACTIVATED, target) } export function onDeactivated( hook: Function, target?: ComponentInternalInstance | null ) { // 注册 deactivated 的回调函数到当前的 instance 的钩子函数上 registerKeepAliveHook(hook, LifecycleHooks.DEACTIVATED, target) }</pre><div class="contentsignin">Nach dem Login kopieren</div></div><div class="contentsignin">Nach dem Login kopieren</div></div><p>然后因为这两个生命周期会注册在 setup 里面,所以只要执行 setup 就会将两个生命周期的回调函数注册到当前的 instance 实例上</p><div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:php;toolbar:false;'>// renderer.ts // mount 函数逻辑 const mountComponent = (initialVNode, container, anchor, parentComponent, parentSuspense, isSVG, optimized ) => { // ... const instance: ComponentInternalInstance = compatMountInstance || (initialVNode.component = createComponentInstance( initialVNode, parentComponent, parentSuspense )) // 执行 setup setupComponent(instance) } // setupcomponent 处理 setup 函数值 export function setupComponent( instance: ComponentInternalInstance, isSSR = false ) { // ... const isStateful = isStatefulComponent(instance) // ... const setupResult = isStateful // setupStatefulComponent 函数主要功能是设置当前的 instance ? setupStatefulComponent(instance, isSSR) : undefined // ... } function setupStatefulComponent( instance: ComponentInternalInstance ){ if (setup) { //设置当前实例 setCurrentInstance(instance) // 执行组件内 setup 函数,执行 onActivated 钩子函数进行回调函数收集 const setupResult = callWithErrorHandling( setup, instance, ErrorCodes.SETUP_FUNCTION, [__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext] ) // currentInstance = null; unsetCurrentInstance() } }</pre><div class="contentsignin">Nach dem Login kopieren</div></div><div class="contentsignin">Nach dem Login kopieren</div></div><p>最后在执行<code>sharedContext.activate
和sharedContext.deactivate
的时候将注册在实例上的回调函数取出来直接执行就OK了,执行时机在 postRender 之后sharedContext.activate = (vnode, container, anchor) => { // KeepAlive 下组件激活时执行的 move 逻辑 move(vnode, container, anchor, 0 /* ENTER */) // 把回调推入到 postFlush 的异步任务队列中去执行 queuePostRenderEffect(() => { if (instance.a) { // a是 activated 钩子的简称 invokeArrayFns(instance.a) } }) } sharedContext.activate = (vnode, container, anchor) => { // KeepAlive 下组件失活时执行的 move 逻辑 move(vnode, container, anchor, 0 /* ENTER */) queuePostRenderEffect(() => { if (instance.da) { // da是 deactivated 钩子的简称 invokeArrayFns(instance.da) } }) } export const enum LifecycleHooks { // ... 其他生命周期声明 DEACTIVATED = 'da', ACTIVATED = 'a', } export interface ComponentInternalInstance { // ... 其他生命周期 [LifecycleHooks.ACTIVATED]: Function[] [LifecycleHooks.DEACTIVATED]: Function[] }
Nach dem Login kopierenNach dem Login kopieren以下是关于上述demo如何实现的简化流程图
需要注意的知识点
1、什么时候缓存
KeepAlive 组件的
onMounted
和onUpdated
生命周期时进行缓存2、什么时候取消缓存
缓存数量超过设置的 max 时
- 监听 include 和 exclude 修改的时候,会读取缓存中的知进行判断是否需要清除缓存
修剪缓存的时候也要 unmount(如果该缓存不是当前组件)或者 resetShapeFlag 将标志为从 KeepAlive 相关 shapeFlag 状态重置为 STATEFUL_COMPONENT 状态(如果该缓存是当前组件,但是被exclude了),当然 unmount 函数内包含 resetShapeFlag 操作
3、缓存策略
KeepAlive 组件的缓存策略是 LRU(last recently used)缓存策略
核心思想在于需要把当前访问或渲染的组件作为最新一次渲染的组件,并且该组件在缓存修剪过程中始终是安全的,即不会被修剪。
看下面的图更加直观,图片来源一篇讲keepAlive 缓存优化的文章
4、如何添加到 vue devtools 组件树上
sharedContext.activate = (vnode, container, anchor) => { // instance 是子组件实例 const instance = vnode.component! // ... // dev环境下设置, 自己模拟写的 devtools.emit('component:added', instance.appContext.app, instance.uid, instance.parent ? instance.parent.uid: undefined, instance) // 官方添加 if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { // Update components tree devtoolsComponentAdded(instance) } } // 同理 sharedContext.deactivates 上也要添加,不然不会显示在组件树上
Nach dem Login kopierenNach dem Login kopieren5、缓存的子组件 props 更新处理
当子组件有 prop 更新时是需要重新去 patch 的,所以在 activate 的时候需要重新执行 patch 进行子组件更新
sharedContext.activate = (vnode, container, anchor) => { // ... // props 改变需要重新 patch(update) patch( instance.vnode, vnode, container, anchor, instance, parentSuspense, isSVG, vnode.slotScopeIds, optimized ) }
Nach dem Login kopierenNach dem Login kopierenDas obige ist der detaillierte Inhalt vonEin Artikel über die KeepAlive-Komponente in Vue. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

AI Hentai Generator
Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

Heiße Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen



PHP und Vue: eine perfekte Kombination von Front-End-Entwicklungstools In der heutigen Zeit der rasanten Entwicklung des Internets ist die Front-End-Entwicklung immer wichtiger geworden. Da Benutzer immer höhere Anforderungen an das Erlebnis von Websites und Anwendungen stellen, müssen Frontend-Entwickler effizientere und flexiblere Tools verwenden, um reaktionsfähige und interaktive Schnittstellen zu erstellen. Als zwei wichtige Technologien im Bereich der Front-End-Entwicklung können PHP und Vue.js in Kombination als perfekte Waffe bezeichnet werden. In diesem Artikel geht es um die Kombination von PHP und Vue sowie um detaillierte Codebeispiele, die den Lesern helfen sollen, diese beiden besser zu verstehen und anzuwenden

In Front-End-Entwicklungsinterviews decken häufige Fragen ein breites Themenspektrum ab, darunter HTML/CSS-Grundlagen, JavaScript-Grundlagen, Frameworks und Bibliotheken, Projekterfahrung, Algorithmen und Datenstrukturen, Leistungsoptimierung, domänenübergreifende Anfragen, Front-End-Engineering, Designmuster sowie neue Technologien und Trends. Interviewerfragen sollen die technischen Fähigkeiten, die Projekterfahrung und das Verständnis des Kandidaten für Branchentrends beurteilen. Daher sollten Kandidaten in diesen Bereichen umfassend vorbereitet sein, um ihre Fähigkeiten und Fachkenntnisse unter Beweis zu stellen.

Django ist ein in Python geschriebenes Webanwendungs-Framework, das Wert auf schnelle Entwicklung und saubere Methoden legt. Obwohl Django ein Web-Framework ist, müssen Sie zur Beantwortung der Frage, ob Django ein Front-End oder ein Back-End ist, ein tiefes Verständnis der Konzepte von Front-End und Back-End haben. Das Front-End bezieht sich auf die Schnittstelle, mit der Benutzer direkt interagieren, und das Back-End bezieht sich auf serverseitige Programme. Sie interagieren mit Daten über das HTTP-Protokoll. Wenn das Front-End und das Back-End getrennt sind, können die Front-End- und Back-End-Programme unabhängig voneinander entwickelt werden, um Geschäftslogik bzw. interaktive Effekte sowie den Datenaustausch zu implementieren.

Als C#-Entwickler umfasst unsere Entwicklungsarbeit in der Regel die Front-End- und Back-End-Entwicklung. Mit der Weiterentwicklung der Technologie und der zunehmenden Komplexität von Projekten wird die gemeinsame Entwicklung von Front-End und Back-End immer wichtiger und komplexer. In diesem Artikel werden einige kollaborative Front-End- und Back-End-Entwicklungstechniken vorgestellt, um C#-Entwicklern dabei zu helfen, die Entwicklungsarbeit effizienter abzuschließen. Nach der Festlegung der Schnittstellenspezifikationen ist die gemeinsame Entwicklung von Front-End und Backend untrennbar mit dem Zusammenspiel der API-Schnittstellen verbunden. Um den reibungslosen Ablauf der kollaborativen Front-End- und Back-End-Entwicklung sicherzustellen, ist es am wichtigsten, gute Schnittstellenspezifikationen zu definieren. Bei der Schnittstellenspezifikation handelt es sich um den Namen der Schnittstelle

Als schnelle und effiziente Programmiersprache erfreut sich Go im Bereich der Backend-Entwicklung großer Beliebtheit. Allerdings assoziieren nur wenige Menschen die Go-Sprache mit der Front-End-Entwicklung. Tatsächlich kann die Verwendung der Go-Sprache für die Front-End-Entwicklung nicht nur die Effizienz verbessern, sondern Entwicklern auch neue Horizonte eröffnen. In diesem Artikel wird die Möglichkeit der Verwendung der Go-Sprache für die Front-End-Entwicklung untersucht und spezifische Codebeispiele bereitgestellt, um den Lesern ein besseres Verständnis dieses Bereichs zu erleichtern. In der traditionellen Frontend-Entwicklung werden häufig JavaScript, HTML und CSS zum Erstellen von Benutzeroberflächen verwendet

Zu den Methoden zur Implementierung von Instant Messaging gehören WebSocket, Long Polling, vom Server gesendete Ereignisse, WebRTC usw. Detaillierte Einführung: 1. WebSocket, das eine dauerhafte Verbindung zwischen dem Client und dem Server herstellen kann, um eine bidirektionale Kommunikation in Echtzeit zu erreichen. Das Front-End kann die WebSocket-API verwenden, um eine WebSocket-Verbindung herzustellen und Instant Messaging durch Senden und Empfangen zu erreichen 2. Long Polling, eine Technologie, die Echtzeitkommunikation usw. simuliert.

Kombination von Golang und Front-End-Technologie: Um zu untersuchen, welche Rolle Golang im Front-End-Bereich spielt, sind spezifische Codebeispiele erforderlich. Mit der rasanten Entwicklung des Internets und mobiler Anwendungen ist die Front-End-Technologie immer wichtiger geworden. Auch in diesem Bereich kann Golang als leistungsstarke Back-End-Programmiersprache eine wichtige Rolle spielen. In diesem Artikel wird untersucht, wie Golang mit Front-End-Technologie kombiniert wird, und sein Potenzial im Front-End-Bereich anhand spezifischer Codebeispiele demonstriert. Die Rolle von Golang im Front-End-Bereich ist effizient, prägnant und leicht zu erlernen

Django: Ein magisches Framework, das sowohl Front-End- als auch Back-End-Entwicklung bewältigen kann! Django ist ein effizientes und skalierbares Webanwendungs-Framework. Es unterstützt mehrere Webentwicklungsmodelle, einschließlich MVC und MTV, und kann problemlos hochwertige Webanwendungen entwickeln. Django unterstützt nicht nur die Back-End-Entwicklung, sondern kann auch schnell Front-End-Schnittstellen erstellen und durch die Vorlagensprache eine flexible Ansichtsanzeige erreichen. Django kombiniert Front-End-Entwicklung und Back-End-Entwicklung zu einer nahtlosen Integration, sodass sich Entwickler nicht auf das Lernen spezialisieren müssen
