<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>

          聊聊vue中的keep-alive

          共 18741字,需瀏覽 38分鐘

           ·

          2021-12-10 16:31

          一、什么是keep-alive?

          官方介紹就是:<keep-alive> 包裹動(dòng)態(tài)組件時(shí),會(huì)緩存不活動(dòng)的組件實(shí)例,而不是銷毀它們。和 <transition> 相似,<keep-alive> 是一個(gè)抽象組件:它自身不會(huì)渲染一個(gè) DOM 元素,也不會(huì)出現(xiàn)在組件的父組件鏈中。

          當(dāng)組件在 <keep-alive> 內(nèi)被切換時(shí),它的 mounted 和 unmounted 生命周期鉤子不會(huì)被調(diào)用,取而代之的是 activated 和 deactivated

          簡(jiǎn)單理解就是說我們可以把一些不常變動(dòng)的組件或者需要緩存的組件用<keep-alive>包裹起來,這樣<keep-alive>就會(huì)幫我們把組件保存在內(nèi)存中,而不是直接的銷毀,這樣做可以保留組件的狀態(tài)或避免多次重新渲染,以提高頁面性能

          二、使用用法

          我們先根據(jù)官方文檔來回顧一下<keep-alive>組件的具體用法,如下:

          <keep-alive>組件可接收三個(gè)屬性:

          • Props:
            • include - string | RegExp | Array。只有名稱匹配的組件會(huì)被緩存。
            • exclude - string | RegExp | Array。任何名稱匹配的組件都不會(huì)被緩存。
            • max - number | string。最多可以緩存多少組件實(shí)例。
          • 用法:

            include 和 exclude prop 允許組件有條件地緩存。二者都可以用逗號(hào)分隔字符串、正則表達(dá)式或一個(gè)數(shù)組來表示:

            <!-- 逗號(hào)分隔字符串 -->
            <keep-alive include="a,b">
              <component :is="view"></component>
            </keep-alive>

            
            <!-- regex (使用 `v-bind`) -->
            <keep-alive :include="/a|b/">
              <component :is="view"></component>
            </keep-alive>

            
            <!-- Array (使用 `v-bind`) -->
            <keep-alive :include="['a', 'b']">
              <component :is="view"></component>
            </keep-alive>

          匹配首先檢查組件自身的 name 選項(xiàng),如果 name 選項(xiàng)不可用,則匹配它的局部注冊(cè)名稱 (父組件 components 選項(xiàng)的鍵值)。匿名組件不能被匹配。

          max表示最多可以緩存多少組件實(shí)例。一旦這個(gè)數(shù)字達(dá)到了,在新實(shí)例被創(chuàng)建之前,已緩存組件中最久沒有被訪問的實(shí)例會(huì)被銷毀掉。


            <keep-alive :max="10">
              <component :is="view"></component>
            </keep-alive>

          在這里簡(jiǎn)單介紹一個(gè)日常項(xiàng)目中有可能出現(xiàn)的場(chǎng)景并使用keep-alive來實(shí)現(xiàn)按需控制緩存 場(chǎng)景:當(dāng)我們從首頁–>列表頁–>商品詳情頁–>返回到列表頁(需要緩存)–>返回到首頁(需要緩存)–>再次進(jìn)入列表頁(不需要緩存)

          1. 在路由meta對(duì)象里定義兩個(gè)值:

            keepAlive:這個(gè)路由是否需要緩存

            deepth:代表頁面之間的前進(jìn)后退的層級(jí)關(guān)系

             
             {
                 path'*',
                 name'Home',
                 component() => import(/* webpackPreload: true */ '@/views/home'),
                 meta: {
                   keepAlivetrue,
                   deepth1
                 }
               },
               {
                 path'/list',
                 name'list',
                 component() => import('@/views/list'),
                 meta: {
                   keepAlivetrue,
                   deepth2
                 }
               },
               {
                 path'/detail',
                 name'Detail',
                 component() => import('@/views/detail'),
                 meta: {
                   keepAlivetrue,
                   deepth3
                 }
               },
               
          1. 監(jiān)聽路由動(dòng)態(tài)控制需要緩存的值
             //3x版本router-view不允許直接寫在keep-alive里面,需注意
             <template>
               <div id="app">
                 <keep-alive :include="include">
                   <router-view v-if="$route.meta.keepAlive" />
                 </keep-alive>
                 <router-view v-if="!$route.meta.keepAlive" />
               </div>

             </template>
             
             export default {
               data() {
                 return {
                   include: []
                 };
               },
               watch: {
                 $route(to, from) {
                   /
          / 如果要to(進(jìn)入)的頁面是需要keepAlive緩存的,把name push進(jìn)include數(shù)組中
                   if (to.meta.keepAlive) {
                     !this.include.includes(to.name) && this.include.push(to.name);
                   }
                   /
          / 如果 要 form(離開) 的頁面是 keepAlive緩存的,
                   /
          / 再根據(jù) deepth 來判斷是前進(jìn)還是后退
                   /
          / 如果是后退:
                   if (from.meta.keepAlive && to.meta.deepth < from.meta.deepth) {
                     const index = this.include.indexOf(from.name);
                     index !== -1 && this.include.splice(index, 1);
                   }
                 }
               }
             };
             

          以上場(chǎng)景在通過監(jiān)聽路由,動(dòng)態(tài)的設(shè)置了在第一次進(jìn)入并回退回來時(shí)的緩存實(shí)現(xiàn),并在第二次進(jìn)入時(shí)重新開始進(jìn)行新一輪緩存設(shè)置,實(shí)現(xiàn)動(dòng)態(tài)控制緩存。

          三、實(shí)現(xiàn)

          <keep-alive>組件的定義位于源碼的 src/core/components/keep-alive.js 文件中,本文參考:https://unpkg.com/browse/[email protected]/src/core/components/keep-alive.js,感興趣的可以自行查看,下面只展示部分代碼。

          const patternTypes: Array<Function> = [StringRegExpArray]

          export default {
            name'keep-alive',
            abstracttrue,

            props: {
              include: patternTypes,
              exclude: patternTypes,
              max: [StringNumber]
            },

            created () {
              this.cache = Object.create(null)
              this.keys = []
            },

            destroyed () {
              for (const key in this.cache) {
                pruneCacheEntry(this.cache, key, this.keys)
              }
            },

            mounted () {
              this.$watch('include', val => {
                pruneCache(this, name => matches(val, name))
              })
              this.$watch('exclude', val => {
                pruneCache(this, name => !matches(val, name))
              })
            },

            render () {
              const slot = this.$slots.default
              const vnode: VNode = getFirstComponentChild(slot)
              const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
              if (componentOptions) {
                // check pattern
                const name: ?string = getComponentName(componentOptions)
                const { include, exclude } = this
                if (
                  // not included
                  (include && (!name || !matches(include, name))) ||
                  // excluded
                  (exclude && name && matches(exclude, name))
                ) {
                  return vnode
                }

                const { cache, keys } = this
                const key: ?string = vnode.key == null
                  // same constructor may get registered as different local components
                  // so cid alone is not enough (#3269)
                  ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
                  : vnode.key
                if (cache[key]) {
                  vnode.componentInstance = cache[key].componentInstance
                  // make current key freshest
                  remove(keys, key)
                  keys.push(key)
                } else {
                  cache[key] = vnode
                  keys.push(key)
                  // prune oldest entry
                  if (this.max && keys.length > parseInt(this.max)) {
                    pruneCacheEntry(cache, keys[0], keys, this._vnode)
                  }
                }

                vnode.data.keepAlive = true
              }
              return vnode || (slot && slot[0])
            }
          }

          開始我們先從created鉤子開始進(jìn)行分析:

          created:

          在 created 鉤子函數(shù)里定義并初始化了兩個(gè)屬性: this.cache 和 this.keys

          created () {
              this.cache = Object.create(null)
              this.keys = []
          }

          this.cache是一個(gè)對(duì)象,用來存儲(chǔ)需要緩存的組件。

          this.keys是一個(gè)數(shù)組,用來存儲(chǔ)每個(gè)需要緩存的組件的key,即對(duì)應(yīng)this.cache對(duì)象中的鍵值。

          destroyed:

          當(dāng)<keep-alive>組件被銷毀時(shí),此時(shí)會(huì)調(diào)用destroyed鉤子函數(shù),在該鉤子函數(shù)里會(huì)遍歷this.cache對(duì)象,然后將那些被緩存的并且當(dāng)前沒有處于被渲染狀態(tài)的組件都銷毀掉并將其從this.cache對(duì)象中刪除。如下:

          destroyed () {
              for (const key in this.cache) {
                pruneCacheEntry(this.cache, key, this.keys)
              }
          }

          上面用到了pruneCacheEntry函數(shù):

          function pruneCacheEntry (
            cache: VNodeCache,
            key: string,
            keys: Array<string>,
            current?: VNode
          {
            const cached = cache[key]
            /* 判斷當(dāng)前沒有處于被渲染狀態(tài)的組件,將其銷毀*/
            if (cached && (!current || cached.tag !== current.tag)) {
              cached.componentInstance.$destroy()
            }
            cache[key] = null
            remove(keys, key)
          }
          mounted:

          mounted鉤子函數(shù)中觀測(cè) include 和 exclude 的變化,如下:

          mounted () {
              this.$watch('include', val => {
                pruneCache(this, name => matches(val, name))
              })
              this.$watch('exclude', val => {
                pruneCache(this, name => !matches(val, name))
              })
            }

          如果include 或exclude 發(fā)生了變化,即表示定義需要緩存的組件的規(guī)則或者不需要緩存的組件的規(guī)則發(fā)生了變化,那么就執(zhí)行pruneCache函數(shù)

          function pruneCache (keepAliveInstance: any, filter: Function{
            const { cache, keys, _vnode } = keepAliveInstance
            for (const key in cache) {
              const cachedNode: ?VNode = cache[key]
              if (cachedNode) {
                const name: ?string = getComponentName(cachedNode.componentOptions)
                if (name && !filter(name)) {
                  pruneCacheEntry(cache, key, keys, _vnode)
                }
              }
            }
          }

          在該函數(shù)內(nèi)對(duì)this.cache對(duì)象進(jìn)行遍歷,取出每一項(xiàng)的name值,用其與新的緩存規(guī)則進(jìn)行匹配,如果匹配不上,則表示在新的緩存規(guī)則下該組件已經(jīng)不需要被緩存,則調(diào)用pruneCacheEntry函數(shù)將這個(gè)已經(jīng)不需要緩存的組件實(shí)例先銷毀掉,然后再將其從this.cache對(duì)象中刪除。

          render:

          <keep-alive> 為一個(gè)函數(shù)式組件。執(zhí)行組件渲染的時(shí)候,就會(huì)執(zhí)行到這個(gè) render 函數(shù)

          render () {
             /* 獲取默認(rèn)插槽中的第一個(gè)組件節(jié)點(diǎn) */
              const slot = this.$slots.default
              const vnode: VNode = getFirstComponentChild(slot)
              const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
              if (componentOptions) {
                /* 獲取該組件節(jié)點(diǎn)的名稱 */
                const name: ?string = getComponentName(componentOptions) 
                /* 如果name與include規(guī)則不匹配或者與exclude規(guī)則匹配則表示不緩存,直接返回vnode */
                const { include, exclude } = this
                if (
                  // not included
                  (include && (!name || !matches(include, name))) ||
                  // excluded
                  (exclude && name && matches(exclude, name))
                ) {
                  return vnode
                }
             /*-----需要走緩存-----*/
                const { cache, keys } = this
                /* 獲取組件的key */
                const key: ?string = vnode.key == null
                  ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
                  : vnode.key
                /* 如果命中緩存,則直接從緩存中拿 vnode 的組件實(shí)例 */
                if (cache[key]) {
                  vnode.componentInstance = cache[key].componentInstance
                  // make current key freshest 調(diào)整該組件key的順序,將其從原來的地方刪掉并重新放在最后一個(gè) 
                  remove(keys, key)
                  keys.push(key)
                } else {
                  /* 如果沒有命中緩存,則將其設(shè)置進(jìn)緩存 */
                  cache[key] = vnode
                  keys.push(key)
                  // prune oldest entry 如果配置了max并且緩存的長(zhǎng)度超過了this.max,則從緩存中刪除第一個(gè)
                  if (this.max && keys.length > parseInt(this.max)) {
                    pruneCacheEntry(cache, keys[0], keys, this._vnode)
                  }
                }

                vnode.data.keepAlive = true
              }
              return vnode || (slot && slot[0])
            }
          1. 獲取默認(rèn)插槽中的第一個(gè)組件節(jié)點(diǎn)。 由于我們也是在 <keep-alive> 標(biāo)簽內(nèi)部寫 DOM,所以可以先獲取到它的默認(rèn)插槽,然后再獲取到它的第一個(gè)子節(jié)點(diǎn)。<keep-alive> 只處理第一個(gè)子元素,所以一般和它搭配使用的有 component 動(dòng)態(tài)組件或者是 router-view
          2. 獲取該組件節(jié)點(diǎn)的名稱,然后用組件名稱跟 includeexclude 中的匹配規(guī)則去匹配,如果組件名稱與 include 規(guī)則不匹配或者與 exclude 規(guī)則匹配,則表示不緩存該組件,直接返回這個(gè)組件的 vnode,否則的話走下一步緩存
          3. 獲取組件的key值:拿到key值后去this.cache對(duì)象中去尋找是否有該值,如果有則表示該組件有緩存,即命中緩存,直接從緩存中拿 vnode 的組件實(shí)例,此時(shí)重新調(diào)整該組件key的順序,將其從原來的地方刪掉并重新放在this.keys中最后一個(gè)。沒有繼續(xù)下一步
          4. 表明該組件還沒有被緩存過,則以該組件的key為鍵,組件vnode為值,將其存入this.cache中,并且把key存入this.keys中。此時(shí)再判斷this.keys中緩存組件的數(shù)量是否超過了設(shè)置的最大緩存數(shù)量值this.max,如果超過了,則把第一個(gè)緩存組件刪掉
          5. 最后設(shè)置 vnode.data.keepAlive = true ,最后將vnode返回

          總結(jié)

          上面介紹了Vue中的內(nèi)置組件<keep-alive>組件以及<keep-alive>組件的具體用法。同時(shí)也分析了<keep-alive>組件的一些內(nèi)部原理,底下則是個(gè)人對(duì)于keep-alive的運(yùn)行的一些個(gè)人理解總結(jié): keep-alive主要作用是緩存vnode,大概可以分為三個(gè)運(yùn)行階段

          1. 初始未存在緩存階段,在created鉤子定義了用于保存vnode的cache對(duì)象以及保存緩存了的vnode列表keys,用于數(shù)據(jù)的存儲(chǔ)。mounted鉤子觀測(cè)監(jiān)聽 include 和 exclude 的變化,進(jìn)行緩存vnode的變化更新,最后調(diào)用render進(jìn)行組件vnode的第一次緩存設(shè)置。 因?yàn)榫彺嬉恍┚唧w業(yè)務(wù)功能的組件vnode對(duì)于我們來說,什么時(shí)候開始緩存、何時(shí)銷毀以及何時(shí)運(yùn)行render重新刷新,開發(fā)者是沒有vnode的直接控制能力,所以需要定義一些屬性include、exclude、max來進(jìn)行一個(gè)判斷,用于更準(zhǔn)確的對(duì)需要緩存的vnode進(jìn)行控制處理。
          2. 已緩存的更新階段,調(diào)用render函數(shù)直接從cache對(duì)象返回已緩存的vnode,避免了多次的重新渲染,來提高頁面性能。
          3. 銷毀階段,destroyed鉤子定義組件銷毀時(shí)清除那些被緩存的并且當(dāng)前沒有處于被渲染狀態(tài)的組件。銷毀組件時(shí),對(duì)于緩存的vnode對(duì)象,不清除的話應(yīng)該也會(huì)造成一些內(nèi)存上的占用或者內(nèi)存泄漏的問題,所以在銷毀時(shí)需要進(jìn)行一個(gè)清除緩存的操作。

          以上就是關(guān)于keep-alive的相關(guān)內(nèi)容,希望每個(gè)看完的同學(xué)都有自己收獲,完結(jié) ??????

          瀏覽 81
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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>
                  一级黄色片视频 | 豆花视频理论在线播放 | 亚洲熟女一区二区 | 成人天堂网 | 黄片入口|