Vue Router里keep-alive的include该怎么用?这些细节要注意!
做Vue项目时,你是不是遇到过“页面切换后状态丢失”的困扰?比如表单填了一半切走,回来全没了;列表页滚动到半路,切回来又得重新滑…这时候Vue里的keep-alive加上include配置就能解决这类问题,但include到底咋用?有哪些细节得注意?今天就把这块儿掰碎了讲明白~
keep-alive和include是干啥的?
先搞懂基础:keep-alive是Vue的内置组件,作用是缓存组件实例,平常组件切换时,Vue会销毁旧组件、创建新组件,有了keep-alive,被缓存的组件实例不会被销毁,切换回来时直接复用,省得重复渲染,性能和用户体验都能提升。
那include是keep-alive的属性,用来指定“哪些组件”要被缓存,打个比方,keep-alive是个“储物箱”,include就是“储物清单”,只让清单上的组件进箱子,其他组件不缓存。
include的值可以是字符串、数组、正则表达式,但得匹配组件的name
选项(组件里export default { name: 'XXX' }
里的name),比如你想缓存名叫MyForm
的组件,include就写'MyForm'
;多个组件用逗号分隔,像'MyForm,MyList'
。
include到底该怎么配置?
配置include得注意“匹配规则”和“组件name”这两点,不然容易踩坑:
(1)字符串形式
适合缓存少数组件,多个组件用英文逗号分隔,比如只缓存表单和列表组件:
<keep-alive include="MyForm,MyList"> <router-view></router-view> </keep-alive>
但要注意,这里的字符串必须和组件的name完全一致,大小写、拼写错一个都不行!
(2)数组形式
如果要动态控制缓存的组件(比如根据用户操作增删),用数组更灵活,得用v-bind
绑定,把include写成数组:
<keep-alive :include="['MyForm', 'MyList']"> <router-view></router-view> </keep-alive>
数组里存的是组件name的字符串,同样要和组件内的name严格匹配。
(3)正则形式
想批量匹配组件(比如所有名字带Form
的组件),可以用正则,但必须配合v-bind
,因为正则是JS表达式:
<keep-alive :include="/Form$/"> <router-view></router-view> </keep-alive>
这里正则/Form$/
表示“名字以Form结尾的组件”都会被缓存,比如MyForm
、OrderForm
这些组件。
划重点:组件必须显式写name选项!如果是单文件组件(.vue),没写name的话,Vue会默认取文件名(比如MyForm.vue
默认name是MyForm
),但如果是匿名组件或者用函数式组件,name可能不对,所以最好手动写name,避免匹配失败。
哪些场景适合用include?
不是所有页面都需要缓存,这些场景用include特别香:
(1)表单类页面
比如用户填收货地址、填个人信息的页面,填了一半切到其他页,回来还能接着填,这时候给表单组件设个name(比如AddressForm
),include里加上它,切换时组件实例被缓存,输入框内容、选择器状态都不会丢。
(2)列表 + 详情页
列表页下滑到第10条,点进详情页再返回,列表还能停在第10条位置,给列表组件设name(比如GoodsList
),include缓存它,就能保留滚动条位置和已加载的数据。
(3)多步骤流程页
像电商下单的“地址→支付→确认”三步,每一步是个组件,用include缓存这三个组件,用户回退步骤时,之前填的信息(比如地址、支付方式)还在,不用重新选。
举个实际例子:做一个“求职流程”页面,分“填简历→选岗位→确认投递”三步,每个步骤是独立组件,把这三个组件的name放进include数组,用户切换步骤时,每一步的输入内容、选择状态都能保留,体验感拉满~
用include时容易踩的坑,你中过几个?
配置看着简单,实际用的时候容易栽跟头,这些坑得避开:
(1)组件name“对不上”
最常见!比如组件里name写的是'MyForm'
,include里写成'myform'
(小写),或者拼错成'MyFrom'
,缓存直接失效,解决方法:组件name和include配置逐字对照,别偷懒!
(2)动态修改include不生效
比如想点击按钮,把某个组件加入缓存列表,用数组存name,但修改数组后include没反应,这是因为Vue对数组的“响应式”有要求——直接修改数组索引(比如arr[0] = 'xxx'
)不会触发更新,得用push
、splice
这些方法,或者用计算属性/ watch来维护数组。
举个错误案例:
// 直接改索引,不触发响应式 cachedNames[0] = 'NewComponent' // 正确做法:用push或splice cachedNames.push('NewComponent')
(3)和Vue Router结合时“搞混name”
路由配置里也有name(比如{ path: '/form', name: 'formRoute' }
),但keep-alive的include匹配的是“组件的name”,不是“路由的name”!很多人误以为路由name和组件name是一回事,结果缓存失败,解决方法:路由组件里必须单独写name选项,和路由name没关系,
// 路由组件MyForm.vue里的配置 export default { name: 'MyForm', // 这个name给keep-alive匹配用 // ...其他逻辑 }
(4)缓存后生命周期“变了”
组件被缓存后,created
、mounted
这些钩子只会在第一次创建时执行,之后切换回来不会再执行,这时候得用activated
和deactivated
钩子:组件被激活(从缓存中取出复用)时触发activated
,被缓存时触发deactivated
,如果想每次切换回来都执行逻辑,得把代码放到activated
里。
比如表单组件想每次激活时请求最新的下拉选项,就得这么写:
export default { name: 'MyForm', activated() { this.fetchOptions() // 每次激活时请求数据 }, methods: { fetchOptions() { ... } } }
include和exclude怎么选?
keep-alive还有个属性叫exclude
,作用是“排除哪些组件不缓存”,选include还是exclude,看场景:
- 大部分组件要缓存,只有少数不缓存→用
exclude
,比如后台管理系统,90%页面需要缓存,只有登录页、404页不需要,就给exclude
设这两个组件的name。 - 大部分组件不缓存,只有少数要缓存→用
include
,比如官网类项目,只有几个表单页需要缓存,其他页面切换时销毁,就用include
指定这几个表单组件。
举个栗子:做一个博客网站,文章列表和文章详情页不需要缓存(每次刷新看最新内容),但“写评论”的弹窗组件(或单独页面)需要缓存用户输入的内容,这时候用include
指定评论组件的name,比用exclude
排除所有页面更简单。
结合Vue Router的实际案例:多标签页缓存
很多后台系统有“多标签页”功能(比如点击菜单打开多个标签,每个标签对应一个路由),需要缓存用户打开过的标签页,这时候include+Vue Router的组合就能实现:
步骤1:给路由组件加name
每个路由对应的组件(比如PageA.vue
、PageB.vue
)里显式写name:
// PageA.vue export default { name: 'PageA', // ... }
步骤2:维护缓存的组件name数组
在父组件(比如Layout.vue
)里,用数组cachedTabs
存当前要缓存的组件name,然后绑定给keep-alive的include:
<template> <div class="layout"> <keep-alive :include="cachedTabs"> <router-view></router-view> </keep-alive> <div class="tabs"> <!-- 标签页切换按钮,点击时更新cachedTabs --> </div> </div> </template> <script> export default { data() { return { cachedTabs: [] // 初始为空 } }, watch: { // 监听路由变化,把当前组件的name加入cachedTabs '$route'(to) { const componentName = to.matched[0].components.default.name if (!this.cachedTabs.includes(componentName)) { this.cachedTabs.push(componentName) } } } } </script>
步骤3:关闭标签时移除缓存
当用户关闭某个标签页时,从cachedTabs
数组里删掉对应的组件name,这样该组件就不再被缓存:
methods: { closeTab(tabName) { const index = this.cachedTabs.indexOf(tabName) if (index > -1) { this.cachedTabs.splice(index, 1) } } }
这样一来,用户打开的标签页会被缓存,关闭后缓存清除,完美实现多标签页的状态保留~
keep-alive的include是个“精准控制缓存”的利器,但得注意组件name匹配、配置形式、和Vue Router的配合这些细节,下次再碰到“状态丢失”的问题,先想想include怎么用,把组件按需求圈进缓存清单里,用户体验和性能都能UpUp~要是还有疑问,评论区随时聊~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。