一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服務(wù)器之家 - 編程語言 - JavaScript - vue.js - 從源碼角度來回答keep-alive組件的緩存原理

從源碼角度來回答keep-alive組件的緩存原理

2022-01-04 16:26十八喲 vue.js

這篇文章主要介紹了從源碼角度來回答keep-alive組件的緩存原理,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

今天開門見山地聊一下面試中被問到的一個問題:keep-alive組件的緩存原理。

官方API介紹和用法

  • <keep-alive> 包裹動態(tài)組件時,會緩存不活動的組件實例,而不是銷毀它們。
  • 和 <transition> 相似,<keep-alive> 是一個抽象組件:它自身不會渲染一個 DOM 元素,也不會出現(xiàn)在組件的父組件鏈中。
  • 當(dāng)組件在 <keep-alive> 內(nèi)被切換,它的 activated 和 deactivated 這兩個生命周期鉤子函數(shù)將會被對應(yīng)執(zhí)行。

官網(wǎng)的例子是 tab 切換保存了用戶的操作,實際中還可能遇到從列表頁跳轉(zhuǎn)去了詳情頁,再跳轉(zhuǎn)回列表頁需要保存用戶進(jìn)行過的篩選操作,這就需要用到 <keep-alive>,這樣也能避免重新渲染,提高頁面性能。

用法及props的講解

?
1
2
3
4
5
6
7
// keep-alive組件搭配動態(tài)組件的用法,還要其他的用法參見官網(wǎng)
<keep-alive
 include="['componentNameA', 'componentNameB']"
 exclude="'componentNameC'"
 :max="10">
 <component :is="view"></component>
</keep-alive>
  • include - 字符串或正則表達(dá)式或數(shù)組,name匹配上的組件會被緩存
  • exclude - 字符串或正則表達(dá)式或數(shù)組,name匹配上的組件都不會被緩存
  • max - 字符串或數(shù)字,緩存組件實例的最大數(shù),最久沒有被訪問的實例會被銷毀掉

注意:

  • <keep-alive> 只渲染其直系的一個組件,因此若在 <keep-alive> 中用 v-for,則其不會工作,若多條件判斷有多個符合條件也同理不工作。
  • include 和 exclude 匹配時,首先檢查組件的 name 選項,若 name 選項不可用,則匹配它的局部注冊名稱 (即父組件 components 選項的鍵值)。匿名組件不能被匹配。
  • <keep-alive> 不會在函數(shù)式組件中正常工作,因為它們沒有緩存實例。

源碼解讀

先貼一張源碼圖

從源碼角度來回答keep-alive組件的緩存原理

