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

keep-alive保持組件狀態(tài)的方法

 更新時間:2020年12月02日 09:35:49   作者:快狗打車前端團隊  
這篇文章主要介紹了keep-alive保持組件狀態(tài)的方法,幫助大家更好的理解和學習vue框架,感興趣的朋友可以了解下

keep-alive的設計初衷

有些業(yè)務場景需要根據(jù)不同的判斷條件,動態(tài)地在多個組件之間切換。頻繁的組件切換會導致組件反復渲染,如果組件包含有大量的邏輯和dom節(jié)點,極易造成性能問題。其次,切換后組件的狀態(tài)也會完全丟失。keep-alive的設計初衷就是為了保持組件的狀態(tài),避免組件的重復渲染。

為什么keep-alive可以直接使用

開發(fā)者無需注冊和引入,直接可以在模板中使用。 跟開發(fā)者使用Vue.component自定義的組件不同,keep-alive無需注冊,在模板中直接可以使用,如下所示:

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

這是因為keep-alive是vue的內置組件,已經在vue中提前定義。

// core/components/keep-alive.js

export default {
 name: 'keep-alive',
 abstract: true,

 props: {
  include: patternTypes,
  exclude: patternTypes,
  max: [String, Number]
 },

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

 destroyed () {
  // keep-alive的銷毀,將所有緩存的組件清除
  for (const key in this.cache) {
   pruneCacheEntry(this.cache, key, this.keys)
  }
 },

 mounted () {
  // 如果指定了include和exclude屬性,需要實時觀察當前這兩個屬性的變化,以及時的更新緩存
  this.$watch('include', val => {
   pruneCache(this, name => matches(val, name))
  })
  this.$watch('exclude', val => {
   pruneCache(this, name => !matches(val, name))
  })
 },

 render () {
  // keepAlive組件本身不會被渲染成dom節(jié)點,其render方法的處理邏輯的是將其包裹的組件的vnode返回
  const slot = this.$slots.default
  // 獲取第一個組件子節(jié)點
  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

   // 1、如果緩存中存在該vnode,從緩存中取得該組件的實例(一個組件對應一顆vnode樹,同時一個組件對應一個vue子類的實例),不再重新創(chuàng)建
   if (cache[key]) {
    vnode.componentInstance = cache[key].componentInstance
    // make current key freshest
    // 將當前的組件的key作為最新的緩存(更新其在keys數(shù)組中的順序)
    remove(keys, key)
    keys.push(key)
   } else {
    // 2、如果未命中緩存,添加到緩存
    cache[key] = vnode
    keys.push(key)
    // 如果緩存超過限制,淘汰最舊的緩存
    if (this.max && keys.length > parseInt(this.max)) {
     pruneCacheEntry(cache, keys[0], keys, this._vnode)
    }
   }

   // 標記為keepAlive組件
   vnode.data.keepAlive = true
  }
  return vnode || (slot && slot[0])
 }
}

這是因為keep-alive是vue的內置組件,已經在vue中提前定義。

// core/components/keep-alive.js

export default {
 name: 'keep-alive',
 abstract: true,

 props: {
  include: patternTypes,
  exclude: patternTypes,
  max: [String, Number]
 },

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

 destroyed () {
  // keep-alive的銷毀,將所有緩存的組件清除
  for (const key in this.cache) {
   pruneCacheEntry(this.cache, key, this.keys)
  }
 },

 mounted () {
  // 如果指定了include和exclude屬性,需要實時觀察當前這兩個屬性的變化,以及時的更新緩存
  this.$watch('include', val => {
   pruneCache(this, name => matches(val, name))
  })
  this.$watch('exclude', val => {
   pruneCache(this, name => !matches(val, name))
  })
 },

 render () {
  // keepAlive組件本身不會被渲染成dom節(jié)點,其render方法的處理邏輯的是將其包裹的組件的vnode返回
  const slot = this.$slots.default
  // 獲取第一個組件子節(jié)點
  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

   // 1、如果緩存中存在該vnode,從緩存中取得該組件的實例(一個組件對應一顆vnode樹,同時一個組件對應一個vue子類的實例),不再重新創(chuàng)建
   if (cache[key]) {
    vnode.componentInstance = cache[key].componentInstance
    // make current key freshest
    // 將當前的組件的key作為最新的緩存(更新其在keys數(shù)組中的順序)
    remove(keys, key)
    keys.push(key)
   } else {
    // 2、如果未命中緩存,添加到緩存
    cache[key] = vnode
    keys.push(key)
    // 如果緩存超過限制,淘汰最舊的緩存
    if (this.max && keys.length > parseInt(this.max)) {
     pruneCacheEntry(cache, keys[0], keys, this._vnode)
    }
   }

   // 標記為keepAlive組件
   vnode.data.keepAlive = true
  }
  return vnode || (slot && slot[0])
 }
}

