本篇文章,我们来讲一下keep-alive的实现。  更容易看懂

Vue中,有三个内置的抽象组件,分别是keep-alivetransitiontransition-group

它们都有一个共同的特点,就是自身不会渲染一个DOM元素,也不会出现在父组件链中。

keep-alive的作用,是包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。具体的用法见这里

该组件的定义,是在src/core/components/keep-alive.js文件中。

它会在Vue初始化时,添加在Vue.options.components上,所以在所有的组件中,都可以直接只用它。

直接看代码:

export default {
name: 'keep-alive',
abstract: true, props: {
...
}, created () {
this.cache = Object.create(null)
}, destroyed () {
...
}, watch: {
...
}, render () {
...
}
}

name不用多说,abstract: true 这个条件我们自己定义组件时通常不会用,

它是用来标识当前的组件是一个抽象组件,它自身不会渲染一个真实的DOM元素。

比如在创建两个vm实例之间的父子关系时,会跳过抽象组件的实例:

  let parent = options.parent
if (parent && !options.abstract) {
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm)
}

props表示我们可以传入include来匹配哪些组件可以缓存,exclude来匹配哪些组件不缓存。

created钩子函数调用时,会创建一个this.cache对象用于缓存它的子组件。

destroyed表示keep-alive被销毁时,会同时销毁它缓存的组件,并调用deactivated钩子函数。

function pruneCacheEntry (vnode: ?VNode) {
if (vnode) {
if (!vnode.componentInstance._inactive) {
callHook(vnode.componentInstance, 'deactivated')
}
vnode.componentInstance.$destroy()
}
}

watch是在我们改变props传入的值时,同时对this.cache缓存中的数据进行处理。

function pruneCache (cache: VNodeCache, filter: Function) {
for (const key in cache) {
const cachedNode: ?VNode = cache[key]
if (cachedNode) {
const name: ?string = getComponentName(cachedNode.componentOptions)
if (name && !filter(name)) {
pruneCacheEntry(cachedNode)
cache[key] = null
}
}
}
}

抽象组件没有实际的DOM元素,所以也就没有template模板,它会有一个render函数,我们就来看看里面进行了哪些操作。

  render () {
const vnode: VNode = getFirstComponentChild(this.$slots.default)
const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
if (componentOptions) {
// check pattern
const name: ?string = getComponentName(componentOptions)
if (name && (
(this.include && !matches(this.include, name)) ||
(this.exclude && matches(this.exclude, name))
)) {
return vnode
}
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
if (this.cache[key]) {
vnode.componentInstance = this.cache[key].componentInstance
} else {
this.cache[key] = vnode
}
vnode.data.keepAlive = true
}
return vnode
}

首先,调用getFirstComponentChild方法,来获取this.$slots.default中的第一个元素。

export function getFirstComponentChild (children: ?Array<VNode>): ?VNode {
return children && children.filter((c: VNode) => c && c.componentOptions)[0]
}

this.$slots.default中包含的是什么内容,我们在《slot和作用域插槽》中已经详细的做了讲解。

从上面的方法我们可以看到,在我们会过滤掉非自定义的标签,然后获取第一个自定义标签所对应的vnode

所以,如果keep-alive里面包裹的是html标签,是不会渲染的。

然后获取componentOptions

vdom——VNode中我们介绍过componentOptions包含五个元素

{ Ctor, propsData, listeners, tag, children }

function getComponentName (opts: ?VNodeComponentOptions): ?string {
return opts && (opts.Ctor.options.name || opts.tag)
}

通过getComponentName方法来获取组件名,然后判断该组件是否合法,

如果include不匹配或exclude匹配,则说明该组件不需要缓存,

此时直接返回该vnode

否则,vnode.key不存在则生成一个,存在则就用vnode.key作为key

然后把该vnode添加到this.cache中,并设置vnode.data.keepAlive = true

最终返回该vnode

以上只是render函数执行的过程,keep-alive本身也是一个组件,

render函数调用生成vnode后,同样会走__patch__。在创建和diff的过程中,

也会调用initprepatchinsertdestroy钩子函数。

