최근 Vue 관련 지식 포인트를 읽다가 KeepAlive 컴포넌트를 보다가 컴포넌트 간 전환 시 어떻게 다시 렌더링되지 않는지 궁금해서 자세히 살펴봤습니다. (학습 동영상 공유: vue 동영상 튜토리얼)
특정 내부 구현이 어떻게 구현되는지 알고 싶거나 어느 정도 이해하고 있지만 충분히 익숙하지 않은 경우 함께 통합할 수도 있습니다
팁: 이런 면접, 이 지식 포인트를 다른 사람에게 큰소리로 물어볼 수 있는 때는 언제일까요?
KeepAlive가 무엇인가요? KeepAlive 기능
<keepalive></keepalive>
是一个内置组件,它的功能是在多个组件间动态切换时缓存被移除的组件实例。
KeepAlive 功能
KeepAlive 一词借鉴于 HTTP 协议,在 HTTP 协议里面 KeepAlive 又称持久连接,作用是允许多个请求/响应共用同一个 HTTP 连接,解决了频繁的销毁和创建 HTTP 连接带来的额外性能开销。而同理 Vue 里的 KeepAlive 组件也是为了避免一个组件被频繁的销毁/重建,避免了性能上的开销。
// App.vue <Test :msg="curTab" v-if="curTab === 'Test'"></Test> <HelloWorld :msg="curTab" v-if="curTab === 'HelloWorld'"></HelloWorld> <div @click="toggle">toggle</div>
上述代码可以看到,如果我们频繁点击 toggle 时会频繁的渲染 Test/HelloWorld 组件,当用户频繁的点击时 Test 组件需要频繁的销毁/渲染,这就造成很大的渲染性能损失。
所以为了解决这种性能开销,你需要知道是时候使用 KeepAlive 组件。
<KeepAlive> <component :is="curTab === 'Test' ? Test : HelloWorld" :msg="curTab"></component> </KeepAlive> <div @click="toggle">toggle</div>
可以看这个录屏,在首次加载后再次频繁的切换并没有重新销毁与挂载,而仅仅是将组件进行了失活(而不是销毁),渲染时只需要重新激活就可以,而不需重新挂载,如果要渲染的组件很大,那就能有不错的性能优化。
想要体验的话可以去看看这个例子?官方demo,其中数据会被缓存这个也需要在开发使用中去注意到的
如何实现
实现原理其实很简单,其实就是缓存管理和特定的销毁和渲染逻辑,使得它不同于其他组件。
KeepAlive 组件在卸载组件时并不能真的将其卸载,而是将其放到一个隐藏的容器里面,当被激活时再从隐藏的容器中拿出来挂载到真正的 dom 上就行,这也就对应了 KeepAlive 的两个独特的生命周期activated
和deactivated
。
先来简单了解下组件的挂载过程
所以在 KeepAlive 内的子组件在 mount 和 unmount 的时候会执行特定的渲染逻辑,从而不会去走挂载和销毁逻辑
具体实现(实现一个小而简单的 KeepAlive)
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
KeepAlive 组件的 setup 逻辑以及渲染逻辑(重点看)
// 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组件 mount 时挂载 renderer 到 ctx 上
在 KeepAlive 组件内会从 sharedContext 上的 renderer 上拿到一些方法比如 move、createElement 等
function mountComponent() { // ... if (isKeepAlive(initialVNode)) { ;(instance.ctx as KeepAliveContext).renderer = internals } }
子组件执行特定的销毁和渲染逻辑
首先从上面可以看到,在渲染 KeepAlive 组件时会对其子组件的 vnode 上增加对应的 shapeFlag 标志
比如COMPONENT_KEPT_ALIVE
标志,组件挂载的时候告诉渲染器这个不需要 mount 而需要特殊处理
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 ) } // ... } }
同理COMPONENT_SHOULD_KEEP_ALIVE
标志也是用来在组件卸载的时候告诉渲染器这个不需要 unmount 而需要特殊处理。
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 } }
-
如何挂载
KeepAlive는 HTTP 프로토콜에서 빌려온 것입니다. HTTP 프로토콜에서 KeepAlive는 영구 연결이라고도 합니다. 동일한 HTTP 연결을 공유하여 HTTP 연결의 빈번한 파괴 및 생성으로 인한 추가 성능 오버헤드를 해결합니다. 같은 방식으로 Vue의 KeepAlive 구성 요소도 구성 요소가 자주 파괴/재구축되는 것을 방지하여 성능 오버헤드를 방지하도록 설계되었습니다.activated
和deactivated
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) }위 코드를 보면 토글을 자주 클릭하면 Test/HelloWorld 컴포넌트가 자주 렌더링된다는 것을 알 수 있습니다. 사용자가 자주 클릭하면 테스트 컴포넌트가 자주 삭제/렌더링되어야 하므로 큰 손실이 발생합니다. 렌더링 성능에서. 그러므로 이러한 성능 오버헤드를 해결하려면 이제 KeepAlive 구성 요소를 사용해야 할 때라는 것을 알아야 합니다. 🎜
// 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() } }🎜

