最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

解讀Vue3中keep-alive和動態(tài)組件的實現(xiàn)邏輯

 更新時間:2023年05月20日 15:57:01   作者:JonnyLan  
這篇文章主要介紹了Vue3中keep-alive和動態(tài)組件的實現(xiàn)邏輯,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

keep-alive組件是Vue提供的組件,它可以緩存組件實例,在某些情況下避免了組件的掛載和卸載,在某些場景下非常實用。

例如最近我們遇到了一種場景,某個組件上傳較大的文件是個耗時的操作,如果上傳的時候切換到其他頁面內(nèi)容,組件會被卸載,對應(yīng)的下載也會被取消。此時可以用keep-alive組件包裹這個組件,在切換到其他頁面時該組件仍然可以繼續(xù)上傳文件,切換回來也可以看到上傳進度。

keep-alive

渲染子節(jié)點

const KeepAliveImpl: ComponentOptions = {
  name: `KeepAlive`,
  setup(props: KeepAliveProps, { slots }: SetupContext) {
    // 需要渲染的子樹VNode
    let current: VNode | null = null
    return () => {
      // 獲取子節(jié)點, 由于Keep-alive只能有一個子節(jié)點,直接取第一個子節(jié)點
      const children = slots.default()
      const rawVNode = children[0]
      // 標記 | ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE,這個組件是`keep-alive`組件, 這個標記 不走 unmount邏輯,因為要被緩存的
      vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
      // 記錄當前子節(jié)點
      current = vnode
      // 返回子節(jié)點,代表渲染這個子節(jié)點
      return rawVNode
    }
  }
}

組件的setup返回函數(shù),這個函數(shù)就是組件的渲染函數(shù);

keep-alive是一個虛擬節(jié)點不需要渲染,只需要渲染子節(jié)點,所以函數(shù)只需要返回子節(jié)點VNode就行了。

緩存功能

定義存儲緩存數(shù)據(jù)的Map, 所有的緩存鍵值數(shù)組Keys,代表當前子組件的緩存鍵值pendingCacheKey;

const cache = new Map()
const keys: Keys = new Set()
let pendingCacheKey: CacheKey | null = null

渲染函數(shù)中獲取子樹節(jié)點VNode的key, 緩存cache中查看是否有key對應(yīng)的緩存節(jié)點

const key = vnode.key
const cachedVNode = cache.get(key)

key是生成子節(jié)點的渲染函數(shù)時添加的,一般情況下就是0,1,2,…這些數(shù)字。

記錄下點前的key

pendingCacheKey = key

如果有找到緩存的cachedVNode節(jié)點,將緩存的cachedVNode節(jié)點的組件實例和節(jié)點元素 復制給新的VNode節(jié)點。沒有找到就先將當前子樹節(jié)點VNode的pendingCacheKey加入到Keys中。

if (cachedVNode) {
? // 復制節(jié)點
? vnode.el = cachedVNode.el
? vnode.component = cachedVNode.component
? // 標記 | ShapeFlags.COMPONENT_KEPT_ALIVE,這個組件是復用的`VNode`, 這個標記 不走 mount邏輯
? vnode.shapeFlag |= ShapeFlags.COMPONENT_KEPT_ALIVE
} else {
? // 添加 pendingCacheKey
? keys.add(key)
}
  • 問題: 這里為什么不實現(xiàn)在cache中存入{pendingCacheKey: vnode}呢?
  • 答案: 這里其實可以加入這邏輯,只是官方間隔這個邏輯延后實現(xiàn)了, 我覺得沒什么差別。

在組件掛載onMounted和更新onUpdated的時候添加/更新緩存

onMounted(cacheSubtree)
onUpdated(cacheSubtree)
const cacheSubtree = () => {
? if (pendingCacheKey != null) {
? ? // 添加/更新緩存
? ? cache.set(pendingCacheKey, instance.subTree)
? }
}

全部代碼

