位置: IT常识 - 正文

Vue中keep-alive原理(vue keep-alive实现原理)

编辑:rootadmin
Vue中keep-alive原理 定义

推荐整理分享Vue中keep-alive原理(vue keep-alive实现原理),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:vue中keepalive的用法优缺点,vue keep-live,keepalived vue,vue中keepalive的用法优缺点,vue中keepalive原理,vue keep-live,vue中keepalive原理,vue中keep alive,内容如对您有帮助,希望把文章链接给更多的朋友!

keep-alive是Vue中内置的一个抽象组件。它自身不会渲染一个 DOM 元素,也不会出现在父组件链中。当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。

keep-alive是用来缓存组件的,比如我们有个列表页,在点击详情页之后,如果返回之后不想刷新列表页,就可以用keep-alive组件进行缓存。除此以外,还有很多应用场景。

用法

用法1:我们想要缓存某个组件,只要用keep-alive组件将其包裹就行。

<keep-alive> <component></component></keep-alive>

用法2:包裹component组件缓存动态组件,或者包裹router-view缓存路由页面,也就是keep-alive配合路由守卫(元信息)实现缓存。

比如常在router.js路由表里定义好哪些页面需要缓存,就可以通过下面这样实现了:

{path: "/index",name: 'index', component: () => import(/* webpackChunkName: "index" */ '@/pages/index'),meta: {title: '首页', keepAlive: true}}<keep-alive> <router-view v-if="$route.meta.keepAlive"></router-view></keep-alive><router-view v-if="!$route.meta.keepAlive && isRouterAlive"></router-view>属性include - 逗号分隔字符串或正则表达式或一个数组来表示。只有名称匹配的组件会被缓存。exclude - 逗号分隔字符串或正则表达式或一个数组来表示。任何名称匹配的组件都不会被缓存。max - 数字。最多可以缓存多少组件实例。

include 和 exclude 属性允许组件有条件地缓存:

<!-- 逗号分隔字符串 --><keep-alive include="a,b"> <component :is="view"></component></keep-alive><!-- 正则表达式 (使用 `v-bind`) --><keep-alive :include="/a|b/"> <component :is="view"></component></keep-alive><!-- 数组 (使用 `v-bind`) --><keep-alive :include="['a', 'b']"> <component :is="view"></component></keep-alive>

注意:想要缓存的组件一定要给定name属性,并且要和include,exclude给定的值一致

生命周期钩子

keep-alive提供了两个生命钩子,分别是activated与deactivated。

Vue中keep-alive原理(vue keep-alive实现原理)

因为keep-alive会将组件保存在内存中,并不会销毁以及重新创建,所以不会重新调用组件的created等方法,需要用activated与deactivated这两个生命钩子来得知当前组件是否处于活动状态。

组件一旦被keep-alive缓存,那么再次渲染的时候就不会执行 created、mounted 等钩子函数。使用keep-alive组件后,被缓存的组件生命周期会多activated和deactivated 两个钩子函数,它们的执行时机分别是keep-alive包裹的组件激活时调用和停用时调用。