🎜구현 방법🎜🎜🎜실제로 구현 원리는 매우 간단합니다. 🎜캐시 관리와 특정 파괴 및 렌더링 로직🎜으로 다른 구성 요소와 다릅니다. 🎜🎜KeepAlive 구성 요소는 🎜제거할 때 실제로 제거할 수 없습니다. 대신 숨겨진 컨테이너에 배치됩니다🎜. 활성화되면 숨겨진 컨테이너에서 꺼내어 실제 dom에 마운트됩니다. KeepAlive 활성화
및 비활성화
의 두 가지 고유한 수명 주기에 적용됩니다. 🎜🎜
🎜🎜먼저 컴포넌트 마운트 과정을 간략하게 알아보겠습니다🎜🎜🎜
그래서 KeepAlive의 🎜 하위 구성 요소는 마운트 및 마운트 해제 시 특정 렌더링 로직을 실행하므로 마운트 및 파괴 로직이 수행되지 않습니다. 🎜🎜🎜구체적인 구현(작고 간단한 KeepAlive 구현)🎜🎜KeepAlive 구성 요소 속성🎜🎜🎜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[]
}
KeepAlive 구성 요소 설정 로직 및 렌더링 로직(포커스)🎜 🎜🎜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 上也要添加,不然不会显示在组件树上
ctx 🎜🎜🎜🎜에 마운트할 때 KeepAlive 구성 요소에 렌더러를 마운트하세요. KeepAlive 구성 요소는 sharedContext의 렌더러에서 move, createElement 등과 같은 일부 메서드를 가져옵니다. 🎜sharedContext.activate = (vnode, container, anchor) => {
// ...
// props 改变需要重新 patch(update)
patch(
instance.vnode,
vnode,
container,
anchor,
instance,
parentSuspense,
isSVG,
vnode.slotScopeIds,
optimized
)
}
하위 구성 요소는 특정 파괴 및 렌더링 논리를 실행합니다🎜🎜🎜🎜우선 위에서 볼 수 있듯이 🎜KeepAlive 구성 요소를 렌더링할 때 해당 ShapeFlag 플래그가 하위 구성 요소의 vnode에 추가됩니다🎜🎜🎜 예를 들어 , COMPONENT_KEPT_ALIVE
플래그는 구성 요소가 마운트될 때 마운트가 필요하지 않지만 특별한 처리가 필요함을 렌더러에 알려줍니다🎜rrreee🎜마찬가지로 COMPONENT_SHOULD_KEEP_ALIVE
플래그도 렌더러는 마운트 해제할 필요가 없지만 특별한 처리가 필요합니다. 🎜rrreee-
활성화
및 비활성화
마운트 방법 code> 라이프사이클 (라이프사이클에 집중할 필요는 없습니다) 🎜🎜🎜🎜우선 이 두 라이프사이클은 KeepAlive 컴포넌트에 고유하게 선언되어 직접 내보내어 사용합니다. 🎜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)
}
然后因为这两个生命周期会注册在 setup 里面,所以只要执行 setup 就会将两个生命周期的回调函数注册到当前的 instance 实例上
// 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()
}
}
最后在执行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[]
}
以下是关于上述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 上也要添加,不然不会显示在组件树上
5、缓存的子组件 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
)
}