const KeepAliveImpl: ComponentOptions = {
? name: `KeepAlive`,
? setup(props: KeepAliveProps, { slots }: SetupContext) {
? ? let current: VNode | null = null
? ? // 緩存的一些數(shù)據(jù)
? ? const cache = new Map()
? ? const keys: Keys = new Set()
? ? let pendingCacheKey: CacheKey | null = null
? ? // 更新/添加緩存數(shù)據(jù)
? ? const cacheSubtree = () => {
? ? ? if (pendingCacheKey != null) {
? ? ? ? // 添加/更新緩存
? ? ? ? cache.set(pendingCacheKey, instance.subTree)
? ? ? }
? ? }
? ? // 監(jiān)聽生命周期
? ? onMounted(cacheSubtree)
? ? onUpdated(cacheSubtree)
? ? return () => {
? ? ? const children = slots.default()
? ? ? const rawVNode = children[0]
? ? ? // 獲取緩存
? ? ? const key = rawVNode.key
? ? ? const cachedVNode = cache.get(key)
? ? ? pendingCacheKey = key
? ? ? if (cachedVNode) {
? ? ? ? // 復用DOM和組件實例
? ? ? ? rawVNode.el = cachedVNode.el
? ? ? ? rawVNode.component = cachedVNode.component
? ? ? } else {
? ? ? ? // 添加 pendingCacheKey
? ? ? ? keys.add(key)
? ? ? }
? ? ? rawVNode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
? ? ? current = rawVNode
? ? ? return rawVNode
? ? }
? }
}

至此,通過cache實現(xiàn)了DOM和組件實例的緩存。

keep-alive的patch復用邏輯

我們知道生成VNode后是進行patch邏輯,生成DOM。

const processComponent = (
? n1: VNode | null,
? n2: VNode,
? container: RendererElement,
? anchor: RendererNode | null,
? parentComponent: ComponentInternalInstance | null,
? parentSuspense: SuspenseBoundary | null,
? isSVG: boolean,
? slotScopeIds: string[] | null,
? optimized: boolean
) => {
? n2.slotScopeIds = slotScopeIds
? if (n1 == null) {
? ? if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
? ? ? ;(parentComponent!.ctx as KeepAliveContext).activate(
? ? ? ? n2,
? ? ? ? container,
? ? ? ? anchor,
? ? ? ? isSVG,
? ? ? ? optimized
? ? ? )
? ? } else {
? ? ? mountComponent(
? ? ? ? n2,
? ? ? ? container,
? ? ? ? anchor,
? ? ? ? parentComponent,
? ? ? ? parentSuspense,
? ? ? ? isSVG,
? ? ? ? optimized
? ? ? )
? ? }
? }
}

processComponent處理組件邏輯的時候如果是復用ShapeFlags.COMPONENT_KEPT_ALIVE則走的父組件keep-alive的activate方法;

const unmount: UnmountFn = (
? vnode,
? parentComponent,
? parentSuspense,
? doRemove = false,
? optimized = false
) => {
? const {
? ? type,
? ? props,
? ? ref,
? ? children,
? ? dynamicChildren,
? ? shapeFlag,
? ? patchFlag,
? ? dirs
? } = vnode
? if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
? ? ;(parentComponent!.ctx as KeepAliveContext).deactivate(vnode)
? ? return
? }
}

unmount卸載的keep-alive組件ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE時調(diào)用父組件keep-alive的deactivate方法。

總結(jié):keep-alive組件的復用和卸載被activate方法和deactivate方法接管了。

active邏輯

