Loading

Vue2 路由

Vue 路由

基本使用

安装

npm i vue-router@3

配置

  1. 在 src 目录下生成 router/index.js 用于存放 router 的配置。

router/index.js

// 引入 vue-router
import VueRouter from "vue-router"
// 引入相关组件
import About from "../components/About.vue"
import Home from "../components/Home.vue"

export default new VueRouter({
  routes: [
    { path: "/", redirect: "/about" },
    { path: "/about", component: About },
    { path: "/home", component: Home }
  ]
})
  1. 在 main.js 中引入 router。
// import Vue from "vue"
import Vue from "vue"
import App from "./App.vue"
// 引入 vue-router
import VueRouter from "vue-router"
// 引入配置好的 router
import router from "./router"

// 关闭生产提示
Vue.config.productionTip = false

// 使用插件
Vue.use(VueRouter)

new Vue({
  render: (h) => h(App),
  // 添加 router
  router
}).$mount("#app")

使用

在 router 的配置文件中设置组件的路由配置。

设置好后,在组件中将需要跳转的 a 链接使用 vue-router 的 router-link 代替。然后再需要显示组件视图的地方使用 router-veiw 标签。

以下实例使用了 bootstrap 3.3.5

<link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.5/dist/css/bootstrap.min.css" rel="stylesheet" />

App.vue

<template>
  <div id="app">
    <div class="row">
      <div class="col-xs-offset-2 col-xs-8">
        <div class="page-header"><h2>Vue Router Demo</h2></div>
      </div>
    </div>
    <div class="row">
      <div class="col-xs-2 col-xs-offset-2">
        <div class="list-group">
          <!-- <a class="list-group-item active" href="./about.html">About</a>
          <a class="list-group-item" href="./home.html">Home</a> -->
          <router-link class="list-group-item" active-class="active" to="/about">About</router-link>
          <router-link class="list-group-item" active-class="active" to="/home">Home</router-link>
        </div>
      </div>
      <div class="col-xs-6">
        <div class="panel">
          <div class="panel-body">
            <router-view></router-view>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    name: "App"
  }
</script>
  • <router-link class="list-group-item" active-class="active" to="/about">About</router-link>用于代替 a 链接。
    • 其中 active-class="active"表示,处于激活选中的链接使用.active样式。
    • to="/about"表示点击后需要路由到的地址。其中的/要注意携带。
  • <router-view></router-view>用于展示路由之后的组件。组件将显示在此处。
  • 使用 router 来调用组件时不需要手动引入组件,并再 components 中配置。因为再路由的配置文件 index.js 中已经引入过相关组件了。

深入解析

路由组件与一般组件

由路由调用显示的组件我们叫做路由组件,一般放置于 pages 文件夹。

正常引入由标签手动调用的组件叫做一般组件,一般放置于 components 文件夹。

组件切换

组件进行切换时,其他组件就被 router 销毁了。之后我们可以使用 keep-alive 来让其不销毁。

我们通过操作钩子就可以轻松验证。

Home.vue

<template>
  <h2>我是Home的内容</h2>
</template>

<script>
export default {
  name:"Home",
  mounted() {
    console.log("home 组件被挂载");
  },
  destroyed() {
    console.log("home 组件被销毁");
  },
}
</script>

vc 实例对象分析

我们使用了 vue-router 之后,我们的 vc 实例对象自身就多了 $route 和 $router 两个属性。

$route 里面存储着路由规则。里面存储了当前组件的一些路由信息,是当前组件独有的。

$router 是整个应用的路由器,整个应用只有一个,所以不同 vc 身上的 $router 都是同一个路由器。

嵌套路由

嵌套路由需要在路由的配置中,在需要嵌套的对象身上继续套娃添加 children 属性,以数组形式嵌套。

router/index.js

// 引入 vue-router
import VueRouter from "vue-router"
// 引入相关组件
import About from "../pages/About.vue"
import Home from "../pages/Home.vue"
import News from "../pages/News.vue"
import Message from "../pages/Message.vue"

