В сочетании со следующими случаями
<keep-alive>
<coma v-if="visible"></coma>
<comb v-else></comb>
</keep-alive>
<button @click="visible = !visible">更改</button>
Например, вcoma
а такжеcomb
Имеетсяinput
иметь соответствующиеvalue
, если мы неkeep-alive
, при сменеvisible
, оба компонента будут перерисованы, ранее введенный контент будет потерян, и будет выполнен полный процесс жизненного цикла:beforeCreate
=> created
....
Но если мы используемkeep-alive
, то на следующем переключенииvisible
когда,input
соответствующийvalue
значение на момент последнего изменения.
такkeep-alive
Он в основном используется для поддержания состояния компонента и предотвращения повторного создания компонента.
принцип
keep-alive
Способ использования указан вcore/components/keep-alive
середина
export default {
abstract: true,
props: {
include: patternTypes, // 缓存白名单
exclude: patternTypes, // 缓存黑名单
max: [String, Number] // 缓存的实例上限
},
created() {
// 用于缓存虚拟DOM
this.cache = Object.create(null);
this.keys = [];
},
mounted() {
// 用于监听i黑白名单,如果发生调用pruneCache
// pruneCache更新vue的cache缓存
this.$watch('include', val => {
pruneCache(this, name => matches(val, name))
})
this.$watch('exclude', val => {
pruneCache(this, name => !matches(val, name))
})
}
render() {
//...
}
}
Вышеприведенный код определяет операции нескольких циклов объявлений, наиболее важныхrender
функция, давайте посмотрим, как она реализована
render
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 // 定义组件的缓存key
// 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) // 调整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])
}
Проведите пошаговый анализ
- Получать
keep-alive
первый объект дочернего компонента, который включает объект - Возвращает свой собственный в зависимости от совпадения белого и черного списков.
vnode
- согласно с
vnode
изcid
а такжеtag
Сгенерированоkey
, есть ли текущий кеш в объекте кеша, если да, вернуть и обновитьkey
существуетkeys
расположение в - Если для текущего кэшированного объекта нет кеша, перейдите к
cache
добавить содержимое этого, и в соответствии сLRU
Алгоритм удаляет экземпляры, которые в последнее время не использовались - Установите для первого дочернего объекта компонента
keep-alive
дляtrue
первый рендер
В сочетании со статьей в начале статьи для анализа текущего примера, когда страница отображается в первый раз, поскольку процесс рендеринга компонента - это сначала дочерний компонент, а затем родительский компонент, данные дочернего компонента можно получить здесь, а затем здесь можно получить данные дочернего компонента.vnode
информация хранится вcache
, и положиcoma
компонентkeepAlive
установлен вtrue
.
Это вопрос, почему я могу получить подкомпоненты?componentOptions
, с помощью приведенного выше примера мы знаем, что генерацияvnode
черезrender
функция,render
функция передается вplatforms/web/entry-runtime-with-compiler
определено вcompileToFunctions
Будуtemplate
компилируется вrender
функция, посмотрите на сгенерированную корреспонденциюrender
функция
<template>
<div class="parent">
<keep-alive>
<coma v-if="visible"></coma>
<comb v-else></comb>
</keep-alive>
</div>
</template>
<script>
(function anonymous() {
with(this) {
return _c('div', {
staticClass: "parent"
}, [
_c('keep-alive', [(visibility) ? _c('coma') : _c('comb')], 1),
_c('button', {
on: {
"click": change
}
}, [_v("change")])], 1)
}
})
</script>
можно увидеть сгенерированныйrender
связанные с функциейkeep-alive
процесс генерации
_c('keep-alive', [(visibility) ? _c('coma') : _c('comb')], 1),
существуетkeep-alive
позвонил первым_c('coma')
, так что вы можете получить доступ к подкомпонентамcomponentOptions
,специфический_c
вvdom/create-element.js
Определенный в , он считает, что он должен генерировать компонентыvnode
или что-то другое.
Изменятьdata
,вызыватьpatch
На первом рендере мы меняемcoma
серединаinput
значение, см. когдаvisible
изменить снова наtrue
когда,input
Будет ли запоминаться предыдущее значение. потому что изменилосьvisible
значение, код будет выполнен повторно
updateComponent = () => {
vm._update(vm._render())
}
Так что это будет выполнено повторноkeep-alive
изrender
функцию, потому что данные уже сохранены вcache
, так что на этот раз данные напрямую изcache
быть казненным.
vnode.componentInstance = cache[key].componentInstance
Когда он был впервые представлен, было упомянуто, что когдаkey
Если значение не существует, подкомпонентvnode
Кэшируется, если вы можете увидеть первый рендер, разбив точкуcomponentInstance
дляundefined
,componentInstance
по фактуpatch
вызов компонента в процессеinit
Хук только генерируется, так почему я могу получить его в это время? Вот пример для объяснения. Например, есть следующие примеры
a = {
b: 1
}
c = a;
a.b = 5;
console.log(c.b) // 5
object
Это ссылочный тип, поэтому при изменении исходного объекта ссылочное место также изменится.
Затем переназначьте предыдущую информацию о состоянииcoma
, то зачем присваиватьcoma
,coma
не будет выполнять процесс создания компонента, см.patch
код, при выполнении которогоcreateComponent
когда, потому чтоcoma
Для компонента будет выполняться логика, связанная с компонентом
// core/vdom/patch.js
function createComponent(vnode, insertedVnodeQueue, parentElm, refELm) {
let i = vnode.data;
if (isDef(i)) {
const isReactivated = isDef(vnode.componentInstance) && i.keepAlive;
if (isDef(i = i.hook) && isDef(i = i.init)) {
i(vnode, false);
}
}
}
// core/vdom/create-component
init(vnode) {
if (vnode.componentInstance &&
!vnode.componentInstance._isDetroyed &&
vnode.data.keepAlive) {
const mountedNode: any = node;
componentVnodeHooks.prepatch(mountedNode, mountedNode)
} else {
const child = vnode.componentInstance = createComponentInstanceForVnode(
vnode,
activeInstance
)
child.$mount(vnode.elm)
}
}
потому чтоvnode.componentInstance
существуетkeep-alive
был переназначен, поэтому иkeepAlive
дляtrue
, поэтому выполняйте толькоprepatch
,такcreated
,mounted
keep-alive
patch
core/instance/render
updateComponent
updateComponent = () => {
vm._update(vm._render())
}
keep-alive
render
vnode
vm._update
patch
keep-alive
patch
keep-alive
patch
core/vdom/patch
function createComponent(vnode, insertedVnodeQueue, parentElm, refElm) {
let i = vnode.data
if (isDef(i)) {
const isReactivated = isDef(vnode.componentInstance) && i.keepAlive
if (isDef(i = i.hook) && isDef(i = i.init)) {
i(vnode, false /* hydrating */)
}
}
}
componentInstance
init
init
keep-alive
core/instance/lifecycle
$children
vm._init
let parent = options.parent
if (parent && !options.abstract) {
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm)
}
patch
visible
keep-alive
patch
data
updateComponent
updateComponent = () => {
vm._update(vm._render())
}
keep-alive
render
patch
keep-alive
prepatch
tick
vnode
keep-alive
keep-alive
keep-alive
-
keep-alive
-
keep-alive
keep-alive
activated
cache
Vue
github