05-Vue-Router

nobility 发布于 2022-02-06 2257 次阅读


Vue-Router

Vue-Router是Vue.js官方的路由管理器,与Vue.js的核心深度集成,让构建单页面应用变得易如反掌

基本使用

Vue-Router中的组件

router-link是VueRouter自带的组件,用于做路由跳转

  • to属性决定跳转的路由地址
  • 默认会被渲染成a标签,可通过tag属性进行修改
  • 默认可以使用浏览器后退按钮返回,可通过replace属性进行修改为不能返回

router-view是VueRouter自带的组件,用于当作路由组件的占位符,router-view就像动态组件那样,可以使用transitionkeep-alive组件进行包裹

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>hello world</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- CDN方式引入Vue -->
  <script src="https://unpkg.com/vue-router@3.4.9/dist/vue-router.js"></script>
</head>

<body>
  <div id="app">
    <router-link to="/one" replace>one</router-link> <!-- 不允许使用浏览器返回按钮返回 -->
    <router-link to="/two" tag="button">two</router-link> <!-- 修改为button -->
    <router-view></router-view>	<!-- 路由组件占位符 -->
  </div>
  <script>
    //Vue.use(VueRouter); //若使用模块化机制编程,才需要这么做
    var One = { template: `<h1>One</h1>` }
    var Two = { template: `<h1>Two</h1>` }
    //定义两个局部组件,用于在路由中间件中注册

    var router = new VueRouter({  //创建路由中间件
      routes: [
        { path: "/one", component: One }, //path用于设置该路由路径
        { path: "/two", component: Two }	//component用于设置该路由下的组件
      ]
    })
    var app = new Vue({
      el: "#app",
      router //在该组件中使用路由
    });
  </script>
</body>

</html>

$router实例

在注册完路由后,Vue实例中为多出一个$router路由对象,该路由对象是当前活跃路由对象,使用该对象也可以实现路由跳转

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>hello world</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- CDN方式引入Vue -->
  <script src="https://unpkg.com/vue-router@3.4.9/dist/vue-router.js"></script>
</head>

<body>
  <div id="app">
    <button @click="changOne">one</button>
    <button @click="changTwo">two</button> 
    <router-view></router-view>
  </div>
  <script>
    //Vue.use(VueRouter); //若使用模块化机制编程,才需要这么做
    var One = { template: `<h1>One</h1>` }
    var Two = { template: `<h1>Two</h1>` }
    //定义两个局部组件,用于在路由中间件中注册

    var router = new VueRouter({  //创建路由中间件
      routes: [
        { path: "/one", component: One }, //path用于设置该路由路径
        { path: "/two", component: Two }	//component用于设置该路由下的组件
      ]
    })

    var app = new Vue({
      el: "#app",
      router, //在该组件中使用路由
      methods: {
        changTwo(){
          if(this.$router.history.current.path === "/two") return;  //若当前路由就是tow则什么都不做
          // this.$router.push("/tow"); //浏览器返回按钮可用
          this.$router.replace("/two"); //浏览器返回按钮不可用
        },
        changOne(){
          if(this.$router.history.current.path === "/one") return;   //若当前路由就是tow则什么都不做
          // this.$router.push("/one"); //浏览器返回按钮可用
          this.$router.replace("/one"); //浏览器返回按钮不可用
        }
      }
    });
  </script>
</body>

</html>

路由模式

在路由对象中使用mode属性设置路由模式,默认是hash模式,可设置为history模式,若浏览器不支持history模式Vue会自动转化为hash模式(可使用fallback属性设置为fasle关闭自动转化)

若设置为history模式则需要后台服务器设置对于任意路径的请求都要重定向到根路由,否则会发生真正的跳转,而返回404,服务器这样的设置就导致整个网站都没有404页面,所以最好在配置一个通用路由,用来展示404组件

  • 可通过base属性设置所有路由的公共前缀(仅在history模式下有效)
  • 可通过路由的redirect属性设置路由重定向路径,通常用于根路径重定向到首页组件
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>hello world</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- CDN方式引入Vue -->
  <script src="https://unpkg.com/vue-router@3.4.9/dist/vue-router.js"></script>
</head>

