亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

keep-alive的設(shè)計(jì)初衷

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

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

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

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

這是因?yàn)閗eep-alive是vue的內(nèi)置組件,已經(jīng)在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屬性,需要實(shí)時(shí)觀察當(dāng)前這兩個(gè)屬性的變化,以及時(shí)的更新緩存
  this.$watch('include', val => {
   pruneCache(this, name => matches(val, name))
  })
  this.$watch('exclude', val => {
   pruneCache(this, name => !matches(val, name))
  })
 },

 render () {
  // keepAlive組件本身不會(huì)被渲染成dom節(jié)點(diǎn),其render方法的處理邏輯的是將其包裹的組件的vnode返回
  const slot = this.$slots.default
  // 獲取第一個(gè)組件子節(jié)點(diǎn)
  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,從緩存中取得該組件的實(shí)例(一個(gè)組件對(duì)應(yīng)一顆vnode樹(shù),同時(shí)一個(gè)組件對(duì)應(yīng)一個(gè)vue子類的實(shí)例),不再重新創(chuàng)建
   if (cache[key]) {
    vnode.componentInstance = cache[key].componentInstance
    // make current key freshest
    // 將當(dāng)前的組件的key作為最新的緩存(更新其在keys數(shù)組中的順序)
    remove(keys, key)
    keys.push(key)
   } else {
    // 2、如果未命中緩存,添加到緩存
    cache[key] = vnode
    keys.push(key)
    // 如果緩存超過(guò)限制,淘汰最舊的緩存
    if (this.max && keys.length > parseInt(this.max)) {
     pruneCacheEntry(cache, keys[0], keys, this._vnode)
    }
   }

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

這是因?yàn)閗eep-alive是vue的內(nèi)置組件,已經(jīng)在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屬性,需要實(shí)時(shí)觀察當(dāng)前這兩個(gè)屬性的變化,以及時(shí)的更新緩存
  this.$watch('include', val => {
   pruneCache(this, name => matches(val, name))
  })
  this.$watch('exclude', val => {
   pruneCache(this, name => !matches(val, name))
  })
 },

 render () {
  // keepAlive組件本身不會(huì)被渲染成dom節(jié)點(diǎn),其render方法的處理邏輯的是將其包裹的組件的vnode返回
  const slot = this.$slots.default
  // 獲取第一個(gè)組件子節(jié)點(diǎn)
  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,從緩存中取得該組件的實(shí)例(一個(gè)組件對(duì)應(yīng)一顆vnode樹(shù),同時(shí)一個(gè)組件對(duì)應(yīng)一個(gè)vue子類的實(shí)例),不再重新創(chuàng)建
   if (cache[key]) {
    vnode.componentInstance = cache[key].componentInstance
    // make current key freshest
    // 將當(dāng)前的組件的key作為最新的緩存(更新其在keys數(shù)組中的順序)
    remove(keys, key)
    keys.push(key)
   } else {
    // 2、如果未命中緩存,添加到緩存
    cache[key] = vnode
    keys.push(key)
    // 如果緩存超過(guò)限制,淘汰最舊的緩存
    if (this.max && keys.length > parseInt(this.max)) {
     pruneCacheEntry(cache, keys[0], keys, this._vnode)
    }
   }

   // 標(biāo)記為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

// 導(dǎo)入內(nèi)置組件
import builtInComponents from '../components/index'

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

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

extend(A, B)是個(gè)簡(jiǎn)單的對(duì)象屬性復(fù)制方法。將對(duì)象B中的屬性復(fù)制到對(duì)象A中。

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

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

我們知道,模板中的每個(gè)HTML標(biāo)簽在vue中由相應(yīng)的vnode節(jié)點(diǎn)對(duì)象來(lái)表示。如果是HTML標(biāo)簽是組件標(biāo)簽,需要為該標(biāo)簽的vnode創(chuàng)建一個(gè)組件實(shí)例。組件實(shí)例負(fù)責(zé)組件內(nèi)的HTML模板的編譯和渲染。因此相比于普通HTML標(biāo)簽的vnode節(jié)點(diǎn),組件vnode節(jié)點(diǎn)會(huì)存在componentOptions和 componentInstance 這兩個(gè)屬性中保存組件選項(xiàng)對(duì)象和組件實(shí)例的引用。

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

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

keep-alive設(shè)置了cache和keys兩個(gè)屬性來(lái)緩存子組件。其中cache中的每項(xiàng)是一個(gè)以所包裹的組件的組件名為key,包裹組件對(duì)應(yīng)的vnoded為值的對(duì)象。keys的每一項(xiàng)是其所包裹的組件的組件名。

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

render () {
  const slot = this.$slots.default
  // 獲取第一個(gè)組件子節(jié)點(diǎn)
  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,從緩存中取得該組件的實(shí)例(一個(gè)組件對(duì)應(yīng)一顆vnode樹(shù),同時(shí)一個(gè)組件對(duì)應(yīng)一個(gè)vue子類的實(shí)例),不再重新創(chuàng)建
   if (cache[key]) {
    vnode.componentInstance = cache[key].componentInstance
    // make current key freshest
    // 將當(dāng)前的組件的key作為最新的緩存(更新其在keys數(shù)組中的順序)
    remove(keys, key)
    keys.push(key)
   } else {
    // 2、如果未命中緩存,添加到緩存
    cache[key] = vnode
    keys.push(key)
    // 如果緩存超過(guò)限制,淘汰最舊的緩存
    if (this.max && keys.length > parseInt(this.max)) {
     pruneCacheEntry(cache, keys[0], keys, this._vnode)
    }
   }

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

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

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

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

舉例說(shuō)明

最后通過(guò)一個(gè)例子加深一下理解。

 <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'>點(diǎn)了我 {{count}} 次</div>"
});
  
Vue.component("any", {
  template: "<div>any</div>"
});

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

由于view默認(rèn)值是count,因此keep-alive包裹的子組件是count。此時(shí)keep-alive的緩存中為空,因此會(huì)把組件count的vnode添加到緩存。緩存結(jié)果為

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

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

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

頁(yè)面顯示結(jié)果為:

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

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

最終頁(yè)面顯示結(jié)果為:

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

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

相關(guān)文章

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

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

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

    laravel5.4+vue+element簡(jiǎn)單搭建的示例代碼

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

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

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

    有關(guān)vue 組件切換,動(dòng)態(tài)組件,組件緩存

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

    詳解vue axios二次封裝

    這篇文章給大家分享了vue axios二次封裝的相關(guān)知識(shí)點(diǎn)等內(nèi)容以及實(shí)例代碼,有興趣的朋友可以參考學(xué)習(xí)下。
    2018-07-07
  • VUE項(xiàng)目啟動(dòng)沒(méi)有問(wèn)題但代碼中script標(biāo)簽有藍(lán)色波浪線標(biāo)注

    VUE項(xiàng)目啟動(dòng)沒(méi)有問(wèn)題但代碼中script標(biāo)簽有藍(lán)色波浪線標(biāo)注

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

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

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

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

    這篇文章主要介紹了vue3?使用setup語(yǔ)法糖實(shí)現(xiàn)分類管理,本次模塊使用 vue3+element-plus 實(shí)現(xiàn)一個(gè)新聞?wù)镜暮笈_(tái)分類管理模塊,其中新增、編輯采用對(duì)話框方式公用一個(gè)表單,需要的朋友可以參考下
    2022-08-08
  • vue選項(xiàng)卡切換登錄方式小案例

    vue選項(xiàng)卡切換登錄方式小案例

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

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

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

最新評(píng)論