vue-router总结

965 阅读8分钟

下载

  • 依赖:npm install vue-router --save
  • 导入路由对象,并且调用Vue.use(VueRouter)
  • 创建路由实例,并且传入路由映射配置
  • Vue实例中,挂载创建的路由实例
  • 使用路由 和

配置基本的路由并使用

  • 新建router文件内置index.js文件
    import Vue from 'vue';
    import VueRouter from 'vue-router';//引入插件
    Vue.use(VueRouter);//通过Vue.use(插件),安装插件
    
    import home from '@pages/home/home.vue';
    import about from '@pages/about/about.vue';
    
    
    const routes=[//配置路径和组件之间的映射关系
        {path:'/home',component:home},
        {path:'/about',component:about}
    ]
    
    const router =new VueRouter({//创建VueRouter对象
        routes:routes   
    });
    
    export{//导出router
        router
    }
    
    /**
     * 使用vue-router的三个步骤
     * 创建路由组件
     * 配置路径和组件之间的映射关系
     * 使用路由 <router-link>和<router-view>
     */
    
  • main.js中引入导出的router,并挂载到vue实例上
    import {router} from './router/index'
    new Vue({
      el: '#app',
      router,
      components: { App },
      template: '<App/>'
    })
    
  • App.vue
    <template>
      <div id="app">
        <router-link to='/home'>home</router-link>
        <router-link to='/about'>about</router-link>
        <router-view></router-view>
      </div>
    </template>
    

路由详细配置

  • 设置重定向:当用户第一次进入的时候.默认显示的组件
    • 基本玩法
    const routes = [ //配置路径和组件之间的映射关系
      {
        path: '',
        //redirect:重定向(设置路由的默认路径)
        redirect:'/home'//配置方式一:填写需要展示的组件路径
        redirect: {//配置方式二:命名路由
          name: 'home'
        },
        meta: {
          title: '主页'
        }
      },
      {
        path: '/home',
        component: home,
        name: 'home',
        meta: {
          title: '主页'
        },
        children: [{
            path: '',
            //redirect:重定向(设置home中默认显示的子组件)
            redirect: 'detail',
            meta: {
              title: '详情页'
            }
          },
          {
            path: 'detail',
            component: detail,
            name: 'detail',
            meta: {
              title: '详情页'
            }
          }
        ]
      },
      {
        path: '/about',
        component: about,
        name: 'about',
        meta: {
          title: '关于页面'
        }
      },
      {
        path: '*',
        //redirect:重定向(设置当URL路径不被路由对象匹配的时候,显示的组件)
        redirect: {
          name: 'home'
        },
        meta: {
          title: '主页'
        }
      }
    ]
    
    • 高级玩法:根据路径动态跳转不同的界面
    { path: '/dynamic-redirect/:id?',//?的意思就是传不传都行,传我接着,不传我也接着
      redirect: to => {
        const { hash, params, query } = to
        if (query.to === 'foo') {//query传值:/dynamic-redirect?to=foo
          return { path: '/foo', query: {
              to:query.to
          } }
        }
        if (hash === '#baz') {//hash传值:/dynamic-redirect#baz
          return { name: 'baz', hash: hash }
        }
        if (params.id) {//params传值:/dynamic-redirect/123
          return `/with-params/${params.id}`
        } else {
          return '/bar'//不传值:/dynamic-redirect
        }
      }
    }
    
  • 别名:alias:别名配置有讲究,一般来说有以下几种情况
    • 绝对路径别名:'/'+[别名]
      { path: '/root', component: Root, alias: '/root-alias' }//路由模块中定义路由参数
      //注意点:如果组件路径是一级路由,那么只能使用绝对路径别名即:'/'+[别名]
      <router-link to="/root-alias">//调用:'/'+[别名],但是必须加 '/',没有'/'不行
          /root-alias (renders /root)
      </router-link>
      //注意点:绝对路径别名的优点/缺点是:
      //优点:他可以无视层级,无论routes中的item嵌套多少层,都可以直接找到
      //缺点:<li><router-link to="/foo">
                  /root-alias (renders /root)
             </router-link></li>//路径上只能写这么一个/foo,写别的不管事,你说气人不!
      //总结:绝对路径别名:不能有父别名/父路径,但是可以有子别名/路径(子别名/路径必须是相对路径别名)
      //调用的时候:'/'+[绝对路径]
      
    • 相对路径别名:[别名]
      { path: 'bar', component: Bar, alias: 'bar-alias' }//路由模块中定义路由参数
      //注意点:相对路径别名,千万不能写写在一级路由上,不然调用不到,切记!
      <li><router-link to="/home/bar-alias">//调用[父路径/父别名]+[别名]
          /root-alias (renders /root)
      </router-link></li>
      //总结:router-link调用的使用:'/'+[绝对路径]+[相对路径]+[相对路径]+[相对路径]...
      
    • 数组别名
      { path: 'baz', component: Baz, alias: ['/baz', 'baz-alias'] }//路由模块中定义路由参数
      /baz:绝对路径别名调用:/baz
      baz-alias:相对路径别名调用:/[父路径/别名]/baz-alias
      
    • 以空字符串作为别名的默认子路由
      { path: 'default', component: Default, alias: '' }//路由模块中定义路由参数
      '':相对路径别名:/[父路径/别名]
      
    • 嵌套别名
      { path: 'nested', component: Nested, alias: '/nested-alias',//路由模块中定义路由参数
          children: [
              { path: '/foo', component: NestedFoo },//绝对路径别名:调用:/foo
              { path: 'foo', component: NestedFoo }//相对路径别名:/nested-alias/foo
          ]
      }
      
    • 总结绝对路径/相对路径
      //注意点:由于编程式导航不能path和params写在同一个对象中,但是如果我写name,别名就不生效了.
      //结果:http://localhost:8080/测试/886
      //官方说明:/home 的别名是 /测试,意味着,当用户访问 /测试 时,URL 会保持为 /测试,但是路由匹配则为 /home,就像用户访问 /home 一样。
      //个人理解:给组件路径换了一个名称,可能为了URL的美观吧,但是用法还是一样的用法,必须保持别名和path的一致性,比如动态路由匹配的时候,也必须要一致,要不然接不到参数.
      受到CSS布局'子绝父相'的启发,我将这个绝对路径别名/相对路径别名,也总结一下:
      一级路由必须是绝对路径别名,
      如果它的嵌套路由中存在绝对路径别名,那么router-link调用的时候,就直接找它嵌套路由中的绝对路径别名即:'/'+[嵌套路由(绝对路径别名)]
      如果不存在:'/'+[一级路由(绝对路径别名)]+[相对路径别名]+[相对路径别名]+[相对路径别名]...
      注意点:如果是动态路由匹配,必须使用相对路径别名
      
  • history模式:默认是hash模式==>http://localhost:8080/#/home
    const router = new VueRouter({ //创建VueRouter对象
      routes: routes,
      mode:'history'//设置history模式:默认是hash模式==>路径带#的那种
    });
    
  • router-link的属性
    • to:属性:定义导航的路径
      <router-link to=[router路由中配置的路径]>home</router-link>
      <router-link to='/home'>home</router-link>
      
    • tag:属性:定义router-link标签解析成的标签类型(默认是a标签)
      <router-link to='/home' tag=[domName]>home</router-link>
      <router-link to='/home' tag='button'>home</router-link>
      
    • replace:属性:定义:history调用的方法,默认是history.pushState():可以触发浏览器的后退功能,加上replace之后,hostory调用的方法是history.replaceState():不能触发浏览器的后退功能
      <router-link to='/about' replace>about</router-link>
      
    • active-class:属性:定义router-link的选中className名称
      <router-link to='/home' tag='button' replace active-class=[customName]>home</router-link>
      <router-link to='/home' tag='button' replace active-class='active'>home</router-link>
      
  • 批量修改router-link的选中className名称
    const router = new VueRouter({ //创建VueRouter对象
      routes: routes,
      mode:'history',
      linkActiveClass:'active'//修改所有router-link的选中类名
    });
    

