В сочетании со следующими случаями
<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-alivepatch
core/instance/renderupdateComponent
updateComponent = () => {
vm._update(vm._render())
}
keep-aliverendervnodevm._updatepatchkeep-alivepatch
keep-alivepatchcore/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 */)
}
}
}
componentInstanceinitinitkeep-alivecore/instance/lifecycle$childrenvm._init
let parent = options.parent
if (parent && !options.abstract) {
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm)
}
patch
visiblekeep-alivepatchdataupdateComponent
updateComponent = () => {
vm._update(vm._render())
}
keep-aliverenderpatchkeep-aliveprepatch
tickvnode
keep-alive
keep-alivekeep-alive
-
keep-alive -
keep-alivekeep-aliveactivatedcache
Vuegithub