源码export default { name: 'keep-alive', abstract: true, props: { include: [String, RegExp, Array], exclude: [String, RegExp, Array], max: [String, Number] }, created () { this.cache = Object.create(null) this.keys = [] }, destroyed () { for (const key in this.cache) { pruneCacheEntry(this.cache, key, this.keys) } }, 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 const vnode = getFirstComponentChild(slot) /* 获取该组件节点的componentOptions */ const componentOptions = vnode && vnode.componentOptions if (componentOptions) { /* 获取该组件节点的名称,优先获取组件的name字段,如果name不存在则获取组件的tag */ const name = getComponentName(componentOptions) const { include, exclude } = this /* 如果name不在inlcude中或者存在于exlude中则表示不缓存,直接返回vnode */ if ( (include && (!name || !matches(include, name))) || // excluded (exclude && name && matches(exclude, name)) ) { return vnode } const { cache, keys } = this const key = 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 (cache[key]) { vnode.componentInstance = cache[key].componentInstance // make current key freshest remove(keys, key) keys.push(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]) }}

可以看到,它有3个属性,即有3个props。此外,它有created,destroyed,mounted,render四个钩子。

原理created和destroyed钩子created钩子会创建一个cache对象,用来作为缓存容器,保存vnode节点。destroyed钩子则在组件被销毁的时候清除cache缓存中的所有组件实例。created () { /* 缓存对象 */ this.cache = Object.create(null) this.keys = []},/* destroyed钩子中销毁所有cache中的组件实例 */destroyed () { for (const key in this.cache) { pruneCacheEntry(this.cache, key, this.keys) }},render钩子

keep-alive实现缓存的核心代码就在这个钩子函数里。

先获取到插槽里的内容调用getFirstComponentChild方法获取第一个子组件,获取到该组件的name,如果有name属性就用name,没有就用tag名。/* 获取该组件节点的名称 */const name = getComponentName(componentOptions)/* 优先获取组件的name字段,如果name不存在则获取组件的tag */function getComponentName (opts: ?VNodeComponentOptions): ?string {return opts && (opts.Ctor.options.name || opts.tag)}接下来会将这个name通过include与exclude属性进行匹配,匹配不成功(说明不需要进行缓存)则不进行任何操作直接返回这个组件的 vnode(vnode是一个VNode类型的对象),否则的话走下一步缓存。匹配:/* 检测name是否匹配 */function matches (pattern: string | RegExp, name: string): boolean { if (typeof pattern === 'string') { /* 字符串情况,如a,b,c */ return pattern.split(',').indexOf(name) > -1 } else if (isRegExp(pattern)) { /* 正则 */ return pattern.test(name) } /* istanbul ignore next */ return false}

检测include与exclude属性匹配的函数很简单,include与exclude属性支持字符串如"a,b,c"这样组件名以逗号隔开的情况以及正则表达式。matches通过这两种方式分别检测是否匹配当前组件。

const { include, exclude } = this/* 如果name与include规则不匹配或者与exclude规则匹配则表示不缓存,直接返回vnode */if ( (include && (!name || !matches(include, name))) || // excluded (exclude && name && matches(exclude, name))) { return vnode}缓存机制:接下来的事情很简单,根据key在this.cache中查找,如果存在则说明之前已经缓存过了,直接将缓存的vnode的componentInstance(组件实例)覆盖到目前的vnode上面。否则将vnode存储在cache中。最后返回vnode(有缓存时该vnode的componentInstance已经被替换成缓存中的了)。缓存的处理:/* 如果命中缓存,则直接从缓存中拿 vnode 的组件实例 */if (cache[key]) { vnode.componentInstance = cache[key].componentInstance /* 调整该组件key的顺序,将其从原来的地方删掉并重新放在最后一个 */ remove(keys, key) keys.push(key)} /* 如果没有命中缓存,则将其设置进缓存 */else { cache[key] = vnode keys.push(key) /* 如果配置了max并且缓存的长度超过了this.max,则从缓存中删除第一个 */ if (this.max && keys.length > parseInt(this.max)) { pruneCacheEntry(cache, keys[0], keys, this._vnode) }}/* 最后设置keepAlive标记位 */vnode.data.keepAlive = true

命中缓存时会直接从缓存中拿 vnode 的组件实例,此时重新调整该组件key的顺序,将其从原来的地方删掉并重新放在this.keys中最后一个。

如果没有命中缓存,即该组件还没被缓存过,则以该组件的key为键,组件vnode为值,将其存入this.cache中,并且把key存入this.keys中。此时再判断this.keys中缓存组件的数量是否超过了设置的最大缓存数量值this.max,如果超过了,则把第一个缓存组件删掉。

为什么要删除第一个缓存组件并且为什么命中缓存了还要调整组件key的顺序?这其实应用了一个缓存淘汰策略LRU: LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

this.keys的逻辑:

将新数据从尾部插入到this.keys中每当缓存命中(即缓存数据被访问),则将数据移到this.keys的尾部当this.keys满的时候,将头部的数据丢弃mounted钩子

在这个钩子函数里,调用了pruneCache方法,以观测 include 和 exclude 的变化。

mounted () { this.$watch('include', val => { pruneCache(this, name => matches(val, name)) }) this.$watch('exclude', val => { pruneCache(this, name => !matches(val, name)) }) },watch: { /* 监视include以及exclude,在被修改的时候对cache进行修正 */ include (val: string | RegExp) { pruneCache(this.cache, this._vnode, name => matches(val, name)) }, exclude (val: string | RegExp) { pruneCache(this.cache, this._vnode, name => !matches(val, name)) }},

如果include 或exclude 发生了变化,即表示定义需要缓存的组件的规则或者不需要缓存的组件的规则发生了变化,那么就执行pruneCache函数,函数如下:

function pruneCache (keepAliveInstance, filter) { const { cache, keys, _vnode } = keepAliveInstance for (const key in cache) { const cachedNode = cache[key] if (cachedNode) { const name = getComponentName(cachedNode.componentOptions) if (name && !filter(name)) { pruneCacheEntry(cache, key, keys, _vnode) } } }}

在该函数内对this.cache对象进行遍历,取出每一项的name值,用其与新的缓存规则进行匹配,如果匹配不上,则表示在新的缓存规则下该组件已经不需要被缓存,则调用pruneCacheEntry函数将其从this.cache对象删除即可。

总结

Vue.js内部将DOM节点抽象成了一个个的VNode节点,keep-alive组件的缓存也是基于VNode节点的而不是直接存储DOM结构。它将满足条件(pruneCache与pruneCache)的组件在cache对象中缓存起来,在需要重新渲染的时候再将vnode节点从cache对象中取出并渲染。

本文链接地址:https://www.jiuchutong.com/zhishi/297724.html 转载请保留说明!

上一篇:Vue语法与标签的使用(vue fragment标签)

下一篇:【vue】 配置代理(vue3.0配置代理)

  • 教师资格证待支付在哪里支付(教师资格证待支付状态会等多久)

    教师资格证待支付在哪里支付(教师资格证待支付状态会等多久)

  • 双方都加不了微信怎么回事(微信双方都加不上好友怎么办)

    双方都加不了微信怎么回事(微信双方都加不上好友怎么办)

  • 荣耀play4pro屏幕刷新率是多少呢(荣耀play4PRO屏幕厂商怎么查)

    荣耀play4pro屏幕刷新率是多少呢(荣耀play4PRO屏幕厂商怎么查)

  • 苹果三代耳机触摸在哪里(苹果三代耳机触摸灵敏度可以调整么)

    苹果三代耳机触摸在哪里(苹果三代耳机触摸灵敏度可以调整么)

  • 华为nova7pro支持防水吗(华为nova7pro支持5G吗)

    华为nova7pro支持防水吗(华为nova7pro支持5G吗)

  • qq怎么才算访问了空间(qq怎么才算访问别人空间)

    qq怎么才算访问了空间(qq怎么才算访问别人空间)

  • 电脑文件怎么解压到桌面(电脑文件怎么解压成软件)

    电脑文件怎么解压到桌面(电脑文件怎么解压成软件)

  • airpodsh1芯片是几代(airpods1芯片型号)

    airpodsh1芯片是几代(airpods1芯片型号)

  • ftp服务器是什么意思(ftp服务器有什么功能)

    ftp服务器是什么意思(ftp服务器有什么功能)

  • 华为手机广角怎么开(华为手机广角怎么设置)

    华为手机广角怎么开(华为手机广角怎么设置)

  • 进程打印数据什么状态(进程打印数据什么意思呀)

    进程打印数据什么状态(进程打印数据什么意思呀)

  • ipad照片图库怎么删除(ipad图库里的照片怎么编辑为jpg)

    ipad照片图库怎么删除(ipad图库里的照片怎么编辑为jpg)

  • 苹果显示lte网络是什么意思(苹果显示LTE网络)

    苹果显示lte网络是什么意思(苹果显示LTE网络)

  • 5d3和5d4的区别(佳能5d3和5d4的区别)

    5d3和5d4的区别(佳能5d3和5d4的区别)

  • 淘宝性别年龄在哪里看(淘宝性别年龄在哪设置)

    淘宝性别年龄在哪里看(淘宝性别年龄在哪设置)

  • 华为mate30隔空设置(华为手机mate30隔空操作功能怎样找)

    华为mate30隔空设置(华为手机mate30隔空操作功能怎样找)

  • 微信换绑手机号频繁要多久(微信换绑手机号后原来的手机号可以注册新的微信吗)

    微信换绑手机号频繁要多久(微信换绑手机号后原来的手机号可以注册新的微信吗)

  • fxaa是什么(fxaa smaa)

    fxaa是什么(fxaa smaa)

  • 咸鱼的评价可以删除吗(咸鱼的评价可以在淘宝上显示吗)

    咸鱼的评价可以删除吗(咸鱼的评价可以在淘宝上显示吗)

  • 趣步糖果怎么交易(趣步上的糖果怎么卖出)

    趣步糖果怎么交易(趣步上的糖果怎么卖出)

  • Word文档中怎么输入上标下标(word文档中怎么加入分隔线)

    Word文档中怎么输入上标下标(word文档中怎么加入分隔线)

  • 苹果xr和x手机壳通用吗(苹果xr手机壳跟x是不是一样的)

    苹果xr和x手机壳通用吗(苹果xr手机壳跟x是不是一样的)

  • 监控系统关闭开启步骤(监控系统关闭后如何开启)

    监控系统关闭开启步骤(监控系统关闭后如何开启)

  • 〖Python网络爬虫实战㉔〗- Ajax数据爬取之Ajax 分析案例(python网络爬虫技术)

    〖Python网络爬虫实战㉔〗- Ajax数据爬取之Ajax 分析案例(python网络爬虫技术)

  • 需要考虑的税务问题
  • 税控盘是什么东西多少钱
  • 高新技术企业加计抵减
  • 取得虚开普票怎样补增值税
  • 小企业需要计提法定盈余公积吗
  • 资金账户托管协议
  • 税务局开专票作废重开及退税流程
  • 旅行社支付导游费怎么算
  • 购进用于研发的国产设备进项税可以抵扣吗
  • 违约金收入需要缴纳印花税吗
  • 小规模纳税人购车是怎么抵税的
  • 进货有商业折扣商品怎么入库
  • 单位购买办公用品可以抵扣吗
  • 发行债券支付的费用要减吗
  • 研发项目领原料加工成产品会计处理是怎样的?
  • 取得虚开增值税专用发票罪量刑标准
  • 广告位的费用
  • 预付款发票可以入费用吗
  • 投资性房地产收取租金增值税税率
  • 怎么看是不是专用发票
  • 酒店预收款是什么意思
  • 牛奶的增值税普通发票
  • 小企业出售无形资产发生的净损失应当计入什么科目
  • 技嘉主板bios更新后无法开机
  • 项目的投入资金预算表
  • 企业所得税汇算清缴表
  • SIMETER.EXE - SIMETER是什么进程 有什么用
  • 教学用具属于什么项目类别
  • php字符串变量
  • 新公司免税额度是多少
  • 物流公司的收入怎么记账
  • 深入理解php内核
  • 计提利息会计分录怎么做
  • 应收票据利息会计科目
  • 上传图片照片
  • windows安装无法继续,若要安装请重新启动
  • vue清空input file的值
  • 商贸公司对加工的影响
  • 2019年4月1日降低城镇职工什么单位缴费比例
  • 会计实务中的计提是什么意思
  • 在建工程发生的非正常损失计入哪
  • 未来的现金流折现
  • 用库存现金支付职工医药费用69元,会计人员
  • 医院绿化方案
  • 设备维修三种形式
  • 买理财产品的风险
  • 进口货物的会计分录
  • 固定资产的更新改造支出计入什么科目
  • 溢价发行可转换债券 利息调整在贷方吗
  • 发给客户的红包是什么费用
  • 转账显示未认证
  • 企业财务费用属于
  • 多计提房产税怎么冲销
  • 应付账款一直挂着,怎么消掉
  • 场地租赁费属于什么税收分类编码
  • 建账时都要建哪些科目
  • 商业企业进货会计分录
  • mysql时间语句
  • mysql有外键数据输入
  • mysql字符集的作用
  • 台式电脑二级网页打不开怎么办
  • redhat 7.0
  • slserv.exe - slserv进程是什么意思
  • win10预览版好吗
  • 苹果笔记本安装win10
  • macbook怎么开启
  • 开机提示windows即将过期
  • windows8鼠标没反应怎么办
  • 'GL_COMBINE_ARB' : undeclared 'GL_RGB_SCALE_ARB' : undeclared 问题
  • handle thread
  • opengl 缓存
  • jquery的方法有哪些
  • Metaio in Unity3d 教学--- 二. 创建自己的Application
  • js实现功能
  • 简单的安卓代码
  • 怎样纳税申报和缴纳
  • 大渡口在重庆吗
  • 怎么查个税交了多久
  • 应交增值税减免税款借贷方向
  • 应纳税所得额怎么求公式
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设