路由Vue-router 的使用总结
1、关于 router-view 匹配
vue 项目使用 vue-router,所有的根级别的路由都是在 App.vue 文件中的 router-view 中渲染的。比如下面的 path: '/' 、path: '/home' 路径匹配到的组件都是在 App.vue 文件下的 router-view 中进行渲染。
其他级别的路径在对应的父组件的 router-view 中进行渲染。
// App.vue <div id="app"> <router-view/> </div> // 路由文件 routes: [ { path: '/', name: 'HelloWorld', component: HelloWorld, children: [ { path: 'item01', name: 'Item01', component: Item01 } ] }, { path: '/home', name: 'Home', component: Home } ]
2、导航守卫
利用导航守卫可以在路由发生变化时进行某些操作。导航守卫分为:全局守卫、路由独享守卫、组件内守卫
示例:
const router = new VueRouter({ ... }) //全局前置守卫 router.beforeEach((to, from, next) => { // ... })
每个守卫方法接收三个参数:
-
to: Route
: 即将要进入的目标 路由对象 -
from: Route
: 当前导航正要离开的路由 -
next: Function,
一定要调用该方法来 resolve 这个钩子。执行效果依赖next
方法的调用参数。-
next()
: 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。 -
next(false)
: 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到from
路由对应的地址。 -
next('/')
或者next({ path: '/' })
: 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向next
传递任意位置对象,且允许设置诸如replace: true
、name: 'home'
之类的选项以及任何用在router-link
的to
prop 或router.push
中的选项。 -
next(error)
: (2.4.0+) 如果传入next
的参数是一个Error
实例,则导航会被终止且该错误会被传递给router.onError()
注册过的回调。
-
必须确保要调用 next
方法,否则钩子就不会被 resolved,即路由不会跳转。
2.1、全局守卫
2.1.1、全局前置守卫(router.beforeEach())
你可以使用 router.beforeEach
注册一个全局前置守卫
const router = new VueRouter({ ... }) router.beforeEach((to, from, next) => { // ... }) //守卫可以有多个 router.beforeEach((to, from, next) => { // ... })
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。
2.1.2、全局解析守卫
在 2.5.0+ 你可以用 router.beforeResolve
注册一个全局守卫。这和 router.beforeEach
类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
2.1.3、全局后置守卫
你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next
函数也不会改变导航本身:
router.afterEach((to, from) => { // ... })
2.2、路由独享守卫
你可以在某个路由配置上直接定义 beforeEnter
守卫,这些守卫与全局前置守卫的方法参数是一样的。
const router = new VueRouter({ routes: [ { path: '/foo', component: Foo, beforeEnter: (to, from, next) => { // ... } } ] })
2.3、组件内守卫
2.3.1、前置守卫(beforeRouteEnter()
)
在渲染该组件的对应路由被 confirm 前调用。注意:该守卫内部不能获取组件实例 `this` ,因为该守卫是在导航确认前被调用,此时组件还没被创建。
const Foo = { template: `...`, beforeRouteEnter (to, from, next) { //... } }
要想在该守卫内部访问组件实例,可以给 next传一个回调函数,
在导航被确认的时候执行回调,并且把组件实例作为 next 回调函数的参数,此时就可以访问组件实例了。
beforeRouteEnter (to, from, next) { next(vm => { // 通过 `vm` 访问组件实例 }) }
beforeRouteEnter
是唯一支持给 next
传递回调的守卫。因为其他守卫比如 beforeRouteUpdate
和 beforeRouteLeave
,this
已经可用了,所以没有必要支持传递回调。
2.3.2、路由改变不跳转时的守卫(beforeRouteUpdate
()
)
在当前路由改变,但是该组件被复用时调用。
比如,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候该守卫会被触发。由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
该守卫可以访问组件实例 `this`。
const Foo = { template: `...`, beforeRouteUpdate (to, from, next) { // ... this.xxx = to.xxx
next(); } }
2.3.3、后置守卫(beforeRouteLeave
()
)
该守卫在导航离开该组件的对应路由时调用。可以访问组件实例 `this`。
const Foo = { template: `...`, beforeRouteLeave (to, from, next) { // ... this.xxx = xxx; next(); } }
这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false)
来取消。
beforeRouteLeave (to, from , next) { const answer = window.confirm('Do you really want to leave? you have unsaved changes!') if (answer) { next() } else { next(false) } }
2.4、完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用离开守卫。
- 调用全局的
beforeEach
守卫。 - 在重用的组件里调用
beforeRouteUpdate
守卫 (2.2+)。 - 在路由配置里调用
beforeEnter
。 - 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter
。 - 调用全局的
beforeResolve
守卫 (2.5+)。 - 导航被确认。
- 调用全局的
afterEach
钩子。 - 触发 DOM 更新。
- 用创建好的实例调用
beforeRouteEnter
守卫中传给next
的回调函数。
3、meta 元信息的妙用
路由可以配置 meta 选项来备注路由的一些信息,利用这个元信息可以实现一些比较巧妙的功能。可以参考:https://www.bbsmax.com/A/A7zglW41J4/
4、路由懒加载(()=>import())
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。结合 Vue 的异步组件和 Webpack 的代码分割功能,就能轻松实现路由组件的懒加载。
实现路由懒加载很简单,只要照着下面写法即可:
//路由懒加载语法: const Foo = () => import('./Foo.vue') const router = new VueRouter({ routes: [{ path: '/foo', component: Foo }] })
上面将会将 foo.vue 组件提取为一个单独的文件,并且在被访问到时才加载。(提示语法报错可能是因为没有使用syntax-dynamic-import 插件,可参考:https://www.cnblogs.com/xiaochongchong/p/7772773.html)
在打包的时候,文件命名会根据 id 命名,每次也可能会发生改变,如果需要指定命名,可以使用Magic Comments(魔法注释)。或者是有时候我们可能会想把某个路由下的所有组件都打包在同个文件 (chunk) 中,也可以使用魔法注释来命名 chunk (需要 Webpack > 2.4):
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue') const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue') const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')
由此,上面将会将三个组件都打包到 group-foo.js 文件中。