Maison  >  Article  >  interface Web  >  Un article pour parler du composant KeepAlive dans Vue

Un article pour parler du composant KeepAlive dans Vue

青灯夜游
青灯夜游avant
2022-11-14 20:21:131855parcourir

Un article pour parler du composant KeepAlive dans Vue

Récemment, je lisais des points de connaissances liés à Vue. Quand j'ai vu le composant KeepAlive, j'étais curieux de savoir comment il ne se restituait pas lors du basculement entre les composants, alors j'ai regardé de plus près. (Partage de vidéos d'apprentissage : tutoriel vidéo vue)

Si vous êtes également intéressé à savoir comment la mise en œuvre interne spécifique est mise en œuvre ou si vous avez une certaine compréhension mais n'êtes pas assez familier, alors vous pouvez également la consolider ensemble

Conseils : Interview comme celle-ci Quand pouvez-vous interroger à haute voix les autres sur ce point de connaissance ?

Qu'est-ce que KeepAlive ?

Fonction KeepAlive

<keepalive></keepalive> 是一个内置组件,它的功能是在多个组件间动态切换缓存被移除的组件实例。

KeepAlive 功能

KeepAlive 一词借鉴于 HTTP 协议,在 HTTP 协议里面 KeepAlive 又称持久连接,作用是允许多个请求/响应共用同一个 HTTP 连接,解决了频繁的销毁和创建 HTTP 连接带来的额外性能开销。而同理 Vue 里的 KeepAlive 组件也是为了避免一个组件被频繁的销毁/重建,避免了性能上的开销。

// App.vue
<Test :msg="curTab" v-if="curTab === &#39;Test&#39;"></Test>
<HelloWorld :msg="curTab" v-if="curTab === &#39;HelloWorld&#39;"></HelloWorld>
<div @click="toggle">toggle</div>

上述代码可以看到,如果我们频繁点击 toggle 时会频繁的渲染 Test/HelloWorld 组件,当用户频繁的点击时 Test 组件需要频繁的销毁/渲染,这就造成很大的渲染性能损失。

所以为了解决这种性能开销,你需要知道是时候使用 KeepAlive 组件。

<KeepAlive>
  <component :is="curTab === &#39;Test&#39; ? Test : HelloWorld" :msg="curTab"></component>
</KeepAlive>
<div @click="toggle">toggle</div>

可以看这个录屏,在首次加载后再次频繁的切换并没有重新销毁与挂载,而仅仅是将组件进行了失活(而不是销毁),渲染时只需要重新激活就可以,而不需重新挂载,如果要渲染的组件很大,那就能有不错的性能优化。

想要体验的话可以去看看这个例子?官方demo,其中数据会被缓存这个也需要在开发使用中去注意到的

如何实现

实现原理其实很简单,其实就是缓存管理和特定的销毁和渲染逻辑,使得它不同于其他组件。

KeepAlive 组件在卸载组件时并不能真的将其卸载,而是将其放到一个隐藏的容器里面当被激活时再从隐藏的容器中拿出来挂载到真正的 dom 上就行,这也就对应了 KeepAlive 的两个独特的生命周期activateddeactivated

Un article pour parler du composant KeepAlive dans Vue

先来简单了解下组件的挂载过程

Un article pour parler du composant KeepAlive dans Vue所以在 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(&#39;div&#39;)

// 存储当前的子组件的缓存 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
  }
}
  • 如何挂载activateddeactivated

    KeepAlive est emprunté au protocole HTTP, KeepAlive est également appelé connexion persistante. Sa fonction est d'autoriser plusieurs requêtes/réponses. partagent la même connexion HTTP, ce qui résout la surcharge de performances supplémentaire causée par la destruction et la création fréquentes de connexions HTTP. De la même manière, le composant KeepAlive dans Vue est également conçu pour empêcher un composant d'être fréquemment détruit/reconstruit, évitant ainsi une surcharge de performances.
  • 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)
    }
Comme vous pouvez le voir dans le code ci-dessus, si nous cliquons fréquemment sur bascule, le composant Test/HelloWorld sera fréquemment rendu. Lorsque l'utilisateur clique fréquemment, le composant Test doit être détruit/rendu fréquemment, ce qui entraîne une grande perte. dans les performances de rendu.

Donc, afin de résoudre cette surcharge de performances, vous devez savoir qu'il est temps d'utiliser le composant 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()
  }
}
🎜🎜🎜Vous pouvez regarder ceci Lors de l'enregistrement de l'écran, des commutations fréquentes après le premier chargement ne le détruisent pas et ne le remontent pas, mais désactivent (plutôt que ne le détruisent) le composant lors du rendu, il suffit de le réactiver sans le remonter. grand, il peut y avoir de bonnes optimisations de performances. 🎜🎜Si vous souhaitez en faire l'expérience, vous pouvez jeter un œil à cet exemple ?Démo officielle🎜 , dans lequel les données seront mises en cache, il faut également y prêter attention lors du développement et de l'utilisation🎜

🎜Comment mettre en œuvre🎜🎜🎜Le principe de mise en œuvre est en fait très simple. il s'agit d'une 🎜gestion du cache et d'une logique spécifique de destruction et de rendu🎜, ce qui le rend différent des autres composants. 🎜🎜Le composant KeepAlive ne peut pas vraiment être désinstallé lors de la 🎜désinstallation du composant, au lieu de cela, il est placé dans un conteneur caché🎜 Lorsqu'il est activé, il est retiré du conteneur caché et monté sur le vrai dom. aux deux cycles de vie uniques de KeepAlive activé et désactivé. 🎜🎜Un article pour parler du composant KeepAlive dans Vue🎜

🎜Commençons par comprendre brièvement le processus de montage des composants🎜🎜🎜Un article pour parler du composant KeepAlive dans VueAinsi, le sous-composant 🎜 de KeepAlive exécutera une logique de rendu spécifique lors du montage et du démontage, afin que la logique de montage et de destruction ne soit pas effectuée 🎜🎜

🎜Implémentation concrète (implémentation d'un petit et simple KeepAlive)🎜🎜

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer