一、先搞懂router view是干啥的?
在Vue项目里搞路由开发时,不少小伙伴会碰到“路由切换后组件没重新渲染、数据还留着旧的”这类麻烦事儿,这时候经常听到“给router - view加个key试试”的建议,那router - view加key到底有啥作用?哪些场景必须用?怎么选key才合理?今天咱们就把这些问题掰碎了聊聊~
router - view是Vue Router里的“路由出口”,就像个“占位符”,咱们在路由配置里写好哪个路径对应哪个组件,当路径匹配时,对应的组件就会被渲染到router - view所在的位置,比如配置了/home
对应Home组件、/about
对应About组件,页面里写<router - view></router - view>,访问不同路径时,这个位置就会动态换成对应的组件。
给router - view加key,核心作用是啥?
Vue渲染组件时,默认会“复用”相同的组件实例(毕竟复用能提升性能),但有些场景下,咱们不想要复用,得让组件“重新创建”,这时候key就起关键作用了——因为Vue会根据key判断两个节点是否“可复用”,key变了,就会销毁旧组件、创建新组件,给router - view加key,主要解决这两类问题:
强制组件实例“重建”,让生命周期重新执行
比如做“文章详情页”,路由是/article/:id
,不管id是1还是2,渲染的都是ArticleDetail组件,默认情况下,Vue会复用ArticleDetail的实例,这就导致:从/article/1切到/article/2时,ArticleDetail的created、mounted这些生命周期钩子不会重新执行,如果数据是在created里请求的,那拿到的还是/article/1的数据,页面自然不更新。
这时候给router - view加个key,比如<router - view :key="$route.fullPath"></router - view>
,因为/article/1和/article/2的fullPath不一样,key就不一样,Vue发现key变了,就会把旧的ArticleDetail组件销毁,重新创建新实例,这时候created、mounted这些钩子会重新执行,数据也能拿到最新的id对应内容。
解决路由切换后的“状态残留”问题
想象一下:组件里有个表单,用户填了一半切到其他路由,再切回来时,表单里的内容还在,这不是用户想要的,毕竟切路由相当于“换页面”,表单得清空重新填,但因为组件实例被复用了,状态也跟着保留下来。
给router - view加key后,组件重新创建,内部的data、表单状态都会被重置,自然就不会残留之前的内容了,再比如组件里有个弹窗组件,默认是关闭的,路由切换后弹窗突然是打开状态——加key让组件重建,就能避免这种尴尬。
哪些场景必须给router - view加key?
不是所有场景都需要加key,得看需求,这三类场景,加key几乎是“必须操作”:
同一路由组件,对应不同参数(动态路由场景)
最典型的就是“动态路由”,比如商品详情(/product/:id
)、文章详情(/article/:id
)、用户个人页(/user/:userId
)这类场景,路由参数变了,但组件本身没换,这时候不加key,组件实例复用,数据和生命周期都不更新,页面肯定出问题。
路由切换后,需要“重置组件状态”
比如做“多步骤表单”,不同路由对应不同步骤(像/form/step1
、/form/step2
),切换步骤后,上一步的输入内容得清空;或者组件里有依赖用户操作的临时状态(比如选中的选项、展开的菜单),路由切换后得恢复初始状态,这时候加key,让组件重建,状态自然就重置了。
组件依赖“全局状态/异步数据”,路由切换需重新计算
比如组件里的数据依赖用户权限(权限可能在路由切换后变化),或者依赖接口返回的动态配置,路由切换后,这些依赖可能变了,组件得重新获取数据、重新计算,但因为组件实例复用,生命周期不执行,数据就没法更新——加key强制重建,就能解决这个问题。
key的值怎么选才合理?
key的核心作用是“区分组件是否需要重建”,所以得选“能准确反映路由变化”的值,这几种选法最常用:
推荐用$route.fullPath
$route.fullPath包含了“路径 + 查询参数”(比如/list?page=1
的fullPath就是完整的带参数路径),只要路由的路径、参数、查询参数有一个变化,fullPath就会变,key也跟着变,这种方式覆盖场景最全,大部分项目里用这个就够了,
<router - view :key="$route.fullPath"></router - view>
举个例子:列表页带搜索参数,从/list?page=1
切到/list?page=2
,fullPath变了,key变了,组件重建,就能重新请求第二页的数据。
按需组合关键参数(route.params.id + $route.query.sort)
如果只关心“部分参数”的变化,比如文章详情页只关心id,列表页只关心page,那可以把关键参数拼起来当key。
<router - view :key="$route.params.id + $route.query.sort"></router - view>
但这种方式要小心:如果有其他参数变化也需要组件重建,这种“按需组合”可能漏掉,比如列表页还有个filter参数,你没加到key里,那filter变化时key不变,组件不会重建,数据就没法更新,所以除非你明确知道“只有这些参数影响组件重建”,否则优先用fullPath更保险。
千万别用固定值或随机数
有人图省事,写:key="1"
——这完全没用,因为key不变,组件还是复用;还有人用Math.random()当key,虽然每次key都变,但组件会频繁销毁重建,性能爆炸差,完全没必要,这两种写法,碰到了赶紧改!
不加key会踩哪些“大坑”?
如果该加key的时候没加,开发时很容易碰到这些让人头大的问题:
数据不更新,展示旧内容
路由参数都变了,组件里的数据请求还拿着旧参数,比如从/article/1切到/article/2,ArticleDetail组件的created里请求数据,结果因为实例复用,created没执行,请求还是基于id=1,页面显示的还是文章1的内容,用户一脸懵。
生命周期钩子“摆烂”不执行
组件里写了mounted钩子初始化图表、定时器,结果路由切换后,mounted根本不执行,图表还是旧的,定时器也没重新设置,功能直接失效。
状态残留,用户体验拉胯
表单输入的内容、弹窗的显示状态、下拉菜单的选中项,路由切换后还留在页面上,用户以为切到新页面了,结果看到旧状态,体验特别差,还容易造成误操作。
给router - view加key,这些细节要注意!
加key不是“随便加加”就行,得考虑性能、缓存这些细节:
性能权衡:别过度加key
加key会让组件销毁重建,频繁切换的话,性能肯定受影响,所以非必要场景别加!比如不同路由对应不同组件(/home对应Home,/about对应About),这种情况Vue本来就会销毁旧组件、创建新组件,加key纯属多余,还影响性能。
和keep - alive配合时,要更谨慎
如果router - view被keep - alive包裹(用来缓存组件实例,提升性能),这时候加key要特别注意——keep - alive是根据组件的name和key来判断是否缓存的,如果key选得不对,可能导致缓存失效,或者缓存了不该缓存的状态,比如你想缓存组件,但加了key导致每次都重建,那keep - alive就白用了,这时候得结合业务需求,平衡“缓存”和“重建”的关系。
确保key“变的时候真的变,不变的时候真不变”
选key的核心是“路由变化时key必须变,路由没变化时key不能变”,比如用fullPath,它能精准反映路由的所有变化;如果自己拼参数,得确保所有影响组件重建的参数都被包含进去,不然就会出现“该重建时没重建,不该重建时乱重建”的情况。
router - view的key,核心作用是控制组件实例是否复用,解决动态路由数据不更新、状态残留这些痛点,但要用对场景、选对key值,还要权衡性能,下次碰到路由切换后组件“不听话”的情况,不妨先检查下router - view的key加对了没~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。