總共125行,收起來一看其實東西也比較少。前面是引入一些需要用到的方法,然后定義了一些  keep-alive 組件自己會用到的一些方法,最后就是向外暴露一個 name 為 keep-alive 的組件選項,這些選項除了 abstract 外,其他的我們都比較熟悉了,其中, render 函數(shù)就是緩存原理最重要的部分,也能看出 keep-alive 組件是一個函數(shù)式組件。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// isRegExp函數(shù)判斷是不是正則表達(dá)式,remove移除數(shù)組中的某一個成員
// getFirstComponentChild獲取VNode數(shù)組的第一個有效組件
import { isRegExp, remove } from 'shared/util'
import { getFirstComponentChild } from 'core/vdom/helpers/index'
?
type VNodeCache = { [key: string]: ?VNode }; // 緩存組件VNode的緩存類型
?
// 通過組件的name或組件tag來獲取組件名(上面注意的第二點)
function getComponentName (opts: ?VNodeComponentOptions): ?string {
 return opts && (opts.Ctor.options.name || opts.tag)
}
?
// 判斷include或exclude跟組件的name是否匹配成功
function matches (pattern: string | RegExp | Array<string>, name: string): boolean {
 if (Array.isArray(pattern)) {
 return pattern.indexOf(name) > -1 // include或exclude是數(shù)組的情況
 } else if (typeof pattern === 'string') {
 return pattern.split(',').indexOf(name) > -1 // include或exclude是字符串的情況
 } else if (isRegExp(pattern)) {
 return pattern.test(name) // include或exclude是正則表達(dá)式的情況
 }
 return false // 都沒匹配上(上面注意的二三點)
}
?
// 銷毀緩存
function pruneCache (keepAliveInstance: any, filter: Function) {
 const { cache, keys, _vnode } = keepAliveInstance // keep-alive組件實例
 for (const key in cache) {
 const cachedNode: ?VNode = cache[key] // 已經(jīng)被緩存的組件
 if (cachedNode) {
  const name: ?string = getComponentName(cachedNode.componentOptions)
  // 若name存在且不能跟include或exclude匹配上就銷毀這個已經(jīng)緩存的組件
  if (name && !filter(name)) {
  pruneCacheEntry(cache, key, keys, _vnode)
  }
 }
 }
}
?
// 銷毀緩存的入口
function pruneCacheEntry (
 cache: VNodeCache,
 key: string,
 keys: Array<string>,
 current?: VNode
) {
 const cached = cache[key] // 被緩存過的組件
 // “已經(jīng)被緩存的組件是否繼續(xù)被緩存” 有變動時
 // 若組件被緩存命中過且當(dāng)前組件不存在或緩存命中組件的tag和當(dāng)前組件的tag不相等
 if (cached && (!current || cached.tag !== current.tag)) {
 // 說明現(xiàn)在這個組件不需要被繼續(xù)緩存,銷毀這個組件實例
 cached.componentInstance.$destroy()
 }
 cache[key] = null // 把緩存中這個組件置為null
 remove(keys, key) // 把這個組件的key移除出keys數(shù)組
}
?
// 示例類型
const patternTypes: Array<Function> = [String, RegExp, Array]
?
// 向外暴露keep-alive組件的一些選項
export default {
 name: 'keep-alive', // 組件名
 abstract: true, // keep-alive是抽象組件
?
 // 用keep-alive組件時傳入的三個props
 props: {
 include: patternTypes,
 exclude: patternTypes,
 max: [String, Number]
 },
?
 created () {
 this.cache = Object.create(null) // 存儲需要緩存的組件
 this.keys = [] // 存儲每個需要緩存的組件的key,即對應(yīng)this.cache對象中的鍵值
 },
?
 // 銷毀keep-alive組件的時候,對緩存中的每個組件執(zhí)行銷毀
 destroyed () {
 for (const key in this.cache) {
  pruneCacheEntry(this.cache, key, this.keys)
 }
 },
?
 // keep-alive組件掛載時監(jiān)聽include和exclude的變化,條件滿足時就銷毀已緩存的組件
 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 // keep-alive組件的默認(rèn)插槽
 const vnode: VNode = getFirstComponentChild(slot) // 獲取默認(rèn)插槽的第一個有效組件
 // 如果vnode存在就取vnode的選項
 const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
 if (componentOptions) {
  //獲取第一個有效組件的name
  const name: ?string = getComponentName(componentOptions)
  const { include, exclude } = this // props傳遞來的include和exclude
  if (
  // 若include存在且name不存在或name未匹配上
  (include && (!name || !matches(include, name))) ||
  // 若exclude存在且name存在或name匹配上
  (exclude && name && matches(exclude, name))
  ) {
  return vnode // 說明不用緩存,直接返回這個組件進(jìn)行渲染
  }
  
  // 匹配上就需要進(jìn)行緩存操作
  const { cache, keys } = this // keep-alive組件的緩存組件和緩存組件對應(yīng)的key
  // 獲取第一個有效組件的key
  const key: ?string = vnode.key == null
  // 同一個構(gòu)造函數(shù)可以注冊為不同的本地組件
  // 所以僅靠cid是不夠的,進(jìn)行拼接一下
  ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
  : vnode.key
  // 如果這個組件命中緩存
  if (cache[key]) {
  // 這個組件的實例用緩存中的組件實例替換
  vnode.componentInstance = cache[key].componentInstance
  // 更新當(dāng)前key在keys中的位置
  remove(keys, key) // 把當(dāng)前key從keys中移除
  keys.push(key) // 再放到keys的末尾
  } else {
  // 如果沒有命中緩存,就把這個組件加入緩存中
  cache[key] = vnode
  keys.push(key) // 把這個組件的key放到keys的末尾
  // 如果緩存中的組件個數(shù)超過傳入的max,銷毀緩存中的LRU組件
  if (this.max && keys.length > parseInt(this.max)) {
   pruneCacheEntry(cache, keys[0], keys, this._vnode)
  }
  }
?
  vnode.data.keepAlive = true // 設(shè)置這個組件的keepAlive屬性為true
 }
 // 若第一個有效的組件存在,但其componentOptions不存在,就返回這個組件進(jìn)行渲染
 // 或若也不存在有效的第一個組件,但keep-alive組件的默認(rèn)插槽存在,就返回默認(rèn)插槽的第一個組件進(jìn)行渲染
 return vnode || (slot && slot[0])
 }
}

補充:

上面關(guān)于刪除第一個舊緩存組件和更新緩存組件 key 的順序,其實是用到了LRU緩存淘汰策略:
LRU全稱Least Recently Used,最近最少使用的意思,是一種內(nèi)存管理算法。
這種算法基于一種假設(shè):長期不用的數(shù)據(jù),在未來被用到的幾率也很小,因此,當(dāng)數(shù)據(jù)所占內(nèi)存達(dá)到一定閾值,可以移除掉最近最少使用的。

總結(jié)

簡單總結(jié)為:

keep-alive 組件在渲染的時候,會根據(jù)傳入的 include 和 exclude 來匹配 keep-alive 包裹的命名組件,未匹配上就直接返回這個命名組件進(jìn)行渲染,若匹配上就進(jìn)行緩存操作:若緩存中已有這個組件,就替換其實例,并更新這個組件的 key 在 keys 中的位置;若緩存中沒有這個組件,就把這個組件放入 keep-alive 組件的緩存 cache 中,并把這個組件的 key 放入 keys 中,由于在 mounted 的時候有對 include 和 exclude 進(jìn)行監(jiān)聽,因此,后續(xù)這兩個屬性值發(fā)生變化時,會再次判斷是否滿足條件而進(jìn)行組件銷毀。

到此這篇關(guān)于從源碼角度來回答keep-alive組件的緩存原理的文章就介紹到這了,更多相關(guān)keep-alive組件緩存內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!

原文鏈接:https://juejin.cn/post/6918569473419165709

延伸 · 閱讀

精彩推薦
  • vue.jsVue2.x 項目性能優(yōu)化之代碼優(yōu)化的實現(xiàn)

    Vue2.x 項目性能優(yōu)化之代碼優(yōu)化的實現(xiàn)

    這篇文章主要介紹了Vue2.x 項目性能優(yōu)化之代碼優(yōu)化的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋...

    優(yōu)小U9632022-02-21
  • vue.js梳理一下vue中的生命周期

    梳理一下vue中的生命周期

    看過很多人講vue的生命周期,但總是被繞的云里霧里,尤其是自學(xué)的同學(xué),可能js的基礎(chǔ)也不是太牢固,聽起來更是吃力,那我就已個人之淺見,以大白話...

    CRMEB技術(shù)團(tuán)隊7992021-12-22
  • vue.jsVue多選列表組件深入詳解

    Vue多選列表組件深入詳解

    這篇文章主要介紹了Vue多選列表組件深入詳解,這個是vue的基本組件,有需要的同學(xué)可以研究下...

    yukiwu6752022-01-25
  • vue.jsVue項目中實現(xiàn)帶參跳轉(zhuǎn)功能

    Vue項目中實現(xiàn)帶參跳轉(zhuǎn)功能

    最近做了一個手機端系統(tǒng),其中遇到了父頁面需要攜帶參數(shù)跳轉(zhuǎn)至子頁面的問題,現(xiàn)已解決,下面分享一下實現(xiàn)過程,感興趣的朋友一起看看吧...

    YiluRen丶4302022-03-03
  • vue.js用vite搭建vue3應(yīng)用的實現(xiàn)方法

    用vite搭建vue3應(yīng)用的實現(xiàn)方法

    這篇文章主要介紹了用vite搭建vue3應(yīng)用的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下...

    Asiter7912022-01-22
  • vue.jsVue中引入svg圖標(biāo)的兩種方式

    Vue中引入svg圖標(biāo)的兩種方式

    這篇文章主要給大家介紹了關(guān)于Vue中引入svg圖標(biāo)的兩種方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的...

    十里不故夢10222021-12-31
  • vue.js詳解vue 表單綁定與組件

    詳解vue 表單綁定與組件

    這篇文章主要介紹了vue 表單綁定與組件的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用vue框架,感興趣的朋友可以了解下...

    Latteitcjz6432022-02-12
  • vue.jsVue2.x-使用防抖以及節(jié)流的示例

    Vue2.x-使用防抖以及節(jié)流的示例

    這篇文章主要介紹了Vue2.x-使用防抖以及節(jié)流的示例,幫助大家更好的理解和學(xué)習(xí)使用vue框架,感興趣的朋友可以了解下...

    Kyara6372022-01-25
主站蜘蛛池模板: 日本动漫xxxxxx | 黄a在线观看 | 国产精品免费精品自在线观看 | 国产日韩欧美精品在线 | girlfriend动漫在线播放 | 免费观看在线永久免费xx视频 | 亚洲天堂男人的天堂 | 国产免费一区二区三区免费视频 | 500av导航大全精品 | 91手机在线| 啪啪免费入口网站 | 99精品视频在线观看免费播放 | 夫妻性生活影院 | 91精品国产91久久久久 | 午夜福利试看120秒体验区 | 三级理论在线播放大全 | 特a级片 | 卫生间被教官做好爽HH视频 | 亚洲国产综合精品 | 白丝萝莉喷水 | 亚洲天堂视频在线观看免费 | 免费人成黄页在线观看69 | 美女扒开粉嫩尿口漫画 | 黑人巨鞭大战白妞10级 | 亚洲天堂视频在线观看 | 国产精品久久久久不卡绿巨人 | 韩国丽卡三级作品 | 亚洲 欧美 中文 日韩 视频 | 雪恋电影完整版免费观看 | 韩国男女做性全过程视频 | 日本韩国推理片免费观看网站 | 国产网站免费在线观看 | 蜜臀91 | 黑白配高清hd在线视频 | 91久久国产综合精品 | 白发在线视频播放观看免费 | 国产福利资源 | 国产成人福利美女观看视频 | 色婷婷六月天 | 国产成人免费高清激情视频 | 亚洲va天堂va国产va久久 |