不过,每个钩子函数中所做的处理,和普通组件有所不同。

  init (
vnode: VNodeWithData,
hydrating: boolean,
parentElm: ?Node,
refElm: ?Node
): ?boolean {
if (!vnode.componentInstance || vnode.componentInstance._isDestroyed) {
const child = vnode.componentInstance = createComponentInstanceForVnode(
vnode,
activeInstance,
parentElm,
refElm
)
child.$mount(hydrating ? vnode.elm : undefined, hydrating)
} else if (vnode.data.keepAlive) {
// kept-alive components, treat as a patch
const mountedNode: any = vnode // work around flow
componentVNodeHooks.prepatch(mountedNode, mountedNode)
}
},

keep-alive组件内调用__patch__时,如果render返回的vnode是第一次使用,

则走正常的创建流程,如果之前创建过且添加了vnode.data.keepAlive

则直接调用prepatch方法,且传入的新旧vnode相同。

  prepatch (oldVnode: MountedComponentVNode, vnode: MountedComponentVNode) {
const options = vnode.componentOptions
const child = vnode.componentInstance = oldVnode.componentInstance
updateChildComponent(
child,
options.propsData, // updated props
options.listeners, // updated listeners
vnode, // new parent vnode
options.children // new children
)
},

prepatch函数做了哪些工作,之前也详细的介绍过,这里就不多说了。

简单的总结,就是依据新vnode中的数据,更新组件内容。

  insert (vnode: MountedComponentVNode) {
if (!vnode.componentInstance._isMounted) {
vnode.componentInstance._isMounted = true
callHook(vnode.componentInstance, 'mounted')
}
if (vnode.data.keepAlive) {
activateChildComponent(vnode.componentInstance, true /* direct */)
}
},

在组件插入到页面后,如果是vnode.data.keepAlive则会调用activateChildComponent

这里面主要是调用子组件的activated钩子函数,并设置vm._inactive的标识状态。

  destroy (vnode: MountedComponentVNode) {
if (!vnode.componentInstance._isDestroyed) {
if (!vnode.data.keepAlive) {
vnode.componentInstance.$destroy()
} else {
deactivateChildComponent(vnode.componentInstance, true /* direct */)
}
}
}

在组件销毁时,如果是vnode.data.keepAlive返回true

则只调用deactivateChildComponent,这里面主要是调用子组件的deactivated钩子函数,

并设置vm._directInactive的标识状态。因为vnode.data.keepAlivetrue的组件,

是会被keep-alive缓存起来的,所以不会直接调用它的$destroy()方法,

上面我们也提到了,当keep-alive组件被销毁时,会触发它缓存中所有组件的$destroy()

因为keep-alive包裹的组件状态变化,还会触发其子组件的activateddeactivated钩子函数,

activateChildComponentdeactivateChildComponent也会做一些这方面的处理,细节大家可以自行查看。

组件的 keep-alive 简介的更多相关文章

  1. 构建简单的 C++ 服务组件,第 1 部分: 服务组件体系结构 C++ API 简介

    构建简单的 C++ 服务组件,第 1 部分: 服务组件体系结构 C++ API 简介 熟悉将用于 Apache Tuscany SCA for C++ 的 API.您将通过本文了解该 API 的主要组 ...

  2. React组件和生命周期简介

        React 简介----React 是 Facebook 出品的一套颠覆式的前端开发类库.为什么说它是颠覆式的呢? 内存维护虚拟 DOM 对于传统的 DOM 维护,我们的步骤可能是:1.初始化 ...

  3. iOS组件化开发-CocoaPods简介

    CocoaPods简介 任何一门开发语言到达一定阶段就会出现第三方的类库管理工具,比如Java的Maven.WEB的Webpack等.在iOS中类库的管理工具-CocoaPods. 利用CocoaPo ...

  4. Akka(17): Stream:数据流基础组件-Source,Flow,Sink简介

    在大数据程序流行的今天,许多程序都面临着共同的难题:程序输入数据趋于无限大,抵达时间又不确定.一般的解决方法是采用回调函数(callback-function)来实现的,但这样的解决方案很容易造成“回 ...

  5. .Net服务组件(ServicedComponent)简介及其使用

    .NET Enterprise Services 为企业应用程序提供重要的基础结构.COM+ 为企业环境中部署的组件编程模型提供服务结构.System.EnterpriseServices命名空间向 ...

  6. .NET Core 基于Quartz的UI可视化操作组件 GZY.Quartz.MUI 简介

    前言 最近在用Quartz做定时任务.虽然很方便,但是Quartz自己貌似是没有UI界面的..感觉操作起来 就很难受.. 查了一下,貌似有个UI组件 不过看了一下文档..直接给我劝退了..太麻烦了 我 ...

  7. 2.第一篇 k8s组件版本及功能简介

    文章转载自:https://mp.weixin.qq.com/s?__biz=MzI1MDgwNzQ1MQ==&mid=2247483772&idx=1&sn=a693d8a9 ...

  8. (day20)javaEE三大组件之一Servlet (简介(二)servletconfig,servletContext,session,cookie,request,response,out)

    javaEE是服务器编程,javaEE提供了服务器的接口让具体的服务器去创建实现的对象 JavaEE是sun公司为了解决企业级开发定义的一套技术,只提供了规范,具体的实现是由服务器完成的 servle ...

  9. day18(javaEE三大组件之一servlet(简介(一)))

    Servlet servlet是小型服务器语言,使用它可以处理前台传递来的信息,servlet进行处理后在响应给前台,其中servlet起到了关键性的作用.前端输入的信息可以持久化的存储在数据库中,并 ...

  10. VMware vSphere 组件和功能

    https://pubs.vmware.com/vsphere-50/index.jsp?topic=%2Fcom.vmware.vsphere.introduction.doc_50%2FGUID- ...