编程式导航

  • 基本使用
<template>
  <div id="app">
    <button @click='jumpRouterHome'>home</button>
    <button @click='jumpRouterAbout'>about</button>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: "App",
  methods: {
    jumpRouterHome() {
      this.$router.push([router中配置的路径]);
      this.$router.push("/home");
      his.$router.replace([router中配置的路径]);
      this.$router.replace("/home");//replace==>history.replaceState()
    },
    jumpRouterAbout() {
      this.$router.push([router中配置的路径]);
      this.$router.push("/about");
    }
  }
};
</script>
  • 编程式导航的几种方法
    • 字符串
      this.$router.push([router中配置的路径]);
      this.$router.push("/home");
      
    • 对象
      this.$router.push({
          path:[router中配置的路径]
      });
      this.$router.push({
          path:"/home"
      });
      
    • 命名路由
      this.$router.push({
          name:[router中name]
      });
      this.$router.push({
          name:"home",
          params:{
              [动态路由参数]:[值]
          }
      });
      
    • 带查询参数
      router.push({ path: '/register', query: { [查询key]: [查询值] }})
      router.push({ name: 'register', query: { [查询key]: [查询值] }})
      
    • router.go(n): history 记录中向前或者后退多少步
      // 在浏览器记录中前进一步,等同于 history.forward()
      router.go(1)
      
      // 后退一步记录,等同于 history.back()
      router.go(-1)
      
      // 前进 3 步记录
      router.go(3)
      
      // 如果 history 记录不够用,那就默默地失败呗
      router.go(-100)
      router.go(100)
      

动态路由的使用:多个路径匹配到同一个组件中

  • 修改router文件中index.js中的配置
    const routes = [ //配置路径和组件之间的映射关系
      {
        path: '/home/:name',//只有一个参数
        component: home,
        name:'home'
      },
      {
        path: '/about/:id/:userName',//多个参数
        component: about,
        name:'about'
      }
    ]
    
  • html结构中配置
    <router-link
        to='/home/zhangsan'
        tag='button'
        replace
    >home</router-link>
    <router-link
        to='/about/886/999'
        replace
    >about</router-link>
    
  • 编程式导航中配置
    this.$router.push({path:`/home/zhangsan`})
    this.$router.push({//推荐这种写法,清晰
        name:"home",
        params:{
          userName:'zhangsan'
        }
    })//注意点:path和params不能在一起配置:不生效
    
  • 取出配置的params
    //this.$route当前那个路由处于活跃状态,拿到的就是哪个路由
    console.log(this.$route.params)
    
  • 复用组件监听路由变化
    • 组件内部watch监听
      watch: {
          '$route' (to, from) {
            // 对路由变化作出响应...
          }
        }
      
    • 组件内守卫:beforeRouteUpdate
      beforeRouteUpdate (to, from, next) {
          // react to route changes...
          // don't forget to call next()
        }
      
  • 捕获所有路由或 404 Not found 路由
    {
        path: '*',
        redirect:{name:'about'}
    }
    
  • 高级匹配模式
    { path: '/' },
    // 参数用冒号表示:
    { path: '/params/:foo/:bar' },//匹配 /params/参数一/参数二
    // 可以通过添加“?”使参数成为可选参数
    { path: '/optional-params/:foo?' },//匹配 /optional-params/参数一 或者/optional-params
    // param后面可以跟着params中的regex模式
    // 只有当:id是所有号码时,才会匹配此路由
    { path: '/params-with-regex/:id(\\d+)' },//匹配 /params-with-regex/886(必须是纯数字)
    // 星号可以匹配任何东西
    { path: '/asterisk/*' },
    // 将路径的一部分用括号括起来,然后添加“?”
    { path: '/optional-group/(foo/)?bar' }//匹配 /optional-group/bar 或者 /optional-group/foo/bar
    
  • 匹配优先级
    有时候,同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高。
    