🎜구체적인 구현(작고 간단한 KeepAlive 구현)🎜🎜KeepAlive 구성 요소 속성🎜🎜🎜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[]
}
KeepAlive 구성 요소 설정 로직 및 렌더링 로직(포커스)🎜 🎜🎜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 上也要添加,不然不会显示在组件树上
ctx 🎜🎜🎜🎜에 마운트할 때 KeepAlive 구성 요소에 렌더러를 마운트하세요. KeepAlive 구성 요소는 sharedContext의 렌더러에서 move, createElement 등과 같은 일부 메서드를 가져옵니다. 🎜sharedContext.activate = (vnode, container, anchor) => {
// ...
// props 改变需要重新 patch(update)
patch(
instance.vnode,
vnode,
container,
anchor,
instance,
parentSuspense,
isSVG,
vnode.slotScopeIds,
optimized
)
}
하위 구성 요소는 특정 파괴 및 렌더링 논리를 실행합니다🎜🎜🎜🎜우선 위에서 볼 수 있듯이 🎜KeepAlive 구성 요소를 렌더링할 때 해당 ShapeFlag 플래그가 하위 구성 요소의 vnode에 추가됩니다🎜🎜🎜 예를 들어 , COMPONENT_KEPT_ALIVE
플래그는 구성 요소가 마운트될 때 마운트가 필요하지 않지만 특별한 처리가 필요함을 렌더러에 알려줍니다🎜rrreee🎜마찬가지로 COMPONENT_SHOULD_KEEP_ALIVE
플래그도 렌더러는 마운트 해제할 필요가 없지만 특별한 처리가 필요합니다. 🎜rrreee-
활성화
및 비활성화
마운트 방법 code> 라이프사이클 (라이프사이클에 집중할 필요는 없습니다) 🎜🎜🎜🎜우선 이 두 라이프사이클은 KeepAlive 컴포넌트에 고유하게 선언되어 직접 내보내어 사용합니다. 🎜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)
}
然后因为这两个生命周期会注册在 setup 里面,所以只要执行 setup 就会将两个生命周期的回调函数注册到当前的 instance 实例上
// 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()
}
}
最后在执行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[]
}
以下是关于上述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 上也要添加,不然不会显示在组件树上
5、缓存的子组件 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
)
}
KeepAlive 구성 요소 속성🎜🎜🎜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[]
}
KeepAlive 구성 요소 설정 로직 및 렌더링 로직(포커스)🎜 🎜🎜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 上也要添加,不然不会显示在组件树上
ctx 🎜🎜🎜🎜에 마운트할 때 KeepAlive 구성 요소에 렌더러를 마운트하세요. KeepAlive 구성 요소는 sharedContext의 렌더러에서 move, createElement 등과 같은 일부 메서드를 가져옵니다. 🎜sharedContext.activate = (vnode, container, anchor) => {
// ...
// props 改变需要重新 patch(update)
patch(
instance.vnode,
vnode,
container,
anchor,
instance,
parentSuspense,
isSVG,
vnode.slotScopeIds,
optimized
)
}
하위 구성 요소는 특정 파괴 및 렌더링 논리를 실행합니다🎜🎜🎜🎜우선 위에서 볼 수 있듯이 🎜KeepAlive 구성 요소를 렌더링할 때 해당 ShapeFlag 플래그가 하위 구성 요소의 vnode에 추가됩니다🎜🎜🎜 예를 들어 , COMPONENT_KEPT_ALIVE
플래그는 구성 요소가 마운트될 때 마운트가 필요하지 않지만 특별한 처리가 필요함을 렌더러에 알려줍니다🎜rrreee🎜마찬가지로 COMPONENT_SHOULD_KEEP_ALIVE
플래그도 렌더러는 마운트 해제할 필요가 없지만 특별한 처리가 필요합니다. 🎜rrreee-
활성화
및 비활성화
마운트 방법 code> 라이프사이클 (라이프사이클에 집중할 필요는 없습니다) 🎜🎜🎜🎜우선 이 두 라이프사이클은 KeepAlive 컴포넌트에 고유하게 선언되어 직접 내보내어 사용합니다. 🎜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)
}
然后因为这两个生命周期会注册在 setup 里面,所以只要执行 setup 就会将两个生命周期的回调函数注册到当前的 instance 实例上
// 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()
}
}
最后在执行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[]
}
以下是关于上述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 上也要添加,不然不会显示在组件树上
5、缓存的子组件 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
)
}
KeepAlive 구성 요소 설정 로직 및 렌더링 로직(포커스)🎜 🎜🎜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 上也要添加,不然不会显示在组件树上
ctx 🎜🎜🎜🎜에 마운트할 때 KeepAlive 구성 요소에 렌더러를 마운트하세요. KeepAlive 구성 요소는 sharedContext의 렌더러에서 move, createElement 등과 같은 일부 메서드를 가져옵니다. 🎜sharedContext.activate = (vnode, container, anchor) => {
// ...
// props 改变需要重新 patch(update)
patch(
instance.vnode,
vnode,
container,
anchor,
instance,
parentSuspense,
isSVG,
vnode.slotScopeIds,
optimized
)
}
하위 구성 요소는 특정 파괴 및 렌더링 논리를 실행합니다🎜🎜🎜🎜우선 위에서 볼 수 있듯이 🎜KeepAlive 구성 요소를 렌더링할 때 해당 ShapeFlag 플래그가 하위 구성 요소의 vnode에 추가됩니다🎜🎜🎜 예를 들어 , COMPONENT_KEPT_ALIVE
플래그는 구성 요소가 마운트될 때 마운트가 필요하지 않지만 특별한 처리가 필요함을 렌더러에 알려줍니다🎜rrreee🎜마찬가지로 COMPONENT_SHOULD_KEEP_ALIVE
플래그도 렌더러는 마운트 해제할 필요가 없지만 특별한 처리가 필요합니다. 🎜rrreee-
활성화
및 비활성화
마운트 방법 code> 라이프사이클 (라이프사이클에 집중할 필요는 없습니다) 🎜🎜🎜🎜우선 이 두 라이프사이클은 KeepAlive 컴포넌트에 고유하게 선언되어 직접 내보내어 사용합니다. 🎜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)
}
然后因为这两个生命周期会注册在 setup 里面,所以只要执行 setup 就会将两个生命周期的回调函数注册到当前的 instance 实例上
// 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()
}
}
最后在执行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[]
}
以下是关于上述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 上也要添加,不然不会显示在组件树上
5、缓存的子组件 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
)
}
ctx 🎜🎜🎜🎜에 마운트할 때 KeepAlive 구성 요소에 렌더러를 마운트하세요. KeepAlive 구성 요소는 sharedContext의 렌더러에서 move, createElement 등과 같은 일부 메서드를 가져옵니다. 🎜sharedContext.activate = (vnode, container, anchor) => {
// ...
// props 改变需要重新 patch(update)
patch(
instance.vnode,
vnode,
container,
anchor,
instance,
parentSuspense,
isSVG,
vnode.slotScopeIds,
optimized
)
}
하위 구성 요소는 특정 파괴 및 렌더링 논리를 실행합니다🎜🎜🎜🎜우선 위에서 볼 수 있듯이 🎜KeepAlive 구성 요소를 렌더링할 때 해당 ShapeFlag 플래그가 하위 구성 요소의 vnode에 추가됩니다🎜🎜🎜 예를 들어 , COMPONENT_KEPT_ALIVE
플래그는 구성 요소가 마운트될 때 마운트가 필요하지 않지만 특별한 처리가 필요함을 렌더러에 알려줍니다🎜rrreee🎜마찬가지로 COMPONENT_SHOULD_KEEP_ALIVE
플래그도 렌더러는 마운트 해제할 필요가 없지만 특별한 처리가 필요합니다. 🎜rrreee-
활성화
및 비활성화
마운트 방법 code> 라이프사이클 (라이프사이클에 집중할 필요는 없습니다) 🎜🎜🎜🎜우선 이 두 라이프사이클은 KeepAlive 컴포넌트에 고유하게 선언되어 직접 내보내어 사용합니다. 🎜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)
}
然后因为这两个生命周期会注册在 setup 里面,所以只要执行 setup 就会将两个生命周期的回调函数注册到当前的 instance 实例上
// 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()
}
}
最后在执行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[]
}
以下是关于上述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 上也要添加,不然不会显示在组件树上
5、缓存的子组件 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
)
}
하위 구성 요소는 특정 파괴 및 렌더링 논리를 실행합니다🎜🎜🎜🎜우선 위에서 볼 수 있듯이 🎜KeepAlive 구성 요소를 렌더링할 때 해당 ShapeFlag 플래그가 하위 구성 요소의 vnode에 추가됩니다🎜🎜🎜 예를 들어 , COMPONENT_KEPT_ALIVE
플래그는 구성 요소가 마운트될 때 마운트가 필요하지 않지만 특별한 처리가 필요함을 렌더러에 알려줍니다🎜rrreee🎜마찬가지로 COMPONENT_SHOULD_KEEP_ALIVE
플래그도 렌더러는 마운트 해제할 필요가 없지만 특별한 처리가 필요합니다. 🎜rrreee-
활성화
및 비활성화
마운트 방법 code> 라이프사이클 (라이프사이클에 집중할 필요는 없습니다) 🎜🎜🎜🎜우선 이 두 라이프사이클은 KeepAlive 컴포넌트에 고유하게 선언되어 직접 내보내어 사용합니다. 🎜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)
}
然后因为这两个生命周期会注册在 setup 里面,所以只要执行 setup 就会将两个生命周期的回调函数注册到当前的 instance 实例上
// 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()
}
}
最后在执行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[]
}
以下是关于上述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 上也要添加,不然不会显示在组件树上
5、缓存的子组件 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
)
}
활성화
및 비활성화
마운트 방법 code> 라이프사이클 (라이프사이클에 집중할 필요는 없습니다) 🎜🎜🎜🎜우선 이 두 라이프사이클은 KeepAlive 컴포넌트에 고유하게 선언되어 직접 내보내어 사용합니다. 🎜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)
}
然后因为这两个生命周期会注册在 setup 里面,所以只要执行 setup 就会将两个生命周期的回调函数注册到当前的 instance 实例上
// 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() } }
最后在执行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[] }
以下是关于上述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 上也要添加,不然不会显示在组件树上
5、缓存的子组件 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 ) }
위 내용은 Vue의 KeepAlive 구성 요소에 대해 이야기하는 기사의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

