中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue中的KeepAlive組件怎么使用

發布時間:2022-11-15 09:06:13 來源:億速云 閱讀:126 作者:iii 欄目:編程語言

這篇文章主要介紹“Vue中的KeepAlive組件怎么使用”,在日常操作中,相信很多人在Vue中的KeepAlive組件怎么使用問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Vue中的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>

Vue中的KeepAlive組件怎么使用

可以看這個錄屏,在首次加載后再次頻繁的切換并沒有重新銷毀與掛載,而僅僅是將組件進行了失活(而不是銷毀),渲染時只需要重新激活就可以,而不需重新掛載,如果要渲染的組件很大,那就能有不錯的性能優化。

想要體驗的話可以去看看這個例子?官方demo,其中數據會被緩存這個也需要在開發使用中去注意到的

如何實現

實現原理其實很簡單,其實就是緩存管理和特定的銷毀和渲染邏輯,使得它不同于其他組件。

KeepAlive 組件在卸載組件時并不能真的將其卸載,而是將其放到一個隱藏的容器里面當被激活時再從隱藏的容器中拿出來掛載到真正的 dom 上就行,這也就對應了 KeepAlive 的兩個獨特的生命周期activateddeactivated

Vue中的KeepAlive組件怎么使用

先來簡單了解下組件的掛載過程

Vue中的KeepAlive組件怎么使用

所以在 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
  }
}

  • 如何掛載activateddeactivated生命周期(生命周期相關可以不用重點看)

首先這兩個生命周期是在 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.activatesharedContext.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如何實現的簡化流程圖

Vue中的KeepAlive組件怎么使用

需要注意的知識點

1、什么時候緩存

KeepAlive 組件的onMountedonUpdated生命周期時進行緩存

2、什么時候取消緩存
  • 緩存數量超過設置的 max 時

  • 監聽 include 和 exclude 修改的時候,會讀取緩存中的知進行判斷是否需要清除緩存

修剪緩存的時候也要 unmount(如果該緩存不是當前組件)或者 resetShapeFlag 將標志為從 KeepAlive 相關 shapeFlag 狀態重置為 STATEFUL_COMPONENT 狀態(如果該緩存是當前組件,但是被exclude了),當然 unmount 函數內包含 resetShapeFlag 操作

3、緩存策略

KeepAlive 組件的緩存策略是 LRU(last recently used)緩存策略

核心思想在于需要把當前訪問或渲染的組件作為最新一次渲染的組件,并且該組件在緩存修剪過程中始終是安全的,即不會被修剪。

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組件怎么使用”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

青冈县| 奉新县| 大安市| 凤城市| 江永县| 丹棱县| 富宁县| 武安市| 乌苏市| 通道| 龙里县| 金寨县| 腾冲县| 鹿泉市| 安徽省| 历史| 大关县| 大足县| 库尔勒市| 阳东县| 海林市| 潞城市| 张北县| 灵丘县| 兰州市| 彰化县| 芦山县| 阳朔县| 溧阳市| 三台县| 静乐县| 洛南县| 武安市| 萝北县| 独山县| 呼伦贝尔市| 松溪县| 台南县| 舟山市| 嘉黎县| 五寨县|