随机推荐

  1. requirejs配置代码示例

    requirejs大致用法:通过定义模板define()再通过require()或requirejs()加载模板,paths是路径,如果路径长可以把它赋一个短名称加入ruquire()中,shim是兼 ...

  2. cent os下搭建简单的服务器

    作为常和网络打交道的程序员,经常会遇到需要服务器的场合,比如搭建一个web服务器,一个代理服务器,又或者一个小型的游戏服务器. 我时常和朋友一起玩一款叫我的世界的游戏,为了能够长期稳定地联机玩,所以特 ...

  3. 【Coursera】Seventh Week

    Application Layer:Use the services of the TCP layer Quick Review Link Layer(Ethernet):gets the data ...

  4. python 删除列表中的字典元素

    data={"} ,{"}]} print("before:\n\n",data,"\n\n") temp=- k= for i in da ...

  5. android listview的HeadView左右切换图片(仿新浪,网易,百度等切换图片)

    首先我们还是看一些示例:(网易,新浪,百度) 显示效果都不错,可是手感就不一样了,百度最棒,网易还行,新浪就操作很不好,这里我说的是滑动切换图片.自己可以测试一下.不得不说牛叉的公司确实有哦牛叉的道理 ...

  6. C++ 多态性和虚函数

    2017-06-27 19:17:52 C++面向对象编程的一个重要的特性就是多态性,而多态性的实现需要依赖虚函数的帮助. 一.多态的作用: 隐藏实现细节,使得代码能够模块化: 接口重用,实现“一个接 ...

  7. SVN提交文件失败:系统找不到指定路径

    完成程序代码工作后,进行SVN的文件提交.先进行项目的更新,然后在修改的文件上进行提交操作,发现SVN弹出提示信息,“系统找不到指定路径”提交失败,如下图:                       ...

  8. wpf里窗体嵌入winform控件被覆盖问题

      问题1:嵌套Winform控件(ZedGraph)在WPF的ScrollViewer控件上,出现滚动条,无论如何设置该Winform控件都在顶层,滚动滚动条会覆盖其他WPF控件. 解决办法:在Sc ...

  9. php--------对象(object) 与 数组(array) 的转换

    php开发中常常用到数组,sql数据都是数组,数组和对象用的也是比较多的,常常相互转化,数组是PHP的灵魂,非常强大,面向对象编程也是挺方便的. /** * 数组 转 对象 * * @param ar ...

  10. Confluence 6 为站点启用匿名用户访问

    如果你希望你的站点能够被所有人看到,包括不需要登录就可以访问的用户.你必须为你的站点启用匿名用户访问权限才可以. 希望启用匿名用户访问你的站点: 在屏幕的右上角单击 控制台按钮 ,然后选择 Gener ...