Netflix는 주로 프레임 워크 선택의 성능, 확장 성, 개발 효율성, 생태계, 기술 부채 및 유지 보수 비용을 고려합니다. 1. 성능 및 확장 성 : Java 및 SpringBoot는 대규모 데이터 및 높은 동시 요청을 효율적으로 처리하기 위해 선택됩니다. 2. 개발 효율성 및 생태계 : React를 사용하여 프론트 엔드 개발 효율성을 향상시키고 풍부한 생태계를 활용하십시오. 3. 기술 부채 및 유지 보수 비용 : Node.js를 선택하여 유지 보수 비용과 기술 부채를 줄이기 위해 마이크로 서비스를 구축하십시오.

Netflix는 주로 VUE가 특정 기능을 위해 보충하는 프론트 엔드 프레임 워크로 React를 사용합니다. 1) React의 구성 요소화 및 가상 DOM은 Netflix 애플리케이션의 성능 및 개발 효율을 향상시킵니다. 2) VUE는 Netflix의 내부 도구 및 소규모 프로젝트에 사용되며 유연성과 사용 편의성이 핵심입니다.

vue.js는 복잡한 사용자 인터페이스를 구축하는 데 적합한 점진적인 JavaScript 프레임 워크입니다. 1) 핵심 개념에는 반응 형 데이터, 구성 요소화 및 가상 DOM이 포함됩니다. 2) 실제 응용 분야에서는 TODO 응용 프로그램을 구축하고 Vuerouter를 통합하여 시연 할 수 있습니다. 3) 디버깅 할 때 VuedeVtools 및 Console.log를 사용하는 것이 좋습니다. 4) 성능 최적화는 V-IF/V- 쇼, 목록 렌더링 최적화, 구성 요소의 비동기로드 등을 통해 달성 할 수 있습니다.