// core/components/index.js
import KeepAlive from './keep-alive'

export default {
 KeepAlive
}

// core/global-api/index.js

// 導入內置組件
import builtInComponents from '../components/index'

/**
 * 為Vue添加全局方法和屬性
 * @param {GlobalAPI} Vue 
 */
export function initGlobalAPI (Vue: GlobalAPI) {
 
 // ...省略了無關代碼
 
 Vue.options = Object.create(null)
 // 添加內置組件keep-alive
 extend(Vue.options.components, builtInComponents)
}

buildInComponents中包含了keep-alive的定義。在initGlobalAPI方法中,將內置組件添加到了 vue的全局變量中。

extend(A, B)是個簡單的對象屬性復制方法。將對象B中的屬性復制到對象A中。

keep-alive是如何保持組件狀態(tài)的

為了保持組件狀態(tài),keep-alive設計了緩存機制。

我們知道,模板中的每個HTML標簽在vue中由相應的vnode節(jié)點對象來表示。如果是HTML標簽是組件標簽,需要為該標簽的vnode創(chuàng)建一個組件實例。組件實例負責組件內的HTML模板的編譯和渲染。因此相比于普通HTML標簽的vnode節(jié)點,組件vnode節(jié)點會存在componentOptions和 componentInstance 這兩個屬性中保存組件選項對象和組件實例的引用。

首先,我們從keep-alive組件的實現(xiàn)代碼中可以看到在組件的created鉤子中設計了緩存機制:

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

keep-alive設置了cache和keys兩個屬性來緩存子組件。其中cache中的每項是一個以所包裹的組件的組件名為key,包裹組件對應的vnoded為值的對象。keys的每一項是其所包裹的組件的組件名。

render 函數(shù)是vue實例和vue組件實例中用來創(chuàng)建vnode的方法。我們在實際的應用中,一般是通過template和el來指定模板,然后由vue將模板編譯成render函數(shù)。如果用戶希望能更靈活地控制vnode的創(chuàng)建可以提供自定義的render函數(shù)。

render () {
  const slot = this.$slots.default
  // 獲取第一個組件子節(jié)點
  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

   // 1、如果緩存中存在該vnode,從緩存中取得該組件的實例(一個組件對應一顆vnode樹,同時一個組件對應一個vue子類的實例),不再重新創(chuàng)建
   if (cache[key]) {
    vnode.componentInstance = cache[key].componentInstance
    // make current key freshest
    // 將當前的組件的key作為最新的緩存(更新其在keys數(shù)組中的順序)
    remove(keys, key)
    keys.push(key)
   } else {
    // 2、如果未命中緩存,添加到緩存
    cache[key] = vnode
    keys.push(key)
    // 如果緩存超過限制,淘汰最舊的緩存
    if (this.max && keys.length > parseInt(this.max)) {
     pruneCacheEntry(cache, keys[0], keys, this._vnode)
    }
   }

   // 標記為keepAlive組件
   vnode.data.keepAlive = true
  }
  return vnode || (slot && slot[0])
 }

keep-alive內部就是單獨提供了render函數(shù)來自定義了vnode的創(chuàng)建邏輯。首先keep-alive獲取到其所包裹的子組件的根vnode,然后去cache中查找該組件是否存在。

如果cache中不存在子組件vnode,則以{子組件名: 子組件vnode}的形式保存到cache對象中。同時將子組件名字保存到keys數(shù)組中。同時如果當前緩存的數(shù)量已經超過max所設置的最大值,需要淘汰掉最近最少使用的緩存項(LRU)。

如果cache中存在子組件vnode,那么只需要復用緩存的組件vnode的組件實例(componentInstance)。同時需要將該子組件vnode在緩存中順序調到最前面,這個主要是為了在緩存不足時,正確地淘汰緩存項。

舉例說明

最后通過一個例子加深一下理解。

 <div id="app">
  <keep-alive><component :is="view"></component></keep-alive>
  <button @click="view = view =='count'? 'any': 'count'">切換組件</button>
</div>
Vue.component("count", {
  data() {
    return {
      count:0
    };
  },
  template: "<div @click='count+=1'>點了我 {{count}} 次</div>"
});
  
Vue.component("any", {
  template: "<div>any</div>"
});

new Vue({
  el: "#app",
  data: {
   view: "count"
  }
});

由于view默認值是count,因此keep-alive包裹的子組件是count。此時keep-alive的緩存中為空,因此會把組件count的vnode添加到緩存。緩存結果為

cache = {1::count: {tag: "vue-component-1-count", data:{tag: "component", hook: {…}}}, componentOptions, componentInstance, ...}
keys = ["1::count"]