export default new VueRouter({
  routes: [
    { path: "/", redirect: "/about" },
    { path: "/about", component: About },
    {
      path: "/home",
      component: Home,
      children: [
        { path: "", redirect: "news" },
        { path: "news", component: News },
        { path: "message", component: Message }
      ]
    }
  ]
})
  • 在嵌套路由 children 中配置 path 时,router 会自动为我们加上/所以不需要我们去手动添加,如果添加后,如path="/news",那么进行路由到组件的时候识别的是//news,就不会将 news 组件显示出来。
  • 一般来说可以多层嵌套,但是实际中嵌套到五六层就已经很少见了。

home.vue

<template>
  <div>
    <h2>我是Home的内容</h2>
    <ul class="nav nav-tabs">
      <li>
        <!-- <a class="list-group-item" href="./home-news.html">News</a> -->
        <router-link
          class="list-group-item"
          active-class="active"
          to="./news"
          >News</router-link
        >
      </li>
      <li>
        <router-link
          class="list-group-item"
          active-class="active"
          to="/home/message"
          >Message</router-link
        >
      </li>
    </ul>
    <router-view></router-view>
  </div>
</template>

<script>
  export default {
    name: "Home"
  }
</script>

  • 嵌套路由在组件中使用方法和一层的时候类似。
  • 对于 to 属性,我们在这里需要将完整地址写上如to="/home/message",因为再配置文件中是嵌套配置的,需要先去找外层的 home,然后再去找内层 message,所以这里的 to 要写全。也可以使用./简写,如to="./message",这里的./就已经代表了/home,所以也是写全了的。

命名路由

为了规避过长的 path 产生的不便,router 允许我们为路由进行命名。

这里仅部分举例 index.js

export default new VueRouter({
  routes: [
    { path: "/", redirect: "/about" },
    { name: "guanyu", path: "/about", component: About },
    {
      name: "jia",
      path: "/home",
      component: Home,
      children: [
        { path: "", redirect: "news" },
        { name: "xinwen", path: "news", component: News },
        { name: "xinxi", path: "message", component: Message }
      ]
    }
  ]
})

在 Home.vue 中使用

<router-link
          class="list-group-item"
          active-class="active"
          :to="{
             name:'xinwen'
          }"
>News</router-link>
  • 如果使用 name 进行跳转时,需要给 to 传递一个包含 name 属性的对象。注意需要使用:to,因为这样传递的才是表达式,否则全部视为字符串。
  • 这里的 :to="{name:'xinwen'}"to="/home/news":to="{path:'/home/news'}"等价。

路由间传递值

在使用 vue-router 之后,vc 上会存在 $route 属性,来携带自身的路由信息。其中就有专门用于传递值的 query 和 params。

使用 query 传值

query 传值可以直接写在 router-link 标签的 to 上,也可以以对象的形式赋值给 to。

Message.vue

<template>
  <div>
    <ul>
      <li v-for="m in messageList" :key="m.id">
        <!-- <a href="/message1">{{ m.title }}</a> -->
        <!-- <router-link :to="`/home/message/detail?id=${m.id}&title=${m.title}`">{{
        m.title
      }}</router-link> -->
        <router-link
          :to="{
            path: '/home/message/detail',
            query: { id: m.id, title: m.title }
          }"
          >{{ m.title }}</router-link
        >
      </li>
    </ul>
    <hr />
    <router-view></router-view>
  </div>
</template>

<script>
  export default {
    name: "Message",
    data() {
      return {
        messageList: [
          { id: "001", title: "消息001" },
          { id: "002", title: "消息002" },
          { id: "003", title: "消息003" }
        ]
      }
    }
  }
</script>
  • 如果需要在字符串中传递变量可以使用模板字符串,在需要的位置使用${变量}直接进行拼接。

  • :to="`/home/message/detail?id=${m.id}&title=${m.title}`"
    :to="{
                path: '/home/message/detail',
                query: { id: m.id, title: m.title }
    }"
    上面二者等价
    

使用 params 传值

使用 params 传值,需要在路由配置中配置需要传递的值。

index.js

