欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue?keep-alive的實(shí)現(xiàn)原理分析

 更新時(shí)間:2022年04月15日 15:42:40   作者:yydounai  
這篇文章主要介紹了Vue?keep-alive的實(shí)現(xiàn)原理分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

keep-alive的實(shí)現(xiàn)原理

使用vue的時(shí)候,想必大家都是用過(guò)keep-alive,其作用就是緩存頁(yè)面以及其狀態(tài)。使用了這么久vue只知道如何使用但不明白其中原理,昨天翻看實(shí)現(xiàn)代碼,這里做個(gè)筆記。

這里以vue3為例

整個(gè)組件的源碼為:

const KeepAliveImpl = {
  name: `KeepAlive`,
 
  // Marker for special handling inside the renderer. We are not using a ===
  // check directly on KeepAlive in the renderer, because importing it directly
  // would prevent it from being tree-shaken.
  __isKeepAlive: true,
 
  props: {
    include: [String, RegExp, Array],
    exclude: [String, RegExp, Array],
    max: [String, Number]
  },
 
  setup(props: KeepAliveProps, { slots }: SetupContext) {
    const cache: Cache = new Map()
    const keys: Keys = new Set()
    let current: VNode | null = null
 
    const instance = getCurrentInstance()!
    // console.log('instance',instance)
    // KeepAlive communicates with the instantiated renderer via the "sink"
    // where the renderer passes in platform-specific functions, and the
    // KeepAlive instance exposes activate/deactivate implementations.
    // The whole point of this is to avoid importing KeepAlive directly in the
    // renderer to facilitate tree-shaking.
    const sink = instance.sink as KeepAliveSink
    const {
      renderer: {
        move,
        unmount: _unmount,
        options: { createElement }
      },
      parentSuspense
    } = sink
    const storageContainer = createElement('div')
    // console.log('sink',sink)
    sink.activate = (vnode, container, anchor) => {
      move(vnode, container, anchor, MoveType.ENTER, parentSuspense)
      queuePostRenderEffect(() => {
        const component = vnode.component!
        component.isDeactivated = false
        if (component.a !== null) {
          invokeHooks(component.a)
        }
      }, parentSuspense)
    }
 
    sink.deactivate = (vnode: VNode) => {
      move(vnode, storageContainer, null, MoveType.LEAVE, parentSuspense)
      queuePostRenderEffect(() => {
        const component = vnode.component!
        if (component.da !== null) {
          invokeHooks(component.da)
        }
        component.isDeactivated = true
      }, parentSuspense)
    }
 
    function unmount(vnode: VNode) {
      // reset the shapeFlag so it can be properly unmounted
      vnode.shapeFlag = ShapeFlags.STATEFUL_COMPONENT
      _unmount(vnode, instance, parentSuspense)
    }
 
    function pruneCache(filter?: (name: string) => boolean) {
      cache.forEach((vnode, key) => {
        const name = getName(vnode.type as Component)
        if (name && (!filter || !filter(name))) {
          pruneCacheEntry(key)
        }
      })
    }
 
    function pruneCacheEntry(key: CacheKey) {
      const cached = cache.get(key) as VNode
      if (!current || cached.type !== current.type) {
        unmount(cached)
      } else if (current) {
        // current active instance should no longer be kept-alive.
        // we can't unmount it now but it might be later, so reset its flag now.
        current.shapeFlag = ShapeFlags.STATEFUL_COMPONENT
      }
      cache.delete(key)
      keys.delete(key)
    }
 
    watch(
      () => [props.include, props.exclude],
      ([include, exclude]) => {
        include && pruneCache(name => matches(include, name))
        exclude && pruneCache(name => matches(exclude, name))
      },
      { lazy: true }
    )
 
    onBeforeUnmount(() => {
      cache.forEach(unmount)
    })
 
    return () => {
      if (!slots.default) {
        return null
      }
 
      const children = slots.default()
      let vnode = children[0]
      if (children.length > 1) {
        if (__DEV__) {
          warn(`KeepAlive should contain exactly one component child.`)
        }
        current = null
        return children
      } else if (
        !isVNode(vnode) ||
        !(vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT)
      ) {
        current = null
        return vnode
      }
 
      const comp = vnode.type as Component
      const name = getName(comp)
      const { include, exclude, max } = props
 
      if (
        (include && (!name || !matches(include, name))) ||
        (exclude && name && matches(exclude, name))
      ) {
        return vnode
      }
 
      const key = vnode.key == null ? comp : vnode.key
      const cached = cache.get(key)
 
      // clone vnode if it's reused because we are going to mutate it
      if (vnode.el) {
        vnode = cloneVNode(vnode)
      }
      cache.set(key, vnode)
      if (cached) {
        // copy over mounted state
        vnode.el = cached.el
        vnode.anchor = cached.anchor
        vnode.component = cached.component
        if (vnode.transition) {
          // recursively update transition hooks on subTree
          setTransitionHooks(vnode, vnode.transition!)
        }
        // avoid vnode being mounted as fresh
        vnode.shapeFlag |= ShapeFlags.COMPONENT_KEPT_ALIVE
        // make this key the freshest
        keys.delete(key)
        keys.add(key) 
      } else {
        keys.add(key)
        // prune oldest entry
        if (max && keys.size > parseInt(max as string, 10)) { 
          pruneCacheEntry(Array.from(keys)[0])
        }
      }
      // avoid vnode being unmounted
      vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
      current = vnode
      return vnode
    }
  }
}