點擊一下組件count,組件的顯示內容變成"點了我1次",然后切換到組件any。與count組件相同,由于在keep-alive的緩存中還未保存any組件的vnode,因此需要將any添加到緩存中。此時緩存結果變成了:

cache = {
  1::count: {tag: "vue-component-1-count", data:{tag: "component", hook: {…}}, componentOptions, componentInstance, ...},
  2::any: {tag: "vue-component-2-any", data:{tag: "component", hook: {…}}, componentOptions, componentInstance, ...},
}
keys = ["1::count", "2::any"]

頁面顯示結果為:

再次點擊切換組件,切回count。此時count組件的vnode在緩存中已經存在,所以直接復用了原來count組件vnode中所保存的組件實例,組件實例中保存了原來的count值,因此組件切換完后,組件的狀態(tài)也跟著還原了回來。

下圖為count組件實例的狀態(tài),可以看到count的值得到了保持:

最終頁面顯示結果為:

從上面的分析可知,如果組件被包裹在keep-alive組件中,組件vnode會緩存到cache中。而組件的狀態(tài)又會保存在組件實例中(componentInstance),當組件再次切換回來時,keep-alive直接將之前緩存的狀態(tài)進行還原,也就實現(xiàn)了組件狀態(tài)的保持。

以上就是keep-alive保持組件狀態(tài)的方法的詳細內容,更多關于keep-alive保持組件狀態(tài)的資料請關注腳本之家其它相關文章!

相關文章

  • vue中filters 傳入兩個參數(shù) / 使用兩個filters的實現(xiàn)方法

    vue中filters 傳入兩個參數(shù) / 使用兩個filters的實現(xiàn)方法

    這篇文章主要介紹了vue中filters 傳入兩個參數(shù) / 使用兩個filters的實現(xiàn)方法,文中給大家提到了Vue 中的 filter 帶多參的使用方法,需要的朋友可以參考下
    2019-07-07
  • laravel5.4+vue+element簡單搭建的示例代碼

    laravel5.4+vue+element簡單搭建的示例代碼

    本篇文章主要介紹了laravel5.4+vue+element簡單搭建的示例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • Vue3?中自定義插件的實現(xiàn)方法

    Vue3?中自定義插件的實現(xiàn)方法

    在 Vue 中,一些簡單的功能,我們可以直接定義為全局方法,然后掛到 Vue 上就能使用了,這篇文章主要介紹了Vue3?中自定義插件的實現(xiàn),需要的朋友可以參考下
    2022-08-08
  • 有關vue 組件切換,動態(tài)組件,組件緩存

    有關vue 組件切換,動態(tài)組件,組件緩存

    這篇文章主要介紹了有關vue 組件切換,動態(tài)組件,組件緩存,在組件化開發(fā)模式下,我們會把整個項目拆分成很多組件,然后按照合理的方式組織起來,達到預期效果,下面來看看文章的詳細內容
    2021-11-11
  • 詳解vue axios二次封裝

    詳解vue axios二次封裝

    這篇文章給大家分享了vue axios二次封裝的相關知識點等內容以及實例代碼,有興趣的朋友可以參考學習下。
    2018-07-07
  • VUE項目啟動沒有問題但代碼中script標簽有藍色波浪線標注

    VUE項目啟動沒有問題但代碼中script標簽有藍色波浪線標注

    這篇文章主要給大家介紹了關于VUE項目啟動沒有問題但代碼中script標簽有藍色波浪線標注的相關資料,文中將遇到的問題以及解決的方法介紹的非常詳細,需要的朋友可以參考下
    2023-05-05
  • Vue實現(xiàn)base64編碼圖片間的切換功能

    Vue實現(xiàn)base64編碼圖片間的切換功能

    這篇文章主要介紹了Vue實現(xiàn)base64編碼圖片間的切換功能,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-12-12
  • vue3?使用setup語法糖實現(xiàn)分類管理功能

    vue3?使用setup語法糖實現(xiàn)分類管理功能

    這篇文章主要介紹了vue3?使用setup語法糖實現(xiàn)分類管理,本次模塊使用 vue3+element-plus 實現(xiàn)一個新聞站的后臺分類管理模塊,其中新增、編輯采用對話框方式公用一個表單,需要的朋友可以參考下
    2022-08-08
  • vue選項卡切換登錄方式小案例

    vue選項卡切換登錄方式小案例

    這篇文章主要為大家詳細介紹了vue選項卡切換登錄方式小案例,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-09-09
  • Vue3 使用axios攔截器打印前端日志

    Vue3 使用axios攔截器打印前端日志

    這篇文章主要介紹了Vue3 使用axios攔截器打印前端日志,這是一種比較值得推薦的方式,也就是寫一次,就不用總寫console.log了。下面來看看文章的詳細內容,需要的朋友可以參考一下
    2021-11-11

最新評論