<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          04-vue3中keep-alive 實(shí)現(xiàn)原理解析

          共 3869字,需瀏覽 8分鐘

           ·

          2021-12-13 12:27

          你好我是劉小灰,這是我的第 04 篇原創(chuàng)文章。

          前言

          最近在做項(xiàng)目中遇到一個(gè)很奇葩的問題,就是給路由設(shè)置 keep-alive 就是不生效,就在寫這篇文章之前,這個(gè) bug 還是沒得到解決,所以打算看看 keep-alive 的實(shí)現(xiàn)邏輯,看是否從源碼中得到些解決這個(gè)bug的啟發(fā)。

          怎么去看源碼

          為了高效,我們不能漫無目的的去看源碼,那樣會很痛苦且沒有任何收獲,所以我建議大家在看源碼的時(shí)候最好是帶著問題去看源碼,這樣會更有目的性,效率自然會大大提高。

          從源碼中想得到什么

          對于 keep-alive 在沒有看源碼之前我有以下幾個(gè)疑問

          1. keep-alive 緩存的是什么?vnode 嗎?
          2. keep-alive 是把組件緩存到哪里了?
          3. keep-alive 的工作流程是什么?

          下面我們就來帶著問題來看源碼

          首先 keep-alive 也是一個(gè)組件,我們先找到組件的實(shí)例,源碼如下所示:

          const?KeepAliveImpl:?ComponentOptions?=?{
          ??name:?`KeepAlive`,
          ??__isKeepAlive:?true,
          ??props:?{
          ????include:?[String,?RegExp,?Array],
          ????exclude:?[String,?RegExp,?Array],
          ????max:?[String,?Number]
          ??},
          ??setup(props:?KeepAliveProps,?{?slots?}:?SetupContext)?{
          ??...
          ??}
          }

          首先我們可以看到 keep-alive 接受三個(gè)props分別是

          1. include 待緩存組件名稱
          2. exclude ?排除緩存的組件名稱
          3. max ? 允許最大緩存組件數(shù)

          接下來我們具體看下 setup 函數(shù),如下所示:

          setup(props:?KeepAliveProps,?{?slots?}:?SetupContext)?{
          ????//?獲取?keep-alive?實(shí)例?并保證一定存在
          ????const?instance?=?getCurrentInstance()!
          ????const?sharedContext?=?instance.ctx?as?KeepAliveContext
          ????if?(!sharedContext.renderer)?{
          ??????return?slots.default
          ????}
          ????//?map?用于緩存?組件的vnode
          ????const?cache:?Cache?=?new?Map()
          ????//?用于緩存組件的key
          ????const?keys:?Keys?=?new?Set()
          ????let?current:?VNode?|?null?=?null
          ????if?(__DEV__?||?__FEATURE_PROD_DEVTOOLS__)?{
          ??????;(instance?as?any).__v_cache?=?cache
          ????}
          ????...
          ??}

          從代碼中我們可以看出 keep-alive 是使用 Map 來緩存組件的,后面的代碼是關(guān)于鉤子函數(shù)的執(zhí)行和對于緩存組件的維護(hù)。

          40e7d4c9c070fa35327b727910185ad7.webp

          watch 后面有一段非常重要的代碼就是cacheSubtree函數(shù),用來緩存組件

          ?const?cacheSubtree?=?()?=>?{
          ??????if?(pendingCacheKey?!=?null)?{
          ????????cache.set(pendingCacheKey,?getInnerChild(instance.subTree))
          ??????}
          ????}

          我們再來看看 getInnerChild 返回的是什么,就證明了 keep-alive 緩存的是不是vnode

          function?getInnerChild(vnode:?VNode)?{
          ??return?vnode.shapeFlag?&?ShapeFlags.SUSPENSE???vnode.ssContent!?:?vnode
          }

          可以看出 keep-alive 緩存的就是組件的vnode。

          接下來我們再來看看 setup 的 return (渲染)函數(shù)

          return?()?=>?{
          ??????//?key
          ??????pendingCacheKey?=?null
          ??????if?(!slots.default)?{
          ????????return?null
          ??????}
          ??????const?children?=?slots.default()
          ??????const?rawVNode?=?children[0]
          ??????if?(children.length?>?1)?{
          ????????if?(__DEV__)?{
          ??????????warn(`KeepAlive?should?contain?exactly?one?component?child.`)
          ????????}
          ????????current?=?null
          ????????return?children
          ??????}?else?if?(
          ????????!isVNode(rawVNode)?||
          ????????(!(rawVNode.shapeFlag?&?ShapeFlags.STATEFUL_COMPONENT)?&&
          ??????????!(rawVNode.shapeFlag?&?ShapeFlags.SUSPENSE))
          ??????)?{
          ????????current?=?null
          ????????return?rawVNode
          ??????}
          ??????let?vnode?=?getInnerChild(rawVNode)
          ??????console.log('vnode',?vnode?===?rawVNode)
          ??????//使用vnode的type當(dāng)做key
          ??????const?comp?=?vnode.type?as?ConcreteComponent
          ??????const?name?=?getComponentName(
          ????????isAsyncWrapper(vnode)
          ????????????(vnode.type?as?ComponentOptions).__asyncResolved?||?{}
          ??????????:?comp
          ??????)
          ??????const?{?include,?exclude,?max?}?=?props
          ??????if?(
          ????????(include?&&?(!name?||?!matches(include,?name)))?||
          ????????(exclude?&&?name?&&?matches(exclude,?name))
          ??????)?{
          ????????current?=?vnode
          ????????return?rawVNode
          ??????}
          ??????const?key?=?vnode.key?==?null???comp?:?vnode.key
          ??????const?cachedVNode?=?cache.get(key)
          ??????if?(vnode.el)?{
          ????????vnode?=?cloneVNode(vnode)
          ????????if?(rawVNode.shapeFlag?&?ShapeFlags.SUSPENSE)?{
          ??????????rawVNode.ssContent?=?vnode
          ????????}
          ??????}
          ??????pendingCacheKey?=?key
          ??????if?(cachedVNode)?{
          ????????vnode.el?=?cachedVNode.el
          ????????vnode.component?=?cachedVNode.component
          ????????if?(vnode.transition)?{
          ??????????setTransitionHooks(vnode,?vnode.transition!)
          ????????}
          ????????vnode.shapeFlag?|=?ShapeFlags.COMPONENT_KEPT_ALIVE
          ????????keys.delete(key)
          ????????keys.add(key)
          ??????}?else?{
          ????????keys.add(key)
          ????????if?(max?&&?keys.size?>?parseInt(max?as?string,?10))?{
          ??????????pruneCacheEntry(keys.values().next().value)
          ????????}
          ??????}
          ??????vnode.shapeFlag?|=?ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
          ??????current?=?vnode
          ??????return?rawVNode
          ????}
          ??}

          主要是對插槽的一些判斷以及對props函數(shù)的處理,最后返回也是需要渲染的vnode

          總結(jié)

          從源碼中可以看出 keep-alive 就是通過 Map 緩存組件的 vnode 以及會給每個(gè)組件做一個(gè)標(biāo)識,這樣在渲染的時(shí)候就不會再執(zhí)行組件的初始化函數(shù)。


          瀏覽 211
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲精品中文字幕在线观看 | 亚洲色无码专区观看在线观 | 精品麻豆| 鸡巴在线观看 | 国产人妻性生交大片 |