Vue2 怎么监听路由变化?不同场景下该用啥方法?

2026-06-29 02:54:20

Vue2 怎么监听路由变化?不同场景下该用啥方法?

做Vue项目的时候,经常碰到“切换路由后要更新数据”“进入路由前判断权限”这类需求,比如后台管理系统切换左侧菜单(对应不同路由),要刷新表格数据;或者进入付费课程页面,得先判断用户有没有购买权限,那在Vue2里,到底咋监听路由变化?不同场景下选哪种方法更顺手?今天拆解三种常用思路,结合实际例子讲明白~

全局路由守卫:管控整个应用的路由跳转

全局路由守卫是通过路由实例(router)的方法来注册的,作用范围是整个Vue应用的所有路由,最常用的有两个:beforeEach(路由跳转前拦截)和afterEach(路由跳转后处理)。

beforeEach:跳转前的“门禁”逻辑

想象一下后台系统的登录拦截场景:用户没登录的话,除了登录页,其他页面都不能进,这时候用beforeEach就很合适,在项目的router.js里这么写:

import Vue from 'vue'

import Router from 'vue-router'

import Login from '@/views/Login.vue'

import Dashboard from '@/views/Dashboard.vue'

Vue.use(Router)

const router = new Router({

routes: [

{ name: 'Login', path: '/login', component: Login },

{ name: 'Dashboard', path: '/dashboard', component: Dashboard }

]

})

// 全局前置守卫:每次路由跳转前都会触发

router.beforeEach((to, from, next) => {

const isLogin = localStorage.getItem('token') // 假设用localStorage存登录态

if (to.name !== 'Login' && !isLogin) {

// 没登录,且要去的不是登录页 → 强制跳登录

next({ name: 'Login' })

} else {

// 已登录,或者要去的是登录页 → 放行

next()

}

})

export default router

这里的to是目标路由对象(要跳转到哪),from是当前路由对象(从哪跳过来),next是必须调用的函数——调用next()表示“放行”,调用next({ name: 'xxx' })表示“强制跳转到xxx路由”,调用next(false)表示“留在当前页面”。

如果要做更细的权限控制(比如某些页面只有管理员能进),可以给路由配置meta字段,再在beforeEach里判断:

routes: [

{

name: 'AdminPage',

path: '/admin',

component: AdminPage,

meta: { requiresAdmin: true } // 标记该页面需要管理员权限

}

]

router.beforeEach((to, from, next) => {

const isLogin = localStorage.getItem('token')

const isAdmin = localStorage.getItem('role') === 'admin'

if (to.meta.requiresAdmin) {

if (isLogin && isAdmin) {

next()

} else {

next({ name: 'Forbidden' }) // 没有权限跳403页面

}

} else {

// 其他页面走普通登录拦截逻辑...

next()

}

})

afterEach:跳转后的“后置处理”

afterEach没有next参数(因为路由已经跳转完成了),适合做不影响跳转的后置操作,比如埋点统计、页面加载进度条关闭。

举个埋点的例子:记录用户访问每个页面的路径和时间,可以在beforeEach里记录“进入时间”,在afterEach里计算时长并上报:

let enterTime = 0

router.beforeEach((to, from, next) => {

enterTime = Date.now() // 记录进入当前路由的时间

next()

})

router.afterEach((to, from) => {

const duration = Date.now() - enterTime // 计算停留时长

console.log(`用户从${from.path}跳到${to.path},停留了${duration}毫秒`)

// 埋点上报代码:把to.path、duration等信息发给后端

})

适用场景:全局权限控制、所有页面的埋点/统计、全局Loading进度条(跳转前显示,跳转后隐藏)等跨页面的全局逻辑。

组件内路由守卫:精准控制单个组件的生命周期

如果需求是“某个组件进入/更新/离开时,执行专属逻辑”,进入商品详情页时请求数据”“路由参数变化时更新数据”“离开编辑页时提示保存”,这时候用组件内路由守卫更精准,每个Vue组件可以定义三个守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave。

beforeRouteEnter:进入组件前触发(组件实例还没创建)

假设做一个商品详情页,路由是/product/:id,进入页面时要根据id请求商品数据,但这个守卫触发时,组件实例(this)还没创建,所以请求数据后要通过next的回调访问组件实例:

export default {

name: 'ProductDetail',

data() {

return {

product: {} // 存储商品数据

}

},

// 进入组件前触发

beforeRouteEnter(to, from, next) {

// 这里this是undefined,因为组件还没创建

axios.get(`/api/product/${to.params.id}`)

.then(res => {

// next的回调函数里,vm就是组件实例(相当于this)

next(vm => {

vm.product = res.data

})

})

}

}

beforeRouteUpdate:路由参数变化但组件复用(组件实例已存在)

还是商品详情页的例子:如果从/product/1跳到/product/2,因为路由结构一样(都是/product/:id),Vue会复用组件实例(不会销毁再重建),这时候要重新请求数据,就用beforeRouteUpdate:

beforeRouteUpdate(to, from, next) {

// 这里this已经存在(组件实例已创建)

this.product = {} // 先清空旧数据

axios.get(`/api/product/${to.params.id}`)

.then(res => {

this.product = res.data

next() // 必须调用next放行路由跳转

})

}