sharedContext.activate = (vnode, container, anchor, isSVG, optimized) => {
? const instance = vnode.component!
? // 1. 直接掛載DOM
? move(vnode, container, anchor, MoveType.ENTER, parentSuspense)
? // 2. 更新prop
? patch(
? ? instance.vnode,
? ? vnode,
? ? container,
? ? anchor,
? ? instance,
? ? parentSuspense,
? ? isSVG,
? ? vnode.slotScopeIds,
? ? optimized
? )
? // 3. 異步執(zhí)行onVnodeMounted 鉤子函數(shù)
? queuePostRenderEffect(() => {
? ? instance.isDeactivated = false
? ? if (instance.a) {
? ? ? invokeArrayFns(instance.a)
? ? }
? ? const vnodeHook = vnode.props && vnode.props.onVnodeMounted
? ? if (vnodeHook) {
? ? ? invokeVNodeHook(vnodeHook, instance.parent, vnode)
? ? }
? }, parentSuspense)
}
  • 直接掛載DOM
  • 更新prop
  • 異步執(zhí)行onVnodeMounted鉤子函數(shù)

deactivate邏輯

const storageContainer = createElement('div')
sharedContext.deactivate = (vnode: VNode) => {
? const instance = vnode.component!
? // 1. 把DOM移除,掛載在一個新建的div下
? move(vnode, storageContainer, null, MoveType.LEAVE, parentSuspense)
? // 2. 異步執(zhí)行onVnodeUnmounted鉤子函數(shù)
? queuePostRenderEffect(() => {
? ? if (instance.da) {
? ? ? invokeArrayFns(instance.da)
? ? }
? ? const vnodeHook = vnode.props && vnode.props.onVnodeUnmounted
? ? if (vnodeHook) {
? ? ? invokeVNodeHook(vnodeHook, instance.parent, vnode)
? ? }
? ? instance.isDeactivated = true
? }, parentSuspense)
? if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
? ? // Update components tree
? ? devtoolsComponentAdded(instance)
? }
}
  • 把DOM移除,掛載在一個新建的div下
  • 異步執(zhí)行onVnodeUnmounted鉤子函數(shù)

問題:舊節(jié)點的deactivate和新節(jié)點的active誰先執(zhí)行

答案:舊節(jié)點的deactivate先執(zhí)行,新節(jié)點的active后執(zhí)行。

keep-alive的unmount邏輯

將cache中出當前子樹VNode節(jié)點外的所有卸載,當前組件取消keep-alive的標記, 這樣當前子樹VNode會隨著keep-alive的卸載而卸載。

onBeforeUnmount(() => {
? cache.forEach(cached => {
? ? const { subTree, suspense } = instance
? ? const vnode = getInnerChild(subTree)
? ? if (cached.type === vnode.type) {
? ? ? // 當然組件先取消`keep-alive`的標記,能正在執(zhí)行unmout
? ? ? resetShapeFlag(vnode)
? ? ? // but invoke its deactivated hook here
? ? ? const da = vnode.component!.da
? ? ? da && queuePostRenderEffect(da, suspense)
? ? ? return
? ? }
? ? // 每個緩存的VNode,執(zhí)行unmount方法
? ? unmount(cached)
? })
})
<!-- 執(zhí)行unmount -->
function unmount(vnode: VNode) {
? ? // 取消`keep-alive`的標記,能正在執(zhí)行unmout
? ? resetShapeFlag(vnode)
? ? // unmout
? ? _unmount(vnode, instance, parentSuspense)
}

keep-alive卸載了,其緩存的DOM也將被卸載。

keep-alive緩存的配置include,exclude和max

這部分知道邏輯就好了,不做代碼分析。

  • 組件名稱在include中的組件會被緩存;
  • 組件名稱在exclude中的組件不會被緩存;
  • 規(guī)定緩存的最大數(shù)量,如果超過了就把緩存的最前面的內(nèi)容刪除。

動態(tài)組件

使用方法

<keep-alive>
? <component is="A"></component>
</keep-alive>

渲染函數(shù)

resolveDynamicComponent("A")

resolveDynamicComponent的邏輯

export function resolveDynamicComponent(component: unknown): VNodeTypes {
? if (isString(component)) {
? ? return resolveAsset(COMPONENTS, component, false) || component
? }
}
function resolveAsset(
? type,
? name,
? warnMissing = true,
? maybeSelfReference = false
) {
? const res =
? ? // local registration
? ? // check instance[type] first which is resolved for options API
? ? resolve(instance[type] || Component[type], name) ||
? ? // global registration
? ? resolve(instance.appContext[type], name)
? return res
}