很容易看出keep-alive其實(shí)就是vue自己封裝的一個(gè)組件,和普通組件一樣。

再講keep-alive組件前先了解下vue組件的整個(gè)渲染

大致流程如下

keep-alive生命周期

組件掛載:

調(diào)用setupStatefulComponent函數(shù)觸發(fā)組件setup方法,其中組件的setup方法核心代碼其實(shí)就幾行:

return () => {
    const children = slots.default()
    let vnode = children[0]
    cache.set(key, vnode)
 
    if (cached) {
      vnode.el = cached.el
      vnode.anchor = cached.anchor
      vnode.component = cached.component
      vnode.shapeFlag |= ShapeFlags.COMPONENT_KEPT_ALIVE
      keys.delete(key)
      keys.add(key) 
    } else {
      keys.add(key)
    }
    return vnode
}

主要邏輯為三:

1.確認(rèn)需要渲染的slot、

2.將其狀態(tài)置入緩存或讀取已存在的緩存、

3.返回slot對(duì)應(yīng)的vnode,緊接著調(diào)用setupRenderEffect,渲染出dom。

組件更新(slot變化):

當(dāng)slot變化后,首先會(huì)調(diào)用keep-alive組件的render即setup的返回函數(shù),邏輯見(jiàn)上面setup方法。緊接著當(dāng)某個(gè)slot卸載時(shí),會(huì)調(diào)用deactivate函數(shù),當(dāng)某個(gè)slot重新掛載時(shí),則會(huì)調(diào)用activate函數(shù),核心代碼如下:

const storageContainer = createElement('div')
sink.activate = (vnode, container, anchor) => {
      move(vnode, container, anchor, MoveType.ENTER, parentSuspense)
      queuePostRenderEffect(() => {
        const component = vnode.component!
        component.isDeactivated = false
        if (component.a !== null) {
          invokeHooks(component.a)
        }
      }, parentSuspense)
    }
 
    sink.deactivate = (vnode: VNode) => {
      move(vnode, storageContainer, null, MoveType.LEAVE, parentSuspense)
      queuePostRenderEffect(() => {
        const component = vnode.component!
        if (component.da !== null) {
          invokeHooks(component.da)
        }
        component.isDeactivated = true
      }, parentSuspense)
    }

邏輯也很簡(jiǎn)單,當(dāng)組件卸載時(shí),將其移入緩存的dom節(jié)點(diǎn)中,調(diào)用slot的deactivate生命周期,當(dāng)組件重新掛載時(shí)候,將其移入至掛載的dom節(jié)點(diǎn)中。

總結(jié)來(lái)說(shuō),keep-alive實(shí)現(xiàn)原理就是將對(duì)應(yīng)的狀態(tài)放入一個(gè)cache對(duì)象中,對(duì)應(yīng)的dom節(jié)點(diǎn)放入緩存dom中,當(dāng)下次再次需要渲染時(shí),從對(duì)象中獲取狀態(tài),從緩存dom中移出至掛載dom節(jié)點(diǎn)中。

keep-alive的使用總結(jié)

在平常開(kāi)發(fā)中,有些組件只需要加載一次,后面的數(shù)據(jù)將不存在變化,亦或者是組件需要緩存狀態(tài),滾動(dòng)條位置等,這個(gè)時(shí)候,keep-alive的用處就立刻凸顯出來(lái)了。

1.App.vue中使用keep-alive

include表示需要緩存的頁(yè)面,exclude表示不需要緩存的頁(yè)面,你可以只設(shè)置其中一個(gè)即可,但兩個(gè)同時(shí)設(shè)置的時(shí)候,切記exclude優(yōu)先級(jí)高于include,例如a組件在exclude中和include中都存在,那么,a組件是不會(huì)被緩存的

<template>
? ? <div id="app">
? ? ? <keep-alive :include="whiteList" :exclude="blackList">
? ? ? ? <router-view ?v-if="isRouterAlive" ></router-view>
? ? ? </keep-alive>
? ? </div>
</template>
<script>
export default {
? ? name: 'App',
? ? data(){
? ? ? return{
? ? ? ? ? isRouterAlive:true,
? ? ? ? ? whiteList:['styleLibrary','OrderList','SalesData'],
? ? ? ? ? blackList:['Footer'],
? ? ? ? ? personShow:false,
? ? ? }
? ? },
}
</script>