vue.js는 중소형 프로젝트에 적합하지만 REACT는 크고 복잡한 응용 프로그램에 더 적합합니다. 1. Vue.js의 응답 형 시스템은 종속성 추적을 통해 DOM을 자동으로 업데이트하여 데이터 변경을 쉽게 관리 할 수 있습니다. 2. 반응은 단방향 데이터 흐름을 채택하고 데이터 흐름에서 하위 구성 요소로 데이터가 흐르고 명확한 데이터 흐름과 곤란하기 쉬운 구조를 제공합니다.

vue.js는 중소형 프로젝트 및 빠른 반복에 적합한 반면 React는 크고 복잡한 응용 프로그램에 적합합니다. 1) vue.js는 사용하기 쉽고 팀이 불충분하거나 프로젝트 규모가 작는 상황에 적합합니다. 2) React는 더 풍부한 생태계를 가지고 있으며 고성능 및 복잡한 기능적 요구가있는 프로젝트에 적합합니다.

VUE에서 태그의 점프를 구현하는 방법에는 다음이 포함됩니다. HTML 템플릿의 A 태그를 사용하여 HREF 속성을 지정합니다. VUE 라우팅의 라우터 링크 구성 요소를 사용하십시오. javaScript 에서이. $ router.push () 메소드를 사용하십시오. 매개 변수는 쿼리 매개 변수를 통해 전달 될 수 있으며 동적 점프를 위해 라우터 옵션에서 경로가 구성됩니다.