和指令一樣,resolveDynamicComponent就是根據(jù)名稱尋找局部或者全局注冊的組件,然后渲染對應(yīng)的組件。

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 如何用VUE和Canvas實現(xiàn)雷霆戰(zhàn)機打字類小游戲

    如何用VUE和Canvas實現(xiàn)雷霆戰(zhàn)機打字類小游戲

    這篇文章主要介紹了如何用VUE和Canvas實現(xiàn)雷霆戰(zhàn)機打字類小游戲,麻雀雖小,五臟俱全,對游戲感興趣的同學,可以參考下,研究里面的原理和實現(xiàn)方法
    2021-04-04
  • Vue3中的事件總線詳解

    Vue3中的事件總線詳解

    在Vue3中,推薦使用CompositionAPI和第三方庫mitt來實現(xiàn)事件總線,而不是Vue2中使用Vue實例的方式
    2026-01-01
  • vue使用v-model實現(xiàn)父子組件傳值之子父組件同步更新方式

    vue使用v-model實現(xiàn)父子組件傳值之子父組件同步更新方式

    文章主要介紹了Vue2和Vue3中父子組件間通過props和emit實現(xiàn)雙向綁定的方法,Vue2中,子組件通過props接收值,通過$emit觸發(fā)change事件修改父組件數(shù)據(jù);Vue3中方法類似,但語法略有不同
    2026-05-05
  • 基于Vue結(jié)合ElementUI的換膚解決方案

    基于Vue結(jié)合ElementUI的換膚解決方案

    本文將介紹幾種基于Vue、Element-UI的換膚實現(xiàn)方案,力爭通俗易懂,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • Vue 通過自定義指令回顧v-內(nèi)置指令(小結(jié))

    Vue 通過自定義指令回顧v-內(nèi)置指令(小結(jié))

    這篇文章主要介紹了Vue 通過自定義指令回顧v-內(nèi)置指令(小結(jié)),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09
  • vue+elementui實現(xiàn)選項卡功能

    vue+elementui實現(xiàn)選項卡功能

    這篇文章主要為大家詳細介紹了vue+elementui實現(xiàn)選項卡功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 詳解Vue整合axios的實例代碼

    詳解Vue整合axios的實例代碼

    本篇文章主要介紹了詳解Vue整合axios的實例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • vue3全局組件自動注冊功能實現(xiàn)

    vue3全局組件自動注冊功能實現(xiàn)

    本文主要講述vue3的全局公共組件的自動注冊功能,本文分步驟結(jié)合實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2023-02-02
  • vue實現(xiàn)websocket客服聊天功能

    vue實現(xiàn)websocket客服聊天功能

    這篇文章主要為大家詳細介紹了vue實現(xiàn)websocket客服聊天功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • VUE3數(shù)據(jù)的偵聽超詳細講解

    VUE3數(shù)據(jù)的偵聽超詳細講解

    在Vue3中watch特性進行了一些改變和優(yōu)化,與computed不同,watch通常用于監(jiān)聽數(shù)據(jù)的變化,并執(zhí)行一些副作用,這篇文章主要給大家介紹了關(guān)于VUE3數(shù)據(jù)偵聽的相關(guān)資料,需要的朋友可以參考下
    2023-12-12

最新評論

望江县| 德化县| 福鼎市| 铁力市| 湖北省| 名山县| 吉林市| 宝清县| 舒城县| 贵溪市| 白河县| 四平市| 安顺市| 延庆县| 江川县| 拜城县| 阜新市| 黎城县| 迁西县| 武穴市| 拉萨市| 健康| 陇西县| 简阳市| 平和县| 普安县| 纳雍县| 综艺| 牡丹江市| 江口县| 双江| 恩施市| 乌拉特前旗| 无锡市| 资溪县| 济南市| 葵青区| 灵川县| 宁明县| 巢湖市| 汕头市|