export default new VueRouter({
  routes: [
    { path: "/", redirect: "/about" },
    { name: "guanyu", path: "/about", component: About },
    {
      name: "jia",
      path: "/home",
      component: Home,
      children: [
        { path: "", redirect: "news" },
        { name: "xinwen", path: "news", component: News },
        {
          name: "xinxi",
          path: "message",
          component: Message,
          children: [
              // 需要在此配置 params 需要传递的值
            { name: "xiangqing", path: "detail/:id/:title", component: Detail }
          ]
        }
      ]
    }
  ]
})

Message.vue

<template>
  <div>
    <ul>
      <li v-for="m in messageList" :key="m.id">
        <!-- <a href="/message1">{{ m.title }}</a> -->
        <!-- <router-link :to="`/home/message/detail/${m.id}/${m.title}`">{{
        m.title
      }}</router-link> -->
        <router-link
          :to="{
            name: 'xiangqing',
            params: { id: m.id, title: m.title }
          }"
          >{{ m.title }}</router-link
        >
      </li>
    </ul>
    <hr />
    <router-view></router-view>
  </div>
</template>

<script>
  export default {
    name: "Message",
    data() {
      return {
        messageList: [
          { id: "001", title: "消息001" },
          { id: "002", title: "消息002" },
          { id: "003", title: "消息003" }
        ]
      }
    }
  }
</script>
  • 如果不适用对象形式给 to 传递,直接以 /xxx/xxx 在地址后拼接需要传递的值。
  • 如果使用 to 的对象形式,那么使用 params 传递值时,不能使用 path 指定路由地址,必须使用 name 来指定路由地址。

路由的 props 配置

index.js 部分

export default new VueRouter({
  routes: [
    { path: "/", redirect: "/about" },
    { name: "guanyu", path: "/about", component: About },
    {
      path: "/home",
      component: Home,
      children: [
        { path: "", redirect: "news" },
        { name: "xinwen", path: "news", component: News },
        {
          name: "xinxi",
          path: "message",
          component: Message,
          children: [
            {
              name: "xiangqing",
              path: "detail/:id/:title",
              component: Detail,
              // 第一种 props 写法,在配置中直接传递确定值,不需要在 path 配置中占位
              // props: { a: 1, b: 2 }
              // 第二中 props 写法,默认将组件中 to 的 params 以 props 形式传递给子组件, 不会理会 query, 需要在 path 配置中占位
              // props: true
              // 第三种 既可以传递 query 也可以传递 params, 如果是 params 需要在 path 配置中占位
              // props($route) {
              //   return {
              //     id: $route.params.id,
              //     title: $route.params.title
              //   }
              // }
              // 解构赋值
              props({ params: { id, title } }) {
                return {
                  id,
                  title
                }
              }
              // props({ query: { id, title } }) {
              //   return {
              //     id,
              //     title
              //   }
              // }
            }
          ]
        }
      ]
    }
  ]
})

Message.vue

<template>
  <div>
    <ul>
      <li v-for="m in messageList" :key="m.id">
        <!-- <a href="/message1">{{ m.title }}</a> -->
        <!-- <router-link :to="`/home/message/detail/${m.id}/${m.title}`">{{
        m.title
      }}</router-link> -->
        <router-link
          :to="{
            name: 'xiangqing',
            params: { id: m.id, title: m.title }
          }"
          >{{ m.title }}</router-link
        >
      </li>
    </ul>
    <hr />
    <router-view></router-view>
  </div>
</template>

<script>
  export default {
    name: "Message",
    data() {
      return {
        messageList: [
          { id: "001", title: "消息001" },
          { id: "002", title: "消息002" },
          { id: "003", title: "消息003" }
        ]
      }
    }
  }
</script>

Detail.vue

<template>
  <ul>
    <li>消息编号:{{ id }}</li>
    <li>消息标题:{{ title }}</li>
    <!-- <p>a: {{ a }}, b: {{ b }}</p> -->
  </ul>
</template>

<script>
  export default {
    name: "Detail",
    props: ["id", "title"]
  }
</script>
  • 使用 props 传值,不管是哪种,都需要在子组件中使用 props 接收。

编程式路由导航

路由历史记录

push

在浏览器进行路由跳转时,浏览器保留每一条历史记录,返回时按跳转顺序返回。

使用 router-link 进行跳转时,默认使用的就是使用的 push,可以在标签属性中添加 replace 来指定使用 replace。如<router-link replace></router-link>

