对keep-alive组件的理解

当在组件之间切换的时候,有时会想保持这些组件的状态,以避免反复重渲染导致的性能等问题,使用<keep-alive>包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。

描述

重新创建动态组件的行为通常是非常有用的,但是在有些情况下我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来,此时使用<keep-alive>包裹组件即可缓存当前组件实例,将组件缓存到内存,用于保留组件状态或避免重新渲染,和<transition>相似它,其自身不会渲染一个DOM元素,也不会出现在组件的父组件链中。

<keep-alive><component v-bind:is="currentComponent" class="tab"></component>
</keep-alive>

<keep-alive>包含的组件不会被再次初始化,也就意味着不会重走生命周期函数,<keep-alive>保持了当前的组件的状态,在第一次创建的时候回正常触发其创建生命周期,但是由于组件其实并未销毁,所以不会触发组件的销毁生命周期,而当组件在<keep-alive>内被切换时,它的activateddeactivated这两个生命周期钩子函数将会被对应执行。

export default {data: function() {return {}},activated: function(){console.log("activated");},deactivated: function(){console.log("deactivated");},
}

<keep-alive>可以接收3个属性做为参数进行匹配对应的组件进行缓存,匹配首先检查组件自身的name选项,如果name选项不可用,则匹配它的局部注册名称,即父组件components选项的键值,匿名组件不能被匹配,除了使用<keep-alive>props控制组件缓存,通常还可以配合vue-router在定义时的meta属性以及在template定义的<keep-alive>进行组件的有条件的缓存控制。

  • include: 包含的组件,可以为字符串,数组,以及正则表达式,只有匹配的组件会被缓存。
  • exclude: 排除的组件,以为字符串,数组,以及正则表达式,任何匹配的组件都不会被缓存,当匹配条件同时在includeexclude存在时,以exclude优先级最高。
  • max: 缓存组件的最大值,类型为字符或者数字,可以控制缓存组件的个数,一旦这个数字达到了,在新实例被创建之前,已缓存组件中最久没有被访问的实例会被销毁掉。
<!-- 包含 逗号分隔字符串 -->
<keep-alive include="a,b"><component :is="show"></component>
</keep-alive><!-- 包含 正则表达式 使用v-bind -->
<keep-alive :include="/a|b/"><component :is="show"></component>
</keep-alive><!-- 包含 数组 使用v-bind -->
<keep-alive :include="['a', 'b']"><component :is="show"></component>
</keep-alive><!-- 排除 逗号分隔字符串 -->
<keep-alive exclude="a,b"><component :is="show"></component>
</keep-alive><!-- 最大缓存量 数字 -->
<keep-alive :max="10"><component :is="show"></component>
</keep-alive>

<keep-alive>是用在其一个直属的子组件被开关的情形,如果在其中有v-for则不会工作,如果有上述的多个条件性的子元素,<keep-alive>要求同时只有一个子元素被渲染,通俗点说,<keep-alive>最多同时只能存在一个子组件,在<keep-alive>render函数中定义的是在渲染<keep-alive>内的组件时,Vue是取其第一个直属子组件来进行缓存。

const vnode: VNode = getFirstComponentChild(this.$slots.default);

实现

Vue<keep-alive>组件源码定义在dev/src/core/components/keep-alive.js,本次分析实现的commit id215f877
<keep-alive>初始化时的created阶段会初始化两个变量,分别为cachekeysmounted阶段会对includeexclude变量的值做监测。

export default {created () {this.cache = Object.create(null)this.keys = []},mounted () {this.$watch('include', val => {pruneCache(this, name => matches(val, name))})this.$watch('exclude', val => {pruneCache(this, name => !matches(val, name))})},
}

上边的$watch方法能够对参数的变化进行检测,如果include或者exclude的值发生变化,就会触发pruneCache函数,不过筛选的条件需要根据matches函数的返回值来决定,matches函数接收三种类型的参数stringRegExpArray,用以决定是否进行缓存。