路由懒加载

  • 路由懒加载能解决的问题
    • 当打包构建应用时,JavaScript包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
    • 路由懒加载的主要作用是将路由对应的组件打包成一个个的js文件,只有当这个路由被访问的时候,才会加载对应的js文件
import Vue from 'vue';
import VueRouter from 'vue-router'; //引入插件
Vue.use(VueRouter); //通过Vue.use(插件),安装插件

// import home from '@pages/home/home.vue';
// import about from '@pages/about/about.vue';

const home=()=>import('@pages/home/home.vue');//懒加载的书写方式,这样在打包的时候,生成的js文件,会是一个路由js文件对应一个生成好的js文件
const about=()=>import('@pages/about/about.vue');


const routes = [ //配置路径和组件之间的映射关系
  {
    path: '/',
    //redirect:重定向(设置路由的默认路径)
    redirect: '/home/zhangsan'
  },
  {
    path: '/home/:userName',
    component: home,
    name:'home'
  },
  {
    path: '/about/:userName',
    component: about,
    name:'about'
  }
]

const router = new VueRouter({ //创建VueRouter对象
  routes: routes,
  mode:'history',
  linkActiveClass:'active'
});

export { //导出router
  router
}

/**
 * 使用vue-router的三个步骤
 * 创建路由组件
 * 配置路径和组件之间的映射关系
 * 使用路由 <router-link>和<router-view>
 */

路由嵌套

//在router文件夹内的index.js中配置
    const routes = [ //配置路径和组件之间的映射关系
      {
        path: '',
        //redirect:重定向(设置路由的默认路径)
        redirect: '/home'
      },
      {
        path: '/home',
        component: home,
        name:'home',
        children:[
          {
            path: '',
            //redirect:重定向(设置路由的默认路径)
            redirect: 'detail'
          },
          {
            path:'detail',
            component: detail,
            name:'detail'
          }
        ]
      },
      {
        path: '/about',
        component: about,
        name:'about'
      }
    ]
    //在home.vue中配置
    <h1>{{msg}}</h1>
    <router-link to='/home/detail'>详情</router-link>//配置路径
    <router-view></router-view>//配置路由出口

路由的参数传递

params方式:注意点:params的方式,需要在路由中配置动态路由匹配

 {//路由配置
    path: '/about/:username/path/:post_id',
    //path: '/about/:username/:post_id',这样写,不能使用编程式导航,不知道为什么,望指教
    component: about,
    name:'about'
  }
//router-link跳转
<router-link
    to='/about/li/path/nan'
    replace
>about</router-link>

//编程式导航
this.$router.push({
    name:"about",
    params:{
        username: 'evan', post_id: '123'
    }
})
//取:this.$route.params

query:无需路由的特殊配置

<router-link//router-link跳转
    :to="{path:'/home',query:{
        name:'肖宝成',
        age:26
        }}"
    tag='button'
    replace
>home</router-link>

this.$router.push({//编程式导航
    name:"home",
    query:{
        name:'宣传部'
    }
})
//取:this.$route.query

路由组件传参

//应用
<div id="app">
    <h1>Route props</h1>
    <ul>
        <li><router-link to="/">/</router-link></li>//Hello Vue! { "foo": "foo" }
        <li><router-link to="/hello/you">/hello/you</router-link></li>//Hello you! { "foo": "foo" }
        <li><router-link to="/static">/static</router-link></li>//Hello world! { "foo": "foo" }
        <li><router-link to="/dynamic/9">/dynamic/1</router-link></li>//Hello  2029! { "foo": "foo" }
        <li><router-link to="/attrs">/attrs</router-link></li>//Hello attrs { "foo": "foo" }
    </ul>
    <router-view class="view" foo="foo"></router-view>