replace

在浏览器进行路由跳转时,替换当前的历史记录。比如从 /home,跳转到/home/news时,会将/home历史记录替换掉。

编程式路由跳转

编程式路由跳转就是用 js 代码进行跳转。

跳转时我们用的是路由器 $router。

Message.vue

<template>
  <div>
    <ul>
      <li v-for="m in messageList" :key="m.id">
        <!-- <a href="/message1">{{ m.title }}</a> -->
        <!-- <router-link :to="`/home/message/detail/${m.id}/${m.title}`">{{
        m.title
      }}</router-link> -->
        <router-link
          :to="{
            name: 'xiangqing',
            params: { id: m.id, title: m.title }
          }"
          >{{ m.title }}</router-link
        >
        <button @click="pushShow(m)">push</button>
        <button @click="replaceShow(m)">replace</button>
      </li>
    </ul>
    <hr />
    <router-view></router-view>
  </div>
</template>

<script>
  export default {
    name: "Message",
    data() {
      return {
        messageList: [
          { id: "001", title: "消息001" },
          { id: "002", title: "消息002" },
          { id: "003", title: "消息003" }
        ]
      }
    },
    methods: {
      pushShow(m) {
        this.$router.push({
          name: "xiangqing",
          params: { id: m.id, title: m.title }
        })
      },
      replaceShow(m) {
        this.$router.replace({
          name: "xiangqing",
          params: { id: m.id, title: m.title }
        })
      },
      back() {
        this.$router.back()
      },
      forward() {
        this.$router.forward()
      }
    }
  }
</script>
  • $router.push()保留历史路由跳转。
  • $router.replace()替换上一个历史路由跳转。
  • $router.back()后退到上一个页面历史。
  • $router.forward()前进到下一个页面历史。

缓存路由组件

我们进行路由跳转时,有时需要保留数据。但是路由跳转后默认会销毁组件。这时就需要用到我们的缓存路由组件了。

缓存路由组件只需要在 router-view 外层包裹 keep-alive 标签即可

<template>
  <div>
    <h2>我是Home的内容</h2>
    <ul class="nav nav-tabs">
      <li>
        <!-- <a class="list-group-item" href="./home-news.html">News</a> -->
        <router-link class="list-group-item" active-class="active" to="./news"
          >News</router-link
        >
      </li>
      <li>
        <router-link
          class="list-group-item"
          active-class="active"
          to="/home/message"
          >Message</router-link
        >
      </li>
    </ul>
      // 缓存路由组件,include指定被缓存的组件名。
    <keep-alive include="News">
      <router-view></router-view>
    </keep-alive>
  </div>
</template>

<script>
  export default {
    name: "Home",
    destroyed() {
      console.log("home 组件被销毁")
    }
  }
</script>
  • 如果 keep-alive 不指定 include 组件,那么所有路由组件都将进行缓存。如果指定了只有指定组件进行缓存。
  • 在 include 中指定的是组件的名字
  • 指定多个组件需要用数组的形式如:
<keep-alive :include="['News','Message']">
      <router-view></router-view>
</keep-alive>

新增钩子

  • activated(){}:组件被激活时调用。
  • deactivated(){}:组件失活时调用。
  • 另一个声明周期图外的钩子是$nextTick(() => {}),作用是等当前组件渲染完毕再调用里面的代码。

路由守卫

再路由进行跳转时,守卫可以为我们再路由跳转前或路由跳转后做相应的逻辑判断。

全局前置守卫

全局前置守卫声明再路由的配置文件中,当初始化的时候被调用,每次路由切换之前也被调用。

const router = new VueRouter({
  routes: [...]
})
// 全局前置守卫
router.beforeEach((to, from, next) => {
  if (to.path === "/about") {
    alert("不允许访问about页面")
  } else {
    next()
  }
})

export default router
  • 需要再暴露之前设置全局前置守卫。

全局后置守卫

// 引入 vue-router
import VueRouter from "vue-router"
// 引入相关组件
import About from "../pages/About.vue"
import Home from "../pages/Home.vue"
import News from "../pages/News.vue"
import Message from "../pages/Message.vue"
import Detail from "../pages/Detail.vue"