function matches (pattern: string | RegExp | Array<string>, name: string): boolean {if (Array.isArray(pattern)) {return pattern.indexOf(name) > -1} else if (typeof pattern === 'string') {return pattern.split(',').indexOf(name) > -1} else if (isRegExp(pattern)) {return pattern.test(name)}/* istanbul ignore next */return false
}

pruneCache函数用以修建不符合条件的key值,每当过滤条件改变,都需要调用pruneCacheEntry方法从已有的缓存中修建不符合条件的key

function pruneCache (keepAliveInstance: any, filter: Function) {const { cache, keys, _vnode } = keepAliveInstancefor (const key in cache) {const cachedNode: ?VNode = cache[key]if (cachedNode) {const name: ?string = getComponentName(cachedNode.componentOptions)if (name && !filter(name)) {pruneCacheEntry(cache, key, keys, _vnode)}}}
}function pruneCacheEntry (cache: VNodeCache,key: string,keys: Array<string>,current?: VNode
) {const cached = cache[key]if (cached && (!current || cached.tag !== current.tag)) {cached.componentInstance.$destroy()}cache[key] = nullremove(keys, key)
}

在每次渲染即render时,首先获取第一个子组件,之后便是获取子组件的配置信息,获取其信息,判断该组件在渲染之前是否符合过滤条件,不需要缓存的便直接返回该组件,符合条件的直接将该组件实例从缓存中取出,并调整该组件在keys数组中的位置,将其放置于最后,如果缓存中没有该组件,那么将其加入缓存,并且定义了max并且缓存组件数量如果超出max定义的值则将第一个缓存的vnode移除,之后返回组件并渲染。