</div>
<template>//hello组件
  <div>
    <h2 class="hello">Hello {{name}} {{ $attrs }}</h2>
  </div>
</template>

<script>
export default {
  props: {
    name: {
      type: String,
      default: 'Vue!'
    }
  }
}
</script>
//路由文件
function dynamicPropsFn (route) {
  const now = new Date()
  return {
    name: (now.getFullYear() + parseInt(route.params.years)) + '!'
  }
}

const router = new VueRouter({
  mode: 'history',
  base: __dirname,
  routes: [
    { path: '/', component: about }, // 没有参数,不做事情
    { path: '/hello/:name', component: about, props: true }, // 将匹配出来的动态参数传进去
    { path: '/static', component: about, props: { name: 'world' }}, // 静态值
    { path: '/dynamic/:years', component: about, props: dynamicPropsFn }, // 将静态值与基于路由的值结合
    { path: '/attrs', component: about, props: { name: 'attrs' }}//静态值
  ]
})

导航守卫

全局前置守卫:写在router文件中

router.beforeEach((to, from, next) => {
  console.log(to);//即将要进入的目标 路由对象
  console.log(from);//当前导航正要离开的路由
  next();//必须调用
  next中的参数:
  1.next()//什么都不放:进行管道中的下一个钩子
  2.next(false) //中断当前的导航
  3.next('/') 或者 next({ name: '命名路由' ,params:{})或者next({ path: '/' ,query:{})//跳转到一个不同的地址
  4.next(error)// 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调
})

全局解析守卫

router.beforeResolve((to, from, next)=>{//后
  console.log('456')
  //从from到to
  // document.title = to.meta.title;
  next();
})

全局后置钩子

//注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身
//对跳转后的页面进行例如滚动条回调0 0 位置、更新页面title、懒加载结束等等
router.afterEach((to, from) => {//最后
  document.title = to.meta.title;
  // console.log(to);
})

路由独享的守卫

{
    path: '/about',
    component: about,
    name: 'about',
    beforeEnter: (to, from, next) => { //路由独享的守卫
      console.log(to);
      next('/home');
    }
  }

组件内的守卫

watch: {
    $route(to, from) {
      //当使用路由参数时,例如从 /user/foo 导航到 /user/bar,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。复用组件时,想对路由参数的变化作出响应的话,你可以简单地 watch (监测变化) $route 对象
      // 对路由变化作出响应...
      console.log(" // 对路由变化作出响应...");
    }
}, //监听
beforeRouteEnter(to, from, next) {
    //路由进入之前调用
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
    //console.log(from);//上一个激活的路由
    //console.log(to);//当前路由
    next(vm => {
      // 通过 `vm` 访问组件实例
      console.log(vm)//当前激活的路由对应的组件对象
    });
    // next();
  },
  beforeRouteUpdate(to, from, next) {
    //路由更新调用
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
    // console.log(to);
    next();
  },
  beforeRouteLeave(to, from, next) {
    //这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
    //to是将要去的路由
    //from是将要离开的路由(也就是当前的路由)
    const answer = window.confirm(
      "Do you really want to leave? you have unsaved changes!"
    );
    if (answer) {
      next();
    } else {
      next(false);
    }
  },

完整的导航解析流程

1.导航被触发。
2.在失活的组件里调用离开守卫。//beforeRouteLeave (组件内调用)
3.调用全局的 beforeEach 守卫。//beforeEach (router.js内调用)
4.在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。//beforeRouteUpdate (组件内调用)
5.在路由配置里调用 //beforeEnter路由独享的守卫 (router.js内调用)
6.解析异步路由组件。
7.在被激活的组件里调用 //beforeRouteEnter (组件内调用)
8.调用全局的 //beforeResolve (router.js内调用)
9.导航被确认。
10.调用全局的 afterEach 钩子。//afterEach (router.js内调用)
11.触发 DOM 更新。
12.用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。//beforeRouteEnter