const router = new VueRouter({
  routes: [
    { path: "/", redirect: "/about" },
    {
      name: "guanyu",
      path: "/about",
      component: About,
      meta: { title: "About" }
    },
    {
      path: "/home",
      component: Home,
      meta: { title: "Home" },
      children: [
        { path: "", redirect: "news" },
        {
          name: "xinwen",
          path: "news",
          component: News,
          meta: { title: "News" }
        },
        {
          name: "xinxi",
          path: "message",
          component: Message,
          meta: { title: "Message" },
          children: [
            {
              name: "xiangqing",
              path: "detail/:id/:title",
              component: Detail,
              // 解构赋值
              props({ params: { id, title } }) {
                return {
                  id,
                  title
                }
              }

            }
          ]
        }
      ]
    }
  ]
})
// 全局前置守卫
router.beforeEach((to, from, next) => {
  if (to.path === "/about") {
    alert("不允许访问about页面")
  } else {
    next()
  }
})
// 全局后置守卫
router.afterEach((to, from) => {
  // 更改标题
  document.title = to.meta.title || "路由demo"
})

export default router

独享路由守卫

独享前置守卫需要写在路由配置中

{
          name: "xinwen",
          path: "news",
          component: News,
          meta: { title: "News" },
          // 独享前置守卫
          beforeEnter: (to, from, next) => {
            let pass = prompt("请输入2查看")
            if (pass == 2) next()
          }
}

组件内路由守卫

  • beforeRouteEnter(to, from, next){}:通过路由规则,进入该组件前被调用。有人来到这个组件时调用。next 允许进入。
  • beforeRouteLeave(to, from, next){}:通过路由规则,离开该组件时被调用。有人离开这个组件时调用。next 允许离开
<template>
  <h2>我是About的内容</h2>
</template>

<script>
  export default {
    name: "About",
    beforeRouteEnter(to, from, next) {
      if (prompt("输入2进入about") == 2) next()
    },
    beforeRouteLeave(to, from, next) {
      if (prompt("输入3离开about") == 3) next()
    }
  }
</script>
  • 由于都是进入这个组件,所以 to.path 永远都是当前组件的路由地址。

history 模式与 hash 模式

hash 模式(哈希)

在 http 地址中 # 后面的值全都是哈希值,请求时并不会作为路径的一部传递给服务器。

history 模式(工厂)

在 http 中没有 #。

区别

  1. hash 模式的兼容性更好,history 模式兼容性略差。
  2. history 模式当项目部署到服务器时,刷新页面时会出错。

history 模式的问题

当我们打包后,将项目部署到服务器。如果我们在页面内实现跳转时,是 vue 的路由器帮我们跳转查找面,并不是服务器帮我们查找。路由器查找的同时,将我们的 url 替换成查找地址。比如localhost:8888/home/message

但是这时我们进行页面刷新时,并不是 vue 的路由器帮我们查找页面,我们会直接把 url 中的内容发送给服务器,进行请求。但是由于我们时单页面应用,并没有相应的文件。所以就会出现报错。

解决 history 模式问题

  1. 我们使用 hash 模式时可以解决此问题,因为 # 后面的东西不会发送给服务器。
  2. 我们在后端中使用响应的中间件来解决此问题。在 node.js 中我们可以使用 connect-history-api-fallback 中间件来解决此问题。

在 router 修改路由模式

export default new VueRouter({
  mode:"history",
  routes: [
    { path: "/", redirect: "/about" },
    { name: "guanyu", path: "/about", component: About },
    {
      name: "jia",
      path: "/home",
      component: Home,
      children: [
        { path: "", redirect: "news" },
        { name: "xinwen", path: "news", component: News },
        {
          name: "xinxi",
          path: "message",
          component: Message,
          children: [
              // 需要在此配置 params 需要传递的值
            { name: "xiangqing", path: "detail/:id/:title", component: Detail }
          ]
        }
      ]
    }
  ]
})
  • 路由模式默认为 hash 模式。
posted @ 2022-10-03 13:31  brokyz  阅读(180)  评论(0编辑  收藏  举报