export default {render () {const slot = this.$slots.defaultconst vnode: VNode = getFirstComponentChild(slot)const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptionsif (componentOptions) {// check patternconst name: ?string = getComponentName(componentOptions)const { include, exclude } = thisif (// not included(include && (!name || !matches(include, name))) ||// excluded(exclude && name && matches(exclude, name))) {return vnode}const { cache, keys } = thisconst 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.keyif (cache[key]) {vnode.componentInstance = cache[key].componentInstance// make current key freshestremove(keys, key)keys.push(key)} else {cache[key] = vnodekeys.push(key)// prune oldest entryif (this.max && keys.length > parseInt(this.max)) {pruneCacheEntry(cache, keys[0], keys, this._vnode)}}vnode.data.keepAlive = true}return vnode || (slot && slot[0])}
}

每日一题

https://github.com/WindrunnerMax/EveryDay

参考

https://zhuanlan.zhihu.com/p/85120544
https://cn.vuejs.org/v2/api/#keep-alive
https://juejin.im/post/6844904082038063111
https://juejin.im/post/6844903919273918477
https://juejin.im/post/6844904099272458253
https://juejin.im/post/6844904160962281479
https://fullstackbb.com/vue/deep-into-keep-alive-in-vuejs/
https://blog.liuyunzhuge.com/2020/03/20/%E7%90%86%E8%A7%A3vue%E4%B8%ADkeep-alive%E7%BB%84%E4%BB%B6%E6%BA%90%E7%A0%81/

对keep-alive组件的理解相关推荐

  1. [react] 写一个react的高阶组件并说明你对高阶组件的理解

    [react] 写一个react的高阶组件并说明你对高阶组件的理解 定义高阶组件 import React, { Component } from 'react';const simpleHoc = ...

  2. [react] 说说你对“在react中,一切都是组件”的理解

    [react] 说说你对"在react中,一切都是组件"的理解 React采用组件化的思想,最小的组件单位就是原生HTML元素,采用JSX的语法声明组件的调用 React的虚拟DO ...

  3. 谈谈我对前端组件化中“组件”的理解,顺带写个Vue与React的demo

    谈谈我对前端组件化中"组件"的理解,顺带写个Vue与React的demo 前言 前端已经过了单兵作战的时代了,现在一个稍微复杂一点的项目都需要几个人协同开发,一个战略级别的APP的 ...

  4. 前端模块化、组件化理解

    前端模块化.组件化理解 随着前端项目规模的增大,我们要考虑提高开发效率,增加代码复用,以及降低模块功能耦合等问题. 模块化 模块化是软件工程中的一种思想,将大的问题拆解细分成一个个小问题,然后分而治之 ...

  5. React 之 高阶组件的理解

    1.基本概念 高阶组件是参数为组件,返回值为新组件的函数. 2.举例说明 ① 装饰工厂模式 组件是 react 中的基本单元,组件中通常有一些逻辑(非渲染)需要复用处理.这里我们可以用高阶组件对组件内 ...

  6. 丁鹿学堂:前端进阶学习vue3最新教程之vue的单文件组件深入理解

    在vue3中,template模版中的html代码,并不能直接在网页中执行,而是会在项目运行时,在浏览器中被编译为js 的函数去执行. 问题:在template模版中写html,体验很差,智能提示都不 ...

  7. 对受控组件和非受控组件的理解,以及应用场景?

    一.受控组件 受控组件,简单来讲,就是受我们控制的组件,组件的状态全程响应外部数据 举个简单的例子: class TestComponent extends React.Component {cons ...

  8. React高阶组件深入理解

    1. 基本概念 高阶组件是React 中一个很重要且较复杂的概念,高阶组件在很多第三方库(如Redux)中都被经常使用,即使你开发的是普通的业务项目,用好高阶组件也能显著提高你的代码质量. 高阶组件的 ...

  9. openstack placement 组件作用理解

    例如,一个资源提供者可以是一个计算节点,共享存储池,或一个IP分配池.placement服务跟踪每个供应商的库存和使用情况.例如,在一个计算节点创建一个实例的可消费资源如计算节点的资源提供者的CPU和 ...

最新文章

  1. 下面方框中,那些包括有220欧姆的电阻呢?
  2. spring源码分析之@ImportSelector、@Import、ImportResource工作原理分析
  3. 安装mysql.dox_linux虚拟机上装mysql数据库-Go语言中文社区
  4. python nonetype_python装饰器 ——@符号与“TypeError: ‘NoneType’ object is not callable” | 学步园...
  5. 【转】UNITY之LUA加密
  6. P5956-[POI2017]Podzielno【数学】
  7. 这本书强烈推荐看看!
  8. vscode用鼠标滚轮_前端开发神器 VSCode 使用总结
  9. Linux系统启动需要多长时间,Linux系统启动时间的极限优化是怎样进行的?
  10. 关于抽象和多态的总结
  11. Linux第二章:5.Xshell安装教程、使用Xshell6进行Linux远程登录
  12. 【vn.py】源码解析之 Dual Thrust 策略
  13. 通过NAT64实现ipv6 client 访问ipv4 Server
  14. 如何把二维表转成一维表
  15. C语言进阶——字符函数和字符串函数
  16. 【Matlab】彻底清除persistent变量
  17. 【用户画像】标签任务开发流程(源码之实体类、工具类、配置文件、DAO层)
  18. vue——双层循环嵌套
  19. HTML5游戏开发案例教程
  20. 爬虫—dy直播各个类别下直播数据

热门文章

  1. Go 语言里怎么正确实现枚举?答案藏着官方的源码里
  2. 关于Go程序错误处理的一些建议
  3. 前端http请求跨域问题解决
  4. 国产IT彪起来了,后浪要将前浪拍死在沙滩上
  5. 《SAFe 4.0参考指南:精益软件与系统工程的规模化敏捷框架》一 3.13 故事
  6. Linux下Weblogic创建域方法和步骤
  7. 基于PHP采集数据入库程序(二)
  8. 学习C语言必须知道的理论知识(第三章-C语句)
  9. Modelsim 后仿真操作步骤之二——用Modelsim单独进行后仿真
  10. 程序员的算法课(12)-使用通配符*,?等来查找字符串