首先抛出这样一个问题, vue-router 是用来做什么的?
这里不着急回答, 也不准备在这篇文章里回答. 这篇文章仅总结一些使用心得, 其实总结完所有关于 vue-router 的内容后, 整篇文章也许就刚好能回答这个问题了.
一 使用步骤
单纯使用 vue.js, 我们可以通过组合组件来组成应用, 不同的页面有不同的地址, 路由依靠链接跳转. 这显然不是单页应用, 因为会有页面刷新.
当要把 vue-router 引入进来, 我们需要做的是, 将组件映射到路由, 然后告诉路由在哪里渲染组件内容.
html 代码
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="utf-8">
- <title>vue-router</title>
- </head>
- <body>
- <div id="app">
- <h1>Hello World!</h1>
- <p>
- <!-- 页面导航元素, 默认被渲染为 a 标签 -->
- <router-link to="user">toUser</router-link>
- <!-- 路由渲染出口, 路由匹配到的组件内容全部渲染到该节点下 -->
- <router-view></router-view>
- </p>
- </div>
- <script src="/dist/build.js"></script>
- </body>
- </html>
- JavaScript
javascript 代码
- import Vue from 'vue';
- import VueRouter from 'vue-router';
- import indexPage from './index.vue';
- import userPage from './user.vue';
- // 注册路由插件
- Vue.use(VueRouter);
- // 配置路由
- const routes = [
- {
- path: '',
- component: indexPage,
- }, {
- path: '/user',
- component: userPage,
- }];
- // 创建 Router 实例
- const router = new VueRouter({
- routes,
- });
- // 创建和挂载 Vue 根实例
- const app = new Vue({
- router,
- });
- app.$mount('#app');
router-link 和 router-view 是两个功能性内置组件. router-link 默认被渲染为 a 标签, 负责路由跳转功能; router-view 是组件内容被渲染的位置.
js 方面代码里的注释已经写得很清楚.
二 动态路由
动态路由其实又可以叫做路由传参.
javascript 代码
- const router = new VueRouter({
- routes: [
- // 动态路径参数 以冒号开头
- { path: '/user/:id', component: User }
- ]
- })
形如上述形式的路径即为动态路由, 冒号后是参数, 可以跟多段参数, 每个参数都被设置到 this.$route.params 中.
注意 / user/:id 和 / user/:name, 当参数变化时, 组件会被复用, 因此组件生命周期钩子不会被再次调用. 复用组建时, 可以通过监听 $route 对象的变化来监测路由是否变化.
路由钩子 beforeRouterUpdate 也会执行.
vue-router 使用 path-to-regexp 作为路径匹配引擎, 假如路径很复杂可以学习高级的匹配模式. 但是路径一般不应设计的太复杂, 如果太复杂, 应该考虑如何简化.
三 命名视图
有时候想同时 (同级) 展示多个视图, 例如创建一个布局, 有 sidebar(侧导航) 和 main(主内容) 两个视图, 这个时候命名视图就派上用场了. 你可以在界面中拥有多个单独命名的视图, 而不是只有一个单独的出口. 如果 router-view 没有设置名字, 那么默认为 default.
javascript 代码
- <router-view class="view one"></router-view>
- <router-view class="view two" name="sidebar"></router-view>
- <router-view class="view three" name="header"></router-view>
一个视图使用一个组件渲染, 因此对于同个路由, 多个视图就需要多个组件. 确保正确使用 components 配置(带上 s):
javascript 代码
- routes: [
- {
- path: '/',
- components: {
- default: Foo,
- a: SideBar,
- b: Header
- }
- }
- ]
- #### 四 路由元信息
定义路由的时候可以配置 meta 字段:
javascript 代码
- routes: [
- {
- path: '/foo',
- component: Foo,
- children: [
- {
- path: 'bar',
- component: Bar,
- // a meta field
- meta: { requiresAuth: true }
- }
- ]
- }
- ]
五 编程式导航
除了使用 <router-link> 创建 a 标签来定义导航链接, 我们还可以借助 router 的实例方法, 通过编写代码来实现.
javascript 代码
router.push(location)
想要导航到不同的 URL, 则使用 router.push 方法. 这个方法会向 history 栈添加一个新的记录, 所以, 当用户点击浏览器后退按钮时, 则回到之前的 URL.
当你点击 <router-link> 时, 这个方法会在内部调用, 所以说, 点击 <router-link :to="..."> 等同于调用 router.push(...).
javascript 代码
router.replace(location)
跟 router.push 很像, 唯一的不同就是, 它不会向 history 添加新记录, 而是跟它的方法名一样 -- 替换掉当前的 history 记录.
javascript 代码
router.go(n);
这个方法的参数是一个整数, 意思是在 history 记录中向前或者后退多少步, 类似 window.history.go(n).
六 懒加载
当打包构建应用时, Javascript 包会变得非常大, 影响页面加载. 如果我们能把不同路由对应的组件分割成不同的代码块, 然后当路由被访问的时候才加载对应组件, 这样就更加高效了.
异步组件模式:
javascript 代码
- const Foo = resolve => {
- // require.ensure 是 webpack 的特殊语法, 用来设置 code-split point
- // (代码分块)
- require.ensure(['./Foo.vue'], () => {
- resolve(require('./Foo.vue'))
- })
- }
Amd 风格的 require 模式:
javascript 代码
- const routes = [
- {
- path: '',
- component: resolve => require(['./index.vue'],resolve),
- }, {
- path: '/user',
- component: resolve => require(['./user.vue'],resolve),
- }];
ensure 模式写起来有点繁琐, 我更倾向于后面一种写法.
七 导航钩子
钩子(Hook), 早期编程可能有个概念叫句柄, 不知道将两者类比而且强行归为一类是不是合适. 钩子的用处是在某个特定流程中的不同时机暴露出一些函数, 使得用户可以通过覆写这些函数实现在原有位置执行自己的代码逻辑的功能.
分类
vue-router 中的导航钩子按定义位置不同 (执行时机也不同) 分为全局钩子, 路由级钩子和组件级钩子.
全局钩子
全局钩子有三个, 分别是 beforeEach,beforeResolve 和 afterEach, 在路由实例对象注册使用;
路由级钩子
路由级钩子有 beforeEnter, 在路由配置项中项定义;
组件级钩子
组件级钩子有 beforeRouteEnter,beforeRouteUpdate 和 beforeRouteLeave, 在组件属性中定义;
代码示例
javascript 代码
- import Vue from 'vue';
- import VueRouter from 'vue-router';
- // Vue 中插件必须 use 注册
- Vue.use(VueRouter);
- // 路由配置项, 此处是路由级钩子的定义
- const routes = [{
- path: '/',
- component: resolve => require(['./index.vue'], resolve),
- keepAlive: true,
- },
- {
- path: '/user/:userName',
- keepAlive: true,
- beforeEnter(to,from,next){
- console.log('router beforeEnter');
- next();
- },
- component: resolve => require(['./user.vue'], resolve),
- }];
- // 实例化路由对象
- const router = new VueRouter({
- routes
- });
- // 全局钩子
- router.beforeEach((to,from,next)=>{
- console.log('global beforeEach')
- next();
- });
- router.beforeResolve((to,from,next)=>{
- console.log('global beforeResolve')
- next();
- });
- router.afterEach((to,from,next)=>{
- console.log('global afterEach')
- });
- // 实例化 Vue 对象并挂载
- new Vue({
- router
- }).$mount('#app');
- user.vue
javascript 代码
- <template>
- <div>
- <h1>{{ msg }}</h1>
- <p > 我是:{{userName}}</p>
- </div>
- </template>
- <script>
- export default {
- name: 'user',
- data () {
- return {
- msg: '这里是 User Page.',
- userName: '叶落'
- };
- },
- methods: {},
- mounted () {
- var me = this;
- me.userName = me.$route.params.userName;
- console.log('user mounted.');
- },
- beforeRouteEnter (to, from, next) {
- console.log('component beforeRouteEnter');
- next();
- },
- beforeRouteUpdate (to, from, next) {
- console.log('component beforeRouteUpdate');
- next();
- },
- beforeRouteLeave(to,from,next){
- console.log('component beforeRouteLeave');
- next();
- }
- };
- </script>
执行时机
由首页进入 user 页面:
global beforeEach> router beforeEnter> component beforeRouteEnter> global beforeResolve> global afterEach> mounted
由 user 回到首页:
component beforeRouteLeave => global beforeEach => global beforeResolve => global afterEach
排除 beforeRouteUpdate, 其余六个导航钩子的执行时机其实很好理解. 大体按照 leave,before,enter,resolve,after 的顺序并全局优先的思路执行. beforeRouteUpdate 的触发是在动态路由情形下, 比如 path: '/user/:userName' 这条路由, 当页面不变更只动态的改变参数 userName 时, beforeRouteUpdate 便会触发.
总结: 使用 vue 组件拼凑成整个应用, 每个页面是独立的, 路由依靠链接跳转, 会刷新页面. 使用 vue-router 则可以不刷新页面加载对应组件, hash 和 history 模式模拟路径变化, 不刷新页面.
来源: http://www.qdfuns.com/article/23231/09b5b3741ace4e09c65a0e54efa4785f.html