<body>
  <div id="app">
    <router-link to="/one">one</router-link>
    <router-link to="/two">two</router-link>
    <router-view></router-view>
  </div>
  <script>
    //Vue.use(VueRouter); //若使用模块化机制编程,才需要这么做
    var One = { template: `<h1>One</h1>` }
    var Two = { template: `<h1>Two</h1>` }
    //定义两个局部组件,用于在路由中间件中注册

    var router = new VueRouter({  //创建路由中间件
      mode: "history", //设置路由模式,默认是hash模式
      base: "/base/", //可设置应用的基路径,所有router-link在跳转时会自动加上该前缀路径,仅在history模式下有效
      routes: [
        { path: "*", component: { template: `<h1>404 Not Find</h1>` } },  //使用通配符设置404组件
        { path: "/", redirect: "/one"},  //redirect用于设置路由重定向地址
        { path: "/one", component: One }, //path用于设置该路由路径
        { path: "/two", component: Two }	//component用于设置该路由下的组件
      ]
    })
    const app = new Vue({
      el: "#app",
      router //在该组件中使用路由
    });
  </script>
</body>

</html>

路由焦点类

router-link渲染标签获得焦点时会为该router-link自动添加.router-link-active.router-link-exact-active类名

两个类名的区别在于:exact-active为完全匹配,而active是前缀匹配

  • 可使用active-classexact-active-class属性单个对其自定义修改,单个自定义修改的优先级更高
  • 也可以在路由对象中通过linkActiveClasslinkExactActiveClass属性统一对其自定义修改
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>hello world</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- CDN方式引入Vue -->
  <script src="https://unpkg.com/vue-router@3.4.9/dist/vue-router.js"></script>
  <style> .active-global{ color: red; } </style>
</head>

<body>
  <div id="app">
    <ul>
      <li><router-link to="/prefix">/prefix,当当前路由前缀是prefix时,也会加上.active类名,但不会加.exact-active类名</router-link></li>
      <li><router-link to="/prefix/one">/prefix/one</router-link></li>
      <li>
        <router-link to="/test"
        active-class="active"
        exact-active-class="exact-active"
        >/test,单个类名自定义修改的优先级更高
        </router-link>
      </li>
    </ul>
  </div>
  <script>
    //Vue.use(VueRouter); //若使用模块化机制编程,才需要这么做
    var router = new VueRouter({  //创建路由中间件
      linkActiveClass: "active-global",  //统一对焦点类修改类名
      linkExactActiveClass: "exact-active-global"  //统一对焦点类修改类名
    })
    var app = new Vue({
      el: "#app",
      router //在该组件中使用路由
    });
  </script>
</body>

</html>

嵌套路由

嵌套路由,即路由中的路由,用来显示组件中的组件,在路由对象中使用children属性进行定义

  • 注意子路由中不要加反斜杠,否则不会被当作子路由
  • router-link中依然要写完整路由路径
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>hello world</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- CDN方式引入Vue -->
  <script src="https://unpkg.com/vue-router@3.4.9/dist/vue-router.js"></script>
</head>

<body>
  <div id="app">
    <router-link to="/one">One</router-link>
    <router-view></router-view>
  </div>
  <script>
    //Vue.use(VueRouter); //若使用模块化机制编程,才需要这么做
    var One = {
      template: `<div>
        <router-link to="/one/two">Two</router-link>
        <router-link to="/one/three">Three</router-link>
        <router-view></router-view>
      </div>`
    } //局部父组件

    var Two = { template: `<h1>Two</h1>` }  //局部子组件
    var Three = { template: `<h1>Three</h1>` }  //局部子组件

    var router = new VueRouter({  //创建路由中间件
      routes: [{
          path: "/one",
          component: One,
          children: [
            {path: "two", component: Two }, //切勿加反斜杠
            {path: "three", component: Three }
          ]
        }]
    })
    
    const app = new Vue({
      el: "#app",
      router //在该组件中使用路由
    });
  </script>
</body>

</html>

路由参数

动态路由

