拓冰建站拓冰建站
首页 / 资讯中心 / 正文

vue-router 组件注入详解:$router、$route 与组件内导航守卫

vue-router 组件注入详解$router、$route 与组件内导航守卫【免费下载链接】vue-router The official router for Vue 2项目地址: https://gitcode.com/gh_mirrors/vu/vue-router本篇文章围绕 vue-routerVue 2 官方路由的组件注入机制展开当你在根实例上以router选项传入路由器实例后Vue Router 会自动把$router与$route两个属性注入到每一个子组件中并为路由组件开放beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave三个组件内守卫选项。读完本文你将理解这三个 API 的确切语义、只读$route对象的内部实现Object.freeze冻结、以及它们在完整导航流程中的执行时机并能在真实项目中正确使用它们。本文以仓库内西班牙语文档 docs-gitbook/es/api/component-injections.md 为核心骨架结合 src/install.js、src/util/route.js 等源码进行佐证与扩充。一、注入机制概览一切从router选项开始组件注入Component Injections的核心前提非常简单只要把router实例作为router选项传给根 Vue 实例这些属性就会被注入到每个子组件中。const router new VueRouter({ routes: [ { path: /, component: Home } ] }) const app new Vue({ router // 关键传入 router 选项 }).$mount(#app)从源码 src/install.js 可以看到注入的具体实现Vue.mixin({ beforeCreate () { if (isDef(this.$options.router)) { this._routerRoot this this._router this.$options.router this._router.init(this) Vue.util.defineReactive(this, _route, this._router.history.current) } else { this._routerRoot (this.$parent this.$parent._routerRoot) || this } } }) Object.defineProperty(Vue.prototype, $router, { get () { return this._routerRoot._router } }) Object.defineProperty(Vue.prototype, $route, { get () { return this._routerRoot._route } })这段代码揭示了三个关键实现细节全局 mixin通过Vue.mixin({ beforeCreate })每个组件创建时都会执行注入逻辑只有根组件持有router选项子组件通过this.$parent._routerRoot向上找到根实例。响应式_route根实例上通过Vue.util.defineReactive(this, _route, ...)定义响应式属性路由切换时更新它从而驱动所有组件重新渲染。原型 getter$router与$route定义在Vue.prototype上子组件经由_routerRoot间接访问根实例的数据。$router是整个路由器实例拥有push、replace、go、back、forward、beforeEach、afterEach等全部方法$route是当前激活的 Route 对象。二者分工明确前者负责导航动作后者描述当前状态。二、$router路由器实例$router是new VueRouter({...})创建出来的实例类型为 src/router.js 中导出的VueRouter类。在组件内你可以这样使用export default { methods: { goHome () { this.$router.push(/home) // 导航到 /home this.$router.replace(/login) // 替换当前历史记录 this.$router.go(-1) // 后退一步 } } }从 src/router.js 可以看出构造时会根据options.mode选择三种历史实现之一html5HTML5History、hashHashHistory、abstractAbstractHistory并调用createMatcher(options.routes || [], this)生成路由匹配器。也就是说组件内调用this.$router.push()时实际上走的是对应 history 实现的push流程最终由 matcher 完成路由匹配与守卫链执行。三、$route只读且不可变的当前路由对象$route是当前激活的 Route 对象。它有两个重要特性只读文档明确说明 Esta propiedad es de solo lectura y sus propiedades son inmutables该属性是只读的其属性不可变。可观察虽然不可修改但它是响应式的可以像普通 data 一样被watch观察。源码 src/util/route.js 用一句话揭示了不可变性的实现根源return Object.freeze(route)createRoute创建的路由对象会被Object.freeze深度冻结query、params通过clone拷贝后同样不可变。这正是文档所说只读、不可变的底层机制——每次导航成功后都会生成一个全新的冻结 Route 对象而不是在原对象上修改。$route对象包含以下属性详见 Route 对象文档属性类型说明$route.pathstring当前路径总是解析为绝对路径如/foo/bar$route.paramsObject动态路径段与通配符段的键值对无参数时为空对象$route.queryObject查询字符串键值对如/foo?user1时$route.query.user 1$route.hashstring当前路由的 hash含#无则空字符串$route.fullPathstring包含 query 和 hash 的完整 URL$route.matchedArrayRouteRecord匹配到的所有嵌套路由记录数组父级在前、子级在后$route.namestring当前路由的名称使用命名路由时存在由于$route是响应式的你可以用watch对参数或 query 变化做出反应watch: { $route (to, from) { // 对路由变化做出响应 }, $route.params.id (id) { // 更精确地观察某个参数 } }需要特别注意的是参数或 query 的变化不会触发导航守卫见 导航守卫文档。此时要么观察$route对象要么使用组件内守卫beforeRouteUpdate详见下文。四、组件内导航守卫Opciones habilitadas除了全局守卫router.beforeEach与路由级守卫beforeEnter你还可以直接在路由组件内部定义以下三个守卫选项beforeRouteEnter进入该路由前调用beforeRouteUpdateVue Router 2.2 新增路由已改变但组件被复用时调用beforeRouteLeave离开该路由前调用const Foo { template: ..., beforeRouteEnter (to, from, next) { // 在渲染该组件的路由被确认前调用 // 此时组件实例尚未创建因此无法访问 this }, beforeRouteUpdate (to, from, next) { // 在当前路由改变、但该组件被复用时调用 // 例如 /foo/:id 从 /foo/1 导航到 /foo/2 时同一组件实例被复用 // 此守卫可以访问 this }, beforeRouteLeave (to, from, next) { // 导航离开渲染该组件的路由时调用 // 可以访问 this } }4.1 为什么beforeRouteEnter拿不到thisbeforeRouteEnter在导航确认之前执行此时目标组件尚未创建因此没有this。如果需要访问组件实例可以向next传入回调函数——该回调会在导航确认后执行并把实例作为参数传入beforeRouteEnter (to, from, next) { next(vm { // 通过 vm 访问组件实例 vm.loadData() }) }从源码 src/util/route.js 的handleRouteEntered可以看到回调的执行时机导航确认、DOM 更新后遍历route.matched中每个 record 的enteredCbs把已创建实例传入回调。这与导航守卫文档中完整流程的第 12 步一致调用beforeRouteEnter守卫传给next的回调函数并传入创建好的实例。4.2beforeRouteUpdate应对参数变化这是 Vue Router 2.2 引入的守卫专门解决动态路由复用问题。当路由/foo/:id从/foo/1导航到/foo/2时组件实例会被复用beforeRouteUpdate此时被调用且可以访问thisbeforeRouteUpdate (to, from, next) { this.postId to.params.id // 直接更新实例数据 next() }4.3beforeRouteLeave离开前的拦截beforeRouteLeave最典型的场景是防止用户在未保存修改时意外离开通过next(false)取消导航beforeRouteLeave (to, from, next) { if (this.formDirty) { const ok window.confirm(你有未保存的修改确定离开吗) ok ? next() : next(false) // next(false) 取消导航 } else { next() } }4.4 组件守卫的钩子合并机制三个组件守卫都是组件选项遵循 Vue 的选项合并策略。源码 src/install.js 特意将它们与生命周期钩子使用相同的合并策略strats.beforeRouteEnter strats.beforeRouteLeave strats.beforeRouteUpdate strats.created这意味着这些守卫可以出现在 mixin 中并与组件自身的守卫按数组形式合并、按顺序执行——这是组件守卫能参与导航链的基础。4.5 组件守卫在完整导航流程中的位置根据导航守卫文档给出的完整解析流程组件守卫分布在导航生命周期的不同阶段导航被触发调用失活组件里的beforeRouteLeave守卫调用全局beforeEach守卫调用复用组件里的beforeRouteUpdate守卫2.2调用路由配置里的beforeEnter解析异步路由组件调用被激活组件里的beforeRouteEnter守卫调用全局beforeResolve守卫2.5导航被确认调用全局afterEach钩子触发 DOM 更新调用beforeRouteEnter中传给next的回调此时实例已创建务必总是调用next函数否则导航链永远不会被解析页面将停滞在当前状态。五、综合实战组合使用注入 API下面是一个将$router、$route与组件守卫组合使用的完整示例模式参考仓库 examples/nested-routes 等示例目录const User { props: [id], template: div classuser p当前用户 ID{{ $route.params.id }}/p button click$router.push({ path: /users/ (Number($route.params.id) 1) }) 下一个用户 /button /div , beforeRouteUpdate (to, from, next) { // 组件复用时的参数变化在此处理 console.log(从用户 ${from.params.id} 切换到用户 ${to.params.id}) next() }, beforeRouteLeave (to, from, next) { if (confirm(确定离开用户页面吗)) { next() } else { next(false) } } } const router new VueRouter({ routes: [ { path: /users/:id, component: User } ] }) new Vue({ router }).$mount(#app)这段代码同时体现了三个要点模板中通过$route.params.id读取动态参数、通过$router.push以Location对象path 参数发起导航、组件内用beforeRouteUpdate与beforeRouteLeave感知复用与离开。六、相关资源组件注入官方文档本主题核心docs-gitbook/es/api/component-injections.mdRoute 对象完整属性说明docs-gitbook/es/api/route-object.md导航守卫详解与完整解析流程docs-gitbook/es/advanced/navigation-guards.md动态路由与参数变化处理docs-gitbook/es/essentials/dynamic-matching.md注入机制实现源码src/install.js$route不可变实现Object.freezesrc/util/route.js导航守卫源码src/composables/guards.js【免费下载链接】vue-router The official router for Vue 2项目地址: https://gitcode.com/gh_mirrors/vu/vue-router创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门