这个守卫的核心是:路由参数变化但组件没销毁时触发,适合处理“组件复用下的更新逻辑”。

beforeRouteLeave:离开组件前触发(可拦截跳转)

用户在表单页填了内容,离开前要提示“是否保存?”,就用beforeRouteLeave:

export default {

name: 'FormPage',

data() {

return {

formEdited: false, // 标记表单是否修改过

formData: {}

}

},

methods: {

handleInput() {

this.formEdited = true

}

},

// 离开组件前触发

beforeRouteLeave(to, from, next) {

if (this.formEdited) {

const confirm = window.confirm('表单还没保存,确定要离开吗?')

if (confirm) {

next() // 确定离开 → 放行

} else {

next(false) // 取消离开 → 留在当前页面

}

} else {

next() // 没修改过 → 直接放行

}

}

}

适用场景:单个组件的初始化(如根据路由参数请求数据)、组件复用后的更新、离开前的确认/数据清理等组件专属的路由交互逻辑。

watch 监听 $route 对象:灵活响应路由变化

如果需求是“组件内对路由变化做灵活响应”,只关心路由参数里的id变化”“根据路由路径变化显示不同Tab”,用watch监听$route对象更自由,Vue组件的watch选项可以监听$route的变化,还能针对$route的某个属性(如params、query、path)做细粒度监听。

监听路由参数的变化

比如用户信息页路由是/user/:id,每次id变化都要重新请求用户数据:

export default {

name: 'UserInfo',

data() {

return {

user: {}

}

},

watch: {

// 监听$route.params.id的变化

'$route.params.id'(newId, oldId) {

this.getUser(newId)

}

},

methods: {

getUser(id) {

axios.get(`/api/user/${id}`)

.then(res => {

this.user = res.data

})

}

}

}

监听整个$route的变化

如果要根据路由的整体变化做复杂逻辑(比如从商品页跳购物车时记录浏览轨迹):

watch: {

$route(to, from) {

if (to.path === '/cart' && from.path === '/product') {

// 从商品页跳到购物车,记录用户浏览的商品id

this.recordTrack(from.params.id)

}

}

}

和组件内守卫的区别:组件内守卫是“生命周期式”的钩子(和组件创建、更新、销毁强绑定),而watch更像“响应式逻辑”——不需要和组件生命周期强关联,想监听哪部分就监听哪部分,写法更自由。

适用场景:组件内对路由变化的“局部响应”,比如根据搜索参数(query)变化刷新列表、根据路由切换显示不同Tab等组件内的灵活逻辑。

不同方法怎么选?看场景匹配度

最后总结下三种方法的核心区别和适用场景,帮你快速做选择:

方法

作用范围

核心特点

典型场景

全局路由守卫(beforeEach/afterEach)

整个Vue应用

管控所有路由的跳转逻辑

全局权限控制、全局埋点、全局Loading

组件内路由守卫(beforeRouteEnter/Update/Leave)

单个组件

和组件生命周期强绑定,处理组件专属的路由交互

组件初始化请求数据、组件复用更新、离开前确认

watch $route

单个组件

灵活监听路由的任意部分,响应式处理

根据路由参数/查询参数变化更新数据、路由切换时的局部逻辑

举个综合例子辅助理解:做一个博客系统,包含“文章列表页(/article/list)”和“文章详情页(/article/detail/:id)”。

全局守卫:用beforeEach判断用户是否登录(如果有会员文章需要登录才能查看)。

文章详情组件内:用beforeRouteEnter在进入时请求文章数据;如果从/article/detail/1跳到/article/detail/2(组件复用),用beforeRouteUpdate更新数据;离开详情页时,用beforeRouteLeave提示“是否要收藏这篇文章?”。

文章列表组件:如果有搜索功能(路由带query,如/article/list?keyword=Vue),用watch监听$route.query.keyword,变化时重新请求文章列表。

这样不同层面的路由监听配合起来,整个应用的路由逻辑就清晰又高效啦~

常见问题:监听$route不生效咋办?

有人会碰到“明明写了watch $route,但路由变化时没触发”的情况,大概率是监听目标写错了:

如果要监听路由参数id的变化,得写'$route.params.id',而不是只写$route(除非你要监听整个路由对象的变化)。

Vue的响应式原理是“对象的属性被访问/修改时触发”,如果路由变化是嵌套对象的变化(比如$route.meta里的某个字段),要确保监听的是响应式的部分。

组件内守卫要写在组件的选项里(和data、methods同级),不能写在methods里面哦~

现在再回头看开头的问题,是不是对Vue2监听路由变化的方法更清晰了?核心是根据“作用范围”和“场景需求”选对应的方法,全局逻辑用全局守卫,组件专属逻辑用组件内守卫或watch,多练几个项目就会越用越顺手啦~

版权声明

本文仅代表作者观点,不代表Code前端网立场。本文系作者Code前端网发表,如需转载,请注明页面地址。

最新发表
友情链接

Copyright © 2022 日本世界杯_林高远世界杯 - edenyn.com All Rights Reserved.