路由的某一段使用冒号开头,该段路由片段就是路由参数,并且该路由参数会以路由参数为键,参数值为值,被设置到 $route.params对象中,可在每个组件中使用

  • 反斜杠不可省
  • 默认匹配除空字符串外任意路径,但若使用问号结尾,表示该路径为可选
  • 星号可以匹配任意路径,但不匹配反斜杠,/**的不同的
  • 动态路由后使用括号可用正则匹配
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>hello world</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- CDN方式引入Vue -->
  <script src="https://unpkg.com/vue-router@3.4.9/dist/vue-router.js"></script>
</head>

<body>
  <div id="app">
      <ul>
        <li><router-link to="/one/oneId">匹配/one/:id,/one/oneId,可匹配到oneId</router-link></li>
        <li><router-link to="/one">匹配/one/:id,/one,省略后匹配不到</router-link></li>
        <li><router-link to="/two/towId">匹配/two/:id?,/two,可匹配到twoId</router-link></li>
        <li><router-link to="/two">匹配/two/:id?,/two,省略后可匹配到</router-link></li>
        <li><router-link to="/four/100">匹配/four/:id(\\d+),/four/100,数字可匹配到</router-link></li>
        <li><router-link to="/four/fourId">匹配/four/:id(\\d+),/four/fourId,非数字匹配不到</router-link></li>
        <li><router-link to="/three/">匹配/three/*,/three/,有反斜杠可匹配到</router-link></li>
        <li><router-link to="/three*">匹配/three/*,/three,没有反斜杠匹配不到</router-link></li>
      </ul>
    <router-view></router-view>
  </div>
  <script>
    //Vue.use(VueRouter); //若使用模块化机制编程,才需要这么做
    var One = { template: `<h1>One:{{$route.params}}</h1>` } //显示路由参数
    var router = new VueRouter({  //创建路由中间件
      routes: [
        { path: "/one/:id", component: One }, //路由参数使用冒号开头
        { path: "/two/:id?", component: One },  //问号结尾代表该路由参数可省
        { path: "/four/:id(\\d+)", component: One },  //正则限制id只能是数字
        { path: "/three/*", component: One },  //星号可以匹配任意路径
      ]
    })
    const app = new Vue({
      el: "#app",
      router //在该组件中使用路由
    });
  </script>
</body>

</html>

查询字符串

查询字符串会被设置到 $route.query对象中,可在每个组件中使用

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>hello world</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- CDN方式引入Vue -->
  <script src="https://unpkg.com/vue-router@3.4.9/dist/vue-router.js"></script>
</head>

<body>
  <div id="app">
      <ul>
        <li><router-link to="/one?id=1&name=zhangsan">字符串形式:/one?id=1&name=zhangsan</router-link></li>
        <li><router-link :to="{path:'/one',query:{id:2, name:'lisi'}}">对象形式:{path:'/one',query:{id:1, name:'zhangsan'}}</router-link></li>
      </ul>
    <router-view></router-view>
  </div>
  <script>
    //Vue.use(VueRouter); //若使用模块化机制编程,才需要这么做
    var One = { template: `<h1>One:{{$route.query}}</h1>` } //显示路由参数
    var router = new VueRouter({  //创建路由中间件
      routes: [
        { path: "/one", component: One },
      ]
    })
    const app = new Vue({
      el: "#app",
      router //在该组件中使用路由
    });
  </script>
</body>

</html>

props参数解耦

首先为路由设置props属性,开启props路由传参,之后在组件中就可以不使用$router对象获取参数,而可以通过props来获取

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>hello world</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- CDN方式引入Vue -->
  <script src="https://unpkg.com/vue-router@3.4.9/dist/vue-router.js"></script>
</head>

<body>
  <div id="app">
      <ul>
        <li><router-link to="/one/oneId">/one/oneId/</router-link></li>
        <li><router-link to="/two">/two</router-link></li>
        <li><router-link to="/three?id=1">/three?id=1</router-link></li>
      </ul>
    <router-view></router-view>
  </div>
  <script>
    //Vue.use(VueRouter); //若使用模块化机制编程,才需要这么做
    var One = {
      template: `<h1>{{id}}</h1>`,
      props: ["id"]
    }

    var router = new VueRouter({  //创建路由中间件
      routes: [
        { path: "/one/:id", component: One, props: true}, //布尔方式
        { path: "/two", component: One, props: { id: "objId" }},  //对象方式
        { path: "/three", component: One, props(router){ return router.query } }, //函数方式
      ]
    })
    const app = new Vue({
      el: "#app",
      router //在该组件中使用路由
    });
  </script>
</body>

</html>

具名路由和视图

可为路由或视图取一个名字以及别名,可用于对象形式的路由跳转和vue-devtools浏览器插件检测到

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>hello world</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- CDN方式引入Vue -->
  <script src="https://unpkg.com/vue-router@3.4.9/dist/vue-router.js"></script>
</head>

<body>
  <div id="app">
    <router-link :to="{name:'oneName'}">通过绑定对象方式更方便访问/one路由 :to="{name:'oneName'}</router-link>
    <br>
    <p>访问 /2 与访问/tow 是一致的</p>
    <router-link to="/2">/2</router-link>
    <br>
    <router-link to="/two">/tow</router-link>
    <router-view></router-view>	<!-- 默认路由视图名为default -->
    <router-view name="view"></router-view>	<!-- 使用name属性命名 -->
  </div>
  <script>
    //Vue.use(VueRouter); //若使用模块化机制编程,才需要这么做
    var One = { template: `<h1>One</h1>` }
    var Two = { template: `<h1>Two</h1>` }
    //定义两个局部组件,用于在路由中间件中注册

    var router = new VueRouter({  //创建路由中间件
      routes: [
        {
          path: "/one",
          name: "oneName",    //为 /one 取一个名字为 oneName
          components: { default: One, view: Two } //component变为components
        },
        { 
          path: "/two",
          alias: "/2",  //为 /two 取一个别名为 /2
          components: { default: Two, view: One } //component变为components
        }
      ]
    })
    var app = new Vue({
      el: "#app",
      router //在该组件中使用路由
    });
  </script>
</body>

</html>

导航守卫

导航守卫主要用来通过跳转或取消的方式守卫导航,每个守卫方法接收三个参数:

  • to: 即将要进入的目标路由对象
  • from: 当前导航正要离开的路由
  • next: 一定要调用该方法来解析这个钩子
    • next(): 进行下一个钩子
    • next(false): 中断当前的导航
    • next(路由): 跳转到一个不同的路由地址

全局守卫

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>hello world</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- CDN方式引入Vue -->
  <script src="https://unpkg.com/vue-router@3.4.9/dist/vue-router.js"></script>
</head>

<body>
  <div id="app">
    <router-link to="/one">one</router-link> 
    <router-link to="/two">two</router-link>
    <router-view></router-view>	<!-- 路由组件占位符 -->
  </div>
  <script>
    //Vue.use(VueRouter); //若使用模块化机制编程,才需要这么做
    var One = { template: `<h1>One</h1>` }
    var Two = { template: `<h1>Two</h1>` }
    //定义两个局部组件,用于在路由中间件中注册
    var router = new VueRouter({  //创建路由中间件
      routes: [
        { path: "/one", component: One }, //path用于设置该路由路径
        { path: "/two", component: Two }	//component用于设置该路由下的组件
      ]
    })

    //全局守卫
    router.beforeEach((to, from, next) => {	//路由跳转前执行
      console.log("beforeEach");
      next();
    })
    router.beforeResolve((to, from, next) => {	//所有组件内守卫被解析后执行
      console.log("beforeResolve");
      next();
    })
    router.afterEach((to, from) => {	//路由跳转后执行
      console.log("afterEach");
    })

    var app = new Vue({
      el: "#app",
      router //在该组件中使用路由
    });
  </script>
</body>

</html>

路由独享的守卫

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>hello world</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- CDN方式引入Vue -->
  <script src="https://unpkg.com/vue-router@3.4.9/dist/vue-router.js"></script>
</head>

<body>
  <div id="app">
    <router-link to="/one">one</router-link> 
    <router-link to="/two">two</router-link>
    <router-view></router-view>	<!-- 路由组件占位符 -->
  </div>
  <script>
    //Vue.use(VueRouter); //若使用模块化机制编程,才需要这么做
    var One = { template: `<h1>One</h1>` }
    var Two = { template: `<h1>Two</h1>` }
    //定义两个局部组件,用于在路由中间件中注册
    var router = new VueRouter({  //创建路由中间件
      routes: [
        { 
          path: "/one",
          component: One,
          beforeEnter(to, from, next){ 	//路由独享守卫
              console.log("beforeEnter");
              next();
          }
        },
        { path: "/two", component: Two }
      ]
    })

    var app = new Vue({
      el: "#app",
      router //在该组件中使用路由
    });
  </script>
</body>

</html>

组件内的守卫

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>hello world</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- CDN方式引入Vue -->
  <script src="https://unpkg.com/vue-router@3.4.9/dist/vue-router.js"></script>
</head>

<body>
  <div id="app">
    <router-link to="/one">/one</router-link> 
    <router-link to="/two">/two</router-link>
    <br>
    只有路由不同,但组件相同时,Vue-Router会复用组件,这时beforeRouteUpdate才会触发
    <router-link to="/one/oneId1">/one/oneId1</router-link> 
    <router-link to="/one/oneId2">/one/oneId2</router-link> 
    <strong>(只有使用动态路由参数时才行,显示注册的路由却不可以)</strong>
    <router-view></router-view>	<!-- 路由组件占位符 -->
  </div>
  <script>
    //Vue.use(VueRouter); //若使用模块化机制编程,才需要这么做
    var One = {
      template: `<h1>One</h1>`,
      beforeRouteEnter (to, from, next) { //路由进入该组件时触发
        console.log("beforeRouteEnter");
       //beforeRouteEnter中不能访问this,因为在最之前调用,不确定是否要跳转,因此即将登场的新组件还没被创建
        next((vm=>{	//vm就是新创建的组件,该回调在整个路由跳转过程中最后执行
          console.log(vm);
        }));	//可使用next方法异步获取组件
      },
      beforeRouteUpdate (to, from, next) {  //动态路由复用组件时触发
        console.log("beforeRouteUpdate");
        next();
      },
      beforeRouteLeave (to, from, next) { //路由离开该组件时触发
        console.log("beforeRouteLeave");
        next();
      }
    }
    var Two = { template: `<h1>Two</h1>` }
    //定义两个局部组件,用于在路由中间件中注册
    var router = new VueRouter({  //创建路由中间件
      routes: [
        { path: "/one/:id", component: One },
        { path: "/one", component: One },
        { path: "/two", component: Two }
      ]
    })

    var app = new Vue({
      el: "#app",
      router //在该组件中使用路由
    });
  </script>
</body>

</html>

完整路由守卫执行顺序

  1. 在离开的组件里调用beforeRouteLeave
  2. 调用全局的beforeEach
  3. 在重用的组件里调用beforeRouteUpdate
  4. 在路由配置里调用beforeEnter
  5. 在被进入的组件里调用beforeRouteEnter
  6. 调用全局的beforeResolve
  7. 调用全局的afterEach
  8. DOM更新后调用beforeRouteEnter中传给next的回调函数

滚动行为

当组件很大时,点击router-link后只是切换组件,页面并不会滚动,而切换后的组件又很小,导致页面白屏,可通过在路由对象中定义scrollBehavior方法来解决,该方法在每次切换路由时都会执行,返回值对象作为页面滚动距离参考

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>hello world</title>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- CDN方式引入Vue -->
  <script src="https://unpkg.com/vue-router@3.4.9/dist/vue-router.js"></script>
  <style>h1{height: 1000px;}</style>
</head>

<body>
  <div id="app">
    <router-view></router-view>
    <router-link to="/one">one</router-link>
    <router-link to="/two">two</router-link>
  </div>
  <script>
    //Vue.use(VueRouter); //若使用模块化机制编程,才需要这么做
    var One = { template: `<h1>one:向下滑动点击按钮</h1>` }
    var Two = { template: `<h1>two:向下滑动点击按钮</h1>` }
    //定义两个局部组件,用于在路由中间件中注册

    var router = new VueRouter({  //创建路由中间件
      scrollBehavior (to, from, savedPosition) {
        //to是切换前的路由,from是切换后的路由
        if (savedPosition) {	//当savedPosition对象存在时,说明是按的浏览器后退键或前进键
          return savedPosition	//使用上次离开页面时的滚动距离
        } else {
          return { x: 0, y: 0 }	//否则会滚动到页面顶部
        }
      },
      routes: [
        { path: "/one", component: One }, //path用于设置该路由路径
        { path: "/two", component: Two }	//component用于设置该路由下的组件
      ]
    })
    var app = new Vue({
      el: "#app",
      router //在该组件中使用路由
    });
  </script>
</body>

</html>
此作者没有提供个人介绍
最后更新于 2022-02-06