VUE에서 구성 요소 점프를 구현하는 방법은 다음과 같습니다. 라우터 링크 및 & lt; router-view & gt; 하이퍼 링크 점프를 수행하고 대상 경로로 속성을 지정합니다. & lt; router-view & gt; 현재 라우팅 된 렌더링 된 구성 요소를 표시하는 구성 요소. 프로그래밍 방식 탐색을 위해 router.push () 및 router.replace () 메소드를 사용하십시오. 전자는 역사를 구하고 후자는 기록을 떠나지 않고 현재 경로를 대체합니다.

VUE에서 DIV 요소를 점프하는 두 가지 방법이 있습니다. VUE 라우터를 사용하고 라우터 링크 구성 요소를 추가하십시오. @Click 이벤트 리스너를 추가하고 이것을 호출하십시오. $ router.push () 메소드를 점프하십시오.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

MinGW - Windows용 미니멀리스트 GNU
이 프로젝트는 osdn.net/projects/mingw로 마이그레이션되는 중입니다. 계속해서 그곳에서 우리를 팔로우할 수 있습니다. MinGW: GCC(GNU Compiler Collection)의 기본 Windows 포트로, 기본 Windows 애플리케이션을 구축하기 위한 무료 배포 가능 가져오기 라이브러리 및 헤더 파일로 C99 기능을 지원하는 MSVC 런타임에 대한 확장이 포함되어 있습니다. 모든 MinGW 소프트웨어는 64비트 Windows 플랫폼에서 실행될 수 있습니다.

DVWA
DVWA(Damn Vulnerable Web App)는 매우 취약한 PHP/MySQL 웹 애플리케이션입니다. 주요 목표는 보안 전문가가 법적 환경에서 자신의 기술과 도구를 테스트하고, 웹 개발자가 웹 응용 프로그램 보안 프로세스를 더 잘 이해할 수 있도록 돕고, 교사/학생이 교실 환경 웹 응용 프로그램에서 가르치고 배울 수 있도록 돕는 것입니다. 보안. DVWA의 목표는 다양한 난이도의 간단하고 간단한 인터페이스를 통해 가장 일반적인 웹 취약점 중 일부를 연습하는 것입니다. 이 소프트웨어는

에디트플러스 중국어 크랙 버전
작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음

SublimeText3 Linux 새 버전
SublimeText3 Linux 최신 버전

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.
