一、Vue Router场景下,mounted啥时候会执行?
不少刚接触Vue Router的同学会碰到这样的困惑:明明在组件里写了mounted钩子,可路由切换后它有时候不执行,有时候又执行了,这到底咋回事?想让mounted在路由变化时按自己想法触发,得从Vue Router的工作逻辑、组件生命周期这些角度慢慢捋,今天就把“Vue Router里mounted的执行规律、不触发的原因、解决办法”这些问题拆明白。
先回忆下Vue组件的基础生命周期:mounted是组件**完成挂载**后触发的,也就是模板编译成DOM、插入页面之后,这时候能拿到真实DOM,做DOM操作、请求数据都很顺手。但结合Vue Router后,mounted啥时候执行,得看路由切换时组件会不会被“复用”,举个例子:假设做了个用户详情页,路由是/user/:id
,组件叫User,从/user/1
跳到/user/2
时,Vue Router默认会复用User组件的实例——因为这俩路由匹配的是同一个组件(User),只是路由参数(id)变了,这种情况下,组件没被销毁再重建,所以mounted这类“挂载阶段”的生命周期钩子,只会在组件第一次挂载时执行,后续参数变化时不会再触发。
还有个常见情况是
路由切换时mounted不触发,原因在哪?
搞清楚mounted不触发的场景,才能针对性解决,常见原因分两类:
组件复用(动态路由参数变化)
Vue Router的“动态路由匹配”(比如/user/:id
)设计,是为了复用组件实例来提升性能——毕竟销毁再重建组件挺费资源的,这时候路由参数变了,但组件还是同一个,所以mounted这类“初始化”钩子不会重复执行,举个🌰:User组件里mounted写了请求用户信息的逻辑,第一次进/user/1
时,mounted触发,请求到用户1的数据;但切到/user/2
时,组件没销毁,mounted不执行,这时候页面还显示用户1的信息,就会出问题。
keep-alive缓存导致
当路由组件被mounted
→ 离开执行deactivated
→ 再进入执行activated
,所以后续进入时,mounted不会再触发,取而代之的是activated,比如做 tabs 切换的页面,用keep-alive缓存tab内容,就会遇到这种情况。
想让mounted在路由变化时执行,咋处理?
不同原因对应不同解法,咱分情况说:
应对“组件复用(动态路由参数变化)”的办法
-
方法1:监听$route变化
组件里用watch监听$route
,当路由参数(比如params、query)变化时,执行原本mounted里的逻辑,举个代码例子:export default { data() { return { user: {} } }, mounted() { this.fetchUser(this.$route.params.id); // 首次进入执行 }, watch: { $route(to) { this.fetchUser(to.params.id); // 路由参数变化时,重新请求数据 } }, methods: { fetchUser(id) { // 调用接口获取用户信息 } } }
这种方法性能友好,因为不用销毁组件,只需要响应路由变化,适合大部分场景。
-
方法2:给
加key,强制组件重建
在路由出口()上绑定一个唯一的key,比如 $route.fullPath
(包含路径和参数的完整路径),这样每次路由变化时,key也会变,Vue会认为这是一个新的组件实例,旧组件销毁、新组件创建,mounted就会重新执行,代码长这样:<template> <div> <keep-alive> <router-view :key="$route.fullPath"></router-view> </keep-alive> </div> </template>
但要注意:这种方法会增加性能开销(组件频繁销毁重建),所以只适合简单场景,或者路由参数变化频繁但组件逻辑不复杂的情况。
应对“keep-alive缓存”的办法
-
方法1:用activated钩子替代mounted
如果组件被keep-alive缓存,后续进入时触发的是activated,所以可以把“每次进入组件都要执行”的逻辑(比如请求数据、初始化插件)放到activated里,原本mounted里的逻辑如果是“首次进入才执行”,可以保留;如果是“每次进入都要执行”,就移到activated,举个例子:export default { mounted() { // 这里写“只有第一次进入”才需要的逻辑,比如初始化第三方插件的基础配置 this.initPlugin(); }, activated() { // 这里写“每次进入(包括从缓存中激活)”都要执行的逻辑,比如刷新数据 this.fetchData(); }, methods: { initPlugin() { /* 初始化插件 */ }, fetchData() { /* 请求数据 */ } } }
如果有“离开组件时清理资源”的需求,原本destroyed里的逻辑要移到deactivated里,因为keep-alive缓存时destroyed不会触发。
-
方法2:排除keep-alive缓存
如果某个路由组件不需要缓存,可以在上用exclude属性,指定组件名(组件的name选项)。 <template> <div> <keep-alive exclude="User"> <router-view></router-view> </keep-alive> </div> </template>
这样User组件就不会被缓存,路由切换时mounted能正常触发。
实战中mounted结合Vue Router的常见场景
理解原理后,得落地到实际开发场景里,举几个常见例子:
场景1:动态路由参数变化时,重新请求数据
比如电商项目的商品详情页,路由是/product/:id
,首次进入时,mounted里请求商品1的数据;从商品1切到商品2时,组件复用,mounted不执行,这时候用watch $route来重新请求:
<template> <div> <h1>{{ product.title }}</h1> <p>{{ product.desc }}</p> </div> </template> <script> export default { name: 'Product', data() { return { product: {} } }, mounted() { this.getProduct(this.$route.params.id); }, watch: { $route(to) { this.getProduct(to.params.id); } }, methods: { getProduct(id) { // 调用接口:axios.get(`/api/product/${id}`) // 然后把数据赋值给this.product } } } </script>
场景2:路由切换时,初始化依赖DOM的第三方插件
比如在文章详情页用富文本编辑器(如Quill),需要在mounted里初始化编辑器(因为要拿到DOM元素),但如果路由切换时组件复用,mounted不执行,编辑器就不会更新,这时候要结合watch $route,在路由变化时先销毁旧编辑器,再初始化新的:
export default { mounted() { this.initQuill(); // 首次初始化编辑器 }, watch: { $route() { this.quill.destroy(); // 销毁旧编辑器实例 this.initQuill(); // 重新初始化 } }, methods: { initQuill() { this.quill = new Quill(this.$refs.editor, { /* 配置 */ }); } } }
场景3:结合keep-alive实现“缓存+强制刷新”
比如App首页用keep-alive缓存,提升切换tab的流畅度,但用户点“刷新”按钮时,要强制重新加载数据,这时候可以:
- 正常情况下,用activated获取数据(缓存时触发);
- 点击刷新时,通过修改
的key,让组件销毁重建,触发mounted。
代码思路:
<!-- 父组件:控制router-view的key --> <template> <div> <button @click="refresh">刷新首页</button> <keep-alive> <router-view :key="pageKey"></router-view> </keep-alive> </div> </template> <script> export default { data() { return { pageKey: 0 } }, methods: { refresh() { this.pageKey += 1; // key变化,强制组件重建 } } } </script> <!-- 首页组件Home --> <template> <div>{{ list.length }}</div> </template> <script> export default { data() { return { list: [] } }, mounted() { this.fetchList(); // 组件重建时触发,重新请求数据 }, methods: { fetchList() { /* 调接口 */ } } } </script>
容易混淆的点:mounted和路由导航守卫的区别
不少同学会把mounted和路由的导航守卫(比如beforeRouteEnter)搞混,这里简单对比下:
- 路由导航守卫(以beforeRouteEnter为例):是路由层面的钩子,在“进入路由前”触发,这时候组件实例还没创建(所以this拿不到),适合做“路由进入前的权限判断、预加载数据”等操作。
- mounted:是组件层面的钩子,在“组件挂载到DOM后”触发,这时候this能拿到组件实例和DOM,适合做“依赖DOM的操作(比如初始化插件)、请求数据并渲染到页面”等操作。
举个🌰:如果想在进入商品详情页前,先判断用户有没有权限,用beforeRouteEnter;如果想在商品详情页DOM渲染后,初始化轮播图,用mounted。
Vue Router里mounted的执行规律,核心是理解“组件复用”和“keep-alive缓存”这两个机制对生命周期的影响,遇到mounted不触发的问题,先判断是哪种场景,再选对应的解法:参数变化用watch $route或加key;keep-alive场景用activated或exclude,把这些逻辑理顺后,就能灵活控制mounted在路由切换时的行为,避免页面逻辑“不听话”的情况~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。