2.App.vue中配合router進(jìn)行使用

<template>
? ? <div id="app">
? <keep-alive>
? ? <router-view v-if="$route.meta.keepAlive"></router-view> ? ?<!--緩存組件-->
? </keep-alive>
? <router-view v-if="!$route.meta.keepAlive"></router-view> ? ? ?<!--非緩存組件-->
? ? </div>
</template>

將需要緩存的組件的$route.meta中的keepAlive設(shè)置為true,反之為false

?{
? ? ? path:'/login',
? ? ? name:'login',
? ? ? component:resolve=>require(['@/pages/login'],resolve),
? ? ? meta:{
? ? ? ? keepAlive:true,
? ? ? ? title:'登錄',
? ? ? ? savedPosition:true,
? ? ? }
? ? },

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

相關(guān)文章

  • Vue使用el-tree 懶加載進(jìn)行增刪改查功能的實(shí)現(xiàn)

    Vue使用el-tree 懶加載進(jìn)行增刪改查功能的實(shí)現(xiàn)

    這篇文章主要介紹了Vue使用el-tree 懶加載進(jìn)行增刪改查,以懶加載的形式展示,目錄根據(jù)需求需要有新增 編輯 刪除 操作以及操作后的刷新樹(shù)結(jié)構(gòu),具體實(shí)現(xiàn)代碼跟隨小編一起看看吧
    2021-08-08
  • vue中Npm run build 根據(jù)環(huán)境傳遞參數(shù)方法來(lái)打包不同域名

    vue中Npm run build 根據(jù)環(huán)境傳遞參數(shù)方法來(lái)打包不同域名

    這篇文章主要介紹了vue項(xiàng)目中Npm run build 根據(jù)環(huán)境傳遞參數(shù)方法來(lái)打包不同域名,使用npm run build --xxx,根據(jù)傳遞參數(shù)xxx來(lái)判定不同的環(huán)境,給出不同的域名配置,具體內(nèi)容詳情大家參考下本文
    2018-03-03
  • Vue 表單控件綁定的實(shí)現(xiàn)示例

    Vue 表單控件綁定的實(shí)現(xiàn)示例

    本篇文章主要介紹了Vue 表單控件綁定的實(shí)現(xiàn)示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-08-08
  • Vue動(dòng)態(tài)組件與異步組件實(shí)例詳解

    Vue動(dòng)態(tài)組件與異步組件實(shí)例詳解

    這篇文章主要介紹了Vue動(dòng)態(tài)組件與異步組件,結(jié)合實(shí)例形式分析了動(dòng)態(tài)組件與異步組件相關(guān)概念、功能及使用技巧,需要的朋友可以參考下
    2019-02-02
  • vue對(duì)于低版本瀏覽器兼容問(wèn)題的解決思路

    vue對(duì)于低版本瀏覽器兼容問(wèn)題的解決思路

    很多時(shí)候使用vue開(kāi)發(fā)的項(xiàng)目,由于無(wú)法在低版本瀏覽器上運(yùn)行,所以需要解決下,下面這篇文章主要給大家介紹了關(guān)于vue對(duì)于低版本瀏覽器兼容問(wèn)題的解決思路,需要的朋友可以參考下
    2023-02-02
  • vue實(shí)現(xiàn)監(jiān)聽(tīng)數(shù)值的變化,并捕捉到

    vue實(shí)現(xiàn)監(jiān)聽(tīng)數(shù)值的變化,并捕捉到

    這篇文章主要介紹了vue實(shí)現(xiàn)監(jiān)聽(tīng)數(shù)值的變化,并捕捉到問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • vue.js實(shí)現(xiàn)照片放大功能

    vue.js實(shí)現(xiàn)照片放大功能

    這篇文章主要為大家詳細(xì)介紹了vue.js實(shí)現(xiàn)照片放大功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-06-06
  • Vue中的動(dòng)態(tài)路由匹配路由問(wèn)題

    Vue中的動(dòng)態(tài)路由匹配路由問(wèn)題

    這篇文章主要介紹了Vue中的動(dòng)態(tài)路由匹配路由問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • 基于Element-Ui封裝公共表格組件的詳細(xì)圖文步驟

    基于Element-Ui封裝公共表格組件的詳細(xì)圖文步驟

    在平時(shí)開(kāi)發(fā)的時(shí)候很多情況都會(huì)使用到表格和分頁(yè)功能,下面這篇文章主要給大家介紹了關(guān)于如何基于Element-Ui封裝公共表格組件的詳細(xì)圖文步驟,需要的朋友可以參考下
    2022-09-09
  • Vue中在data里面調(diào)用method方法的實(shí)現(xiàn)

    Vue中在data里面調(diào)用method方法的實(shí)現(xiàn)

    這篇文章主要介紹了Vue中在data里面調(diào)用method方法的實(shí)現(xiàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06

最新評(píng)論