# Vue Router 路由传参、别名、匹配语法、编程式导航

TIP

从本节内容开始我们正式学习 Vue Router 核心基础 和 在实际开发中的实践应用相关内容

  • Vue Router 的基本用法
  • Vue Router 路由传参
  • 路由别名
  • 命名视图
  • 动态路由的匹配语法
  • Vue Router 编程式导航
  • 实战应用:项目框架搭建

# 一、Vue Router 的基本用法

TIP

Vue Router 是 Vue.js (opens new window) 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建SPA 单页应用变得轻而易举。在 Vue3 中需要安装vue-router4 版本。

Vue Router 官网地址:https://router.vuejs.org/zh/guide/ (opens new window)

# 1、使用 vue-router 的步骤

TIP

在 vue3 中使用vue-router@4.x 版本步骤

  • 第一步:安装vue-router@4
  • 第二步:定义路由组件
  • 第二步:创建路由实例
  • 第三步:配置路由映射:路径和组件映射关系
  • 第四步:在 app 应用实例中挂载创建的路由实例
  • 第五步:使用路由:通过<router-link><router-view>组件

接下来,我们按以上步骤,最终实现如下效果的单页面应用

GIF2023-8-218-02-39

# 1.1、第一步:安装vue-router@4.x版本

TIP

执行以下 npm 命令,安装vue-router4.x 版本,安装成生产依赖

npm install vue-router@4

# 1.2、第二步:定义路由组件

TIP

src/views/目录下新建当前应用需要用到的所有路由组件(也称视图组件)

views / Home.vue; // Home 组件
views / About.vue; //  About 组件
views / News.vue; // News 组件

# 1.3、第三步:创建路由实例

TIP

src/router/目录下新建router.js文件,此文件最终对外暴露router路由实例

import { createRouter, createWebHashHistory } from "vue-router";

// createRouter方法,用来创建路由实例,参数为一个对象,用来配置路由相关信息
const router = createRouter({
  // 路由模式,createWebHashHistory()创建 hash 模式。
  history: createWebHashHistory(),
});

// 对外暴露router实例
export { router };

# 1.4、第四步:配置路由映射 - 路径和组件映射关系

TIP

  • ①、导入路由组件
  • ②、定义路由:路径与组件的映射关系
  • ③、在createRouter方法的参数中配置routes选项
import { createRouter, createWebHashHistory } from "vue-router";

// 1、导入路由组件 -------------------
import Home from "../views/Home.vue";
import About from "../views/About.vue";
import News from "../views/News.vue";

// 2、定义一些路由: 路径和组件映射关系 -------------------
const routes = [
  {
    path: "/", // 路径
    component: Home, // 路径对应渲染的组件
  },
  {
    path: "/about",
    component: About,
  },
  {
    path: "/news",
    component: News,
  },
];

// createRouter方法,用来创建路由实例,参数为一个对象,用来配置路由相关信息
const router = createRouter({
  // 路由模式,createWebHashHistory()创建 hash 模式。
  history: createWebHashHistory(),
  // 3、配置routes -------------------
  routes, // 'routes':routes的缩写
});

// 对外暴露router实例
export { router };

# 1.5、第五步:全局注册 router 实例

TIP

main.js中利用 app.use 方法注册 router 路由实例

import { createApp } from "vue";
import App from "./App.vue";
// 导入router实例
import { router } from "./router/router.js";
const app = createApp(App);
// 全局注册路由
app.use(router);
app.mount("#app");

TIP

可以在App.vue组件中调用<router-link><router-view>组件使用路由

  • <router-link> 组件用来创建链接,最终会生成<a>标签形式的导航
  • <router-view>组件用来显示与 Url 对应的组件内容
<!--App组件-->
<template>
  <ul class="router-link">
    <!--创建a标签链接  to属性最终转换为a标签的href属性值-->
    <li><router-link to="/">网站首页</router-link></li>
    <li><router-link to="/about">关于我们</router-link></li>
    <li><router-link to="/news">新闻中心</router-link></li>
  </ul>

  <div class="router-view">
    <!--url对应组件内容渲染出口-->
    <router-view></router-view>
  </div>
</template>

<style>
  .router-link {
    display: flex;
    justify-content: center;
    text-align: center;
    list-style: none;
    margin-top: 30px;
  }

  .router-link li {
    margin: 0 10px;
  }

  .router-link li a {
    color: #000;
    font-size: 16px;
    text-decoration: none;
  }

  .router-link li a:hover {
    color: tomato;
  }
  .router-view {
    width: 80%;
    margin: 30px auto;
    font-size: 20px;
    min-height: 300px;
    background-color: rgb(251, 249, 249);
  }
</style>

经过以上 6 步,最终我们完成了刚开始我们期望的效果。

# 2、路由模式

TIP

createRouter方法参数对象的history属性用来指定路由的模式,当history的值为

  • createWebHashHistory() 创建 hash 路由模式
  • createWebHistory() 创建 history 路由 模式
const router = createRouter({
  // history属性指定路由模式
  history: createWebHashHistory(), // 创建 hash 路由 模式。
  //  history: createWebHistory(),  // 创建 history 路由 模式
});

# 3、链接激活时 Class 类名

TIP

默认情况下,链接被激活时会加上router-link-active Class 类名,比如前面提到的案例

image-20230802180836683

我们可以通过添中router-link-activeClass 类名,来控制被激活链接的样式

/* 在上面案例的基础上,在style标签中 添加如下css样式 */
.router-link li a.router-link-active {
  color: tomato;
}

添加以上 css 后,最终渲染效果如下

GIF2023-8-218-14-48

我们还可以设置<router-link>组件的active-class属性值来更改链接激活时使用的 CSS 类名

/* 我们修改上面案例中的如下css样式 */
.router-link li a.router-link-active {
  color: tomato;
}
/* 修改成 */
.router-link li a.active {
  color: tomato;
}

以上代码,最终渲染后效果如下:

image-20230802182113674

关于什么情况下会加上的router-link-exact-activeClass 类名,及有什么用,后面会讲到

# 4、去掉路由历史记录

TIP

如果想在页面切换时,不留下历史记录,也就是在浏览器最顶部左侧不会出现前进和后退按扭。我们只需要在<router-link>标签上添加replace属性(表示用当前路径替换之前路径)

修改以上案例中<App>组件中 所有<router-link>标签内容如下:

<ul class="router-link">
  <!--创建a标签链接  to属性最终转换为a标签的href属性值-->
  <li>
    <router-link to="/" replace active-class="active">网站首页</router-link>
  </li>
  <li>
    <router-link to="/about" replace active-class="active"
      >关于我们</router-link
    >
  </li>
  <li>
    <router-link to="/news" replace active-class="active">新闻中心</router-link>
  </li>
</ul>

最终效果如下,注意观察,导航切换时,浏览器最顶部前进和后退按扭一直是不灰色,不可点状态。

GIF2023-8-218-31-06

# 5、访问router实例和当前路由route

TIP

通过调用 app.use(router)注册 vue-router 插件后。

选项式 API 中,我们可以在任意的组件中以

  • this.$router访问到 router 实例 (即:createRouter()方法返回的 router 实例)
  • this.$route访问到当前路由

组合式 API 中,我们可以在任意组件中,调用vue router提供的

  • useRouter 函数访问到 router 实例
  • useRoute 函数访问到当前路由

在任意组件的模板中,通过

  • $router 访问到 router 实例
  • $route 访问到当前路由

路由实例方法查阅教程 (opens new window) | 路由对象属性查阅教程 (opens new window)

用以下代码,替换前面案例中的About组件内容

<script>
  export default {
    data() {
      // this.$router.currentRoute.value.path 当有url后面的路径值
      console.log("选项式API:路由实例", this.$router.currentRoute.value.path);
      console.log("选项式API:当前路由信息", this.$route.path);
      return {};
    },
  };
</script>
<script setup>
  import { useRouter, useRoute } from "vue-router";
  console.log("组合式API:路由实例", useRouter().currentRoute.value.path);
  console.log("组合式API:当前路由信息", useRoute().path);
</script>

<template>
  <div>关于我们</div>
  <div>路由实例:{{ $router.currentRoute.value.path }}</div>
  <div>当前路由的路径:{{ $route.path }}</div>
</template>

最终渲染后效果如下:

image-20230802190555269

# 6、嵌套路由

TIP

以下图中展示的效果就是一个嵌套路由效果

  • 在点击News导航时,会显示对应组件的内容,在该组件中还存在二级导航,点击对应二级导航时,会加载不同的组件来显示不同内容。
  • 我们把/home/about/news看作是一级路由,则/news/tab1/news/tab2/news/tab3可以看成是二级路由。
  • 并且这些路由显示的内容是嵌套在/news路由所在组件中,所以我们在定义路由时,需要把这些二级路由作为/news路由的子路由。

GIF2023-8-219-03-19

以下代码为/news一级路由定义了三个子路由 ,记得先用import导入Tab1Tab2Tab3(用以下代码替换前面案例中的对应代码)

{
    path: "/news",
    component: News,
     // 所有子路由的书写方式和父路由一样,父路由对应子路由按以下方式写在children数组中
    children: [
        {
            // 这里不需要加 / ,加/ 表示的是绝对路径  http://www.xx.com/tab1
            // 不加表示相对父路由而言  http://www.xx.com/news/tab1
            path: "tab1",
            component: Tab1,
        },
        {
            path: "tab2",
            component: Tab2,
        },
        {
            path: "tab3",
            component: Tab3,
        }

    ]
}

子路由是在父路由组件中显示的,所以我们需要在父路由组件News中来定义子路由链接及路由组件内容渲染出口

用以下代码替换掉前面案例中的News.vue组件内容。

<!--News.vue 组件-->
<template>
  <div class="menu">
    <router-link to="/news/tab1">最新动态</router-link> |
    <router-link to="/news/tab2">热门推荐</router-link> |
    <router-link to="/news/tab3">历史动态</router-link>
  </div>

  <div class="main">
    <router-view />
  </div>
</template>

<style scoped>
  .menu a {
    font-size: 16px;
  }

  .router-link-active {
    color: #fff;
    background-color: skyblue;
    padding: 5px;
    border-radius: 10px;
  }
</style>

按以上两步操作后,就可以实现如上图所示案例效果。

关于更深层级的路由嵌套和上面一样,一层一层嵌套下去就好。

# 7、链接精确激活时 Class 类名

TIP

默认情况下,链接精确激活时会加上router-link-exact-active Class 类名。

链接精确激活与链接激活的区别

  • 精确激活: 当地址栏链接为/news/tab1时,则只有链接/news/tab1对应的 a 标签会被添加router-link-exact-active Class 类,此时/news/tab1链接为精确激活状态
  • 激活:当地址栏链接为/news/tab1时,链接/news/news/tab1对应的 a 标签会被添加router-link-active Class 类名,此时/news/tab1/news链接为激活状态

激活状态的链接中包含精确激活状态的链接。

下图是链接地址为/news/tab时的截图,我们可以看/news/news/tab1对应的 a 标签都加上激活状态的 class 类名,但只有/news/tab1对应的 a 标签加上了精确激活的 class 类名

image-20230802190846766

如果精确激活的链接样式包含了激活链接的样式,则我们只需要针不同部分的样式,给精确激活的链接添加router-link-exact-active类名来实现。

exact-active-class 属性

我们可以通过<router-link>标签的 exact-active-class属性来自定义精确激活链接时添加的 Class 类名

<router-link to="/news/tab1" exact-active-class="exact-active"
  >最新动态</router-link
>

# 8、命名路由

TIP

在定义路由时,我们可以为路由添加name属性来指定路由的名字

const routes = [
    {

        name: 'home', // 路由名字,名字自定义,不一定是home
        path: "/", // 路径
        component: Home, // 路径对应渲染的组件
    },
    {
        name: 'about',
        path: "/about",
        component: About,

    },
    {
        name: 'news',
        path: "/news",
        component: News,
        children: [
            {
                name:'tab1' // 路由名字
                path: "tab1",
                component: Tab1,
            },
            {
                path: "tab2",
                component: Tab2,
            },
            {
                path: "tab3",
                component: Tab3,
            }

        ]
    }
]

有了name属性后,我们可以向<rotuer-link>组件的 to 属性传递一个对象,来实现路由的跳转。

<!--以下方式,会根据name属性来找到指定的路由中的path值,实现路由跳转-->
<router-link :to="{ name: 'home' }" active-class="active">Home</router-link> |
<router-link :to="{ name: 'about' }" active-class="active">About</router-link> |
<router-link :to="{ name: 'news' }" active-class="active">News</router-link>

<!--以上代码,与以下代码实现的效果一模一样-->
<router-link to="/" active-class="active">Home</router-link> |
<router-link to="/about" active-class="active">About</router-link> |
<router-link to="/news" active-class="active">News</router-link>

<router-link>to 的属性值为一个对象时,name 属性与 path 属性只需要写一个即可

<!--以下写法是错的-->
<router-link :to="{name:'home',path:'/'}" active-class="active"
  >Home</router-link
>

<!--以下两种写法是对的-->
<router-link :to="{name:'home'}" active-class="active">Home</router-link> |
<router-link :to="{path:'/'}" active-class="active">Home</router-link> |

注:

当我们的路由名很长很复杂时,在<router-link>标签中可以通过name属性来简化,而不需要书写path属性

# 9、路由重定向

TIP

当我们访问某个路径时,我们希望他能重定向到其它的路径,比如访问/news时,希望重定向到到/news/tab1

我们就可以在路由的配置中添加redirect属性来实现路由的重定向。

  • 重定向的目标可以是一个字符串路径
{
        name: 'news',
        path: "/news",
        component: News,
        redirect: "/news/tab1",  // 重定向到 /news/tab1
        children: [
            {
                name: 'tab1',
                path: "tab1",
                component: Tab1,
            },
           // ....此处省略部分代码
        ]
    }
  • 重定向的目标可以是一个命名的路由 (采用路由对象写法)
{
        name: 'news',
        path: "/news",
        component: News,
        redirect: { name: 'tab1' },  // 重定向到路由命名name所在的路径
        children: [
            {
                name: 'tab1',
                path: "tab1",
                component: Tab1,
            },
           // ....此处省略部分代码
        ]
    }
  • 重定向的目标也可以是一个方法,该方法的返回值可以是前面提到的 ”字符串路径“”路由对象“ 中的一种
{
        name: 'news',
        path: "/news",
        component: News,
       redirect: (to) => {
 // to为目标路由对象,此处为/news路由对象
//  {fullPath: '/news', hash: '', query: {…}, name: 'news', path: '/news', …}
            console.log(to)
            return { path: '/news/tab1' }
        }
        children: [
            {
                name: 'tab1',
                path: "tab1",
                component: Tab1,
            },
           // ....此处省略部分代码
        ]
    }

以上三种方式,最终都能实现,当我们访问/news路由时,会重定向到/new/tab1路由,具体效果如下

GIF2023-8-219-24-55

# 9.1、注意事项

TIP

如果重定向的路由没有子路由,则该路由中的component可以省略不写,因为它从来没有被直接访问过

const routes = [
    {
        path:"/"
        redirect:"/home"
        // 此处可以没有component
    },
    {
        name: 'home', // 名字自定义,不一定是home
        path: "/home", // 路径
        component: Home,
    }
]

# 二、vue Router 路由传参

TIP

在路由进行跳转时,我们可以通过传参的形式为当前路由携带相关数据。

路由传参有以下两种形式:

  • query 参数
  • params 参数

# 1、路由的 query 参数

TIP

query 参数最终会以 key = value 键值对的形式出现在地址 后面。

image-20230531001454243

我们来看下面这个路由

const routes = [
  {
    name: "user",
    path: "/user",
    component: User,
  },
  // ....
];

我们在定义路由导航时,就可以通过以下两种方式来传递 query 参数

  • query 参数的两种写法

直接在址后面以key=value形式携带传递的数据

<!--id=001 为传递的参数数据-->
<router-link to="/user?id=001">用户中心</router-link>

to属性的值定义为一个路由对象,通过query字段来传递参数

<router-link
  :to="{
                  path: '/user',
                  query: {
                	  id: '001',
                  	   title:'abc'
                  }
                  }"
  >用户中心</router-link
>

<!-- 也可以采用如下写法 -->
<router-link
  :to="{
                  name: 'user',
                  query: {
                 	 id: '001'
                  }
                  }"
  >用户中心</router-link
>
  • 接受 query 参数

User.vue组件中,接受传递的 query 参数,代码如下:

<script setup>
  import { useRoute } from "vue-router";
  // 当前路由对象
  const route = useRoute();
  // 读取query参数
  console.log(route.query.id); // 001
</script>
<template>
  <div>用户中心</div>
  <!--$route.query.id 读取传递的id,值为 001-->
  <div>query参数:{{ $route.query.id }}</div>
  <div>query参数:{{ route.query.id }}</div>
</template>

最终User.vue组件渲染后效果如下:

image-20230803130053537

# 1.1、实战应用:根据 query 参数渲染数据

TIP

  • 当我们点击不同的新闻标题时,需要把当前新闻的id作为参数传递给到Detail组件
  • Detail组件接受传递过来的id值,向后端发请求获取当前 Id 对应的新闻详细内容,然后渲染在页面中

GIF2023-8-314-00-51

实现步骤

  • 在前面项目的基础上,在views/news目录下新建Detail组件
  • routes配置中,/news/detail路由定义为/news的子路由,因为/news/detail路由对应的组件最终被渲染后显示在News组件中
const routes = [
  // ...省略部分代码
    {
        name: 'news',
        path: "/news",
        component: News,
        redirect: { name: 'tab1' },  // 重定向到路由命名name所在的路径
        children: [
            // 因为该组件内容显示在  News组件中,所以定义为他的子路由
            {
                name: "detail",
                path: 'detail',
                component: Detail

            },
            {
                name: 'tab1',
                path: "tab1",
                component: Tab1,
            }
            // ....省略部分代码

        ]
    }
  • 修改组件 Tab1 的内容如下
<script setup>
  import { reactive } from "vue";
  const list = reactive([
    {
      id: "001",
      title: "新闻标题1111",
    },
    {
      id: "002",
      title: "新闻标题2222",
    },
    {
      id: "003",
      title: "新闻标题3333",
    },
  ]);
</script>

<template>
  <div>最新动态内容</div>
  <ul>
    <li v-for="{ id, title } in list">
      <!--  方式一
            <RouterLink :to="`/news/detail?id=${id}`">{{ title }}</RouterLink> 
			-->
      <!--以下把path去掉,改用name="detail" 也可以-->
      <RouterLink
        :to="{
                path: '/news/detail',
                query: {
                    id: `${id}`
                }
            }"
        >{{ title }}</RouterLink
      >
    </li>
  </ul>
</template>

<style scoped>
  ul li {
    line-height: 35px;
    border-bottom: 1px dashed #ddd;
  }

  ul li a {
    font-size: 16px;
    color: #666;
  }
</style>
  • Detail组件中接受传递的id参数,并根据 id 查找满足条件的新闻,将新闻标题和内容显示在页面中
<script setup>
  import { ref } from "vue";
  import axios from "axios";
  import { useRoute } from "vue-router";
  // 获取当前路由
  const route = useRoute();
  // 接受传递的参数id
  const id = route.query.id;
  // 保存请求回来的新闻详情
  const info = ref({});
  // 根据id来发请求,获取当前新闻的详细内容
  axios
    .get(
      `https://www.fastmock.site/mock/6ec78e345df340241e1f5043f0167833/icode/detail/${id}`
    )
    .then((res) => {
      info.value = res.data.data;
    })
    .catch((err) => {
      console.log(err);
    });
</script>

<template>
  <h3>{{ info.title }}</h3>
  <div class="main">{{ info.content }}</div>
</template>

# 2、路由的 params 参数

TIP

params形式传参,需要在配置路由时,以:key形式先占位。

如下:

const routes=[
    // ....
            {
                name:'detail',
                 // 能匹配detail/12  detail/ab    不能匹配detail/12/b
                path:'detail/:id',
                component:Detail
            },
            {
			   path:'add/:id/:typeid
                // path:'add/:id/a/:typeid'
                component:Add'
            }
            // ......
        ]
    }
    // ....
]

<router-link>组件中,to 属性的值可以写成一个路由对象,params属性表示传递的参数。

注意:

以 params 形式传参时,只能通过name属性来指定要跳转的链接,不能用path属性。

<RouterLink
  :to="{
                name: 'detail',
                params: {
                    id: '001'
                }
            }"
  >{{ title }}</RouterLink
>

<!--以下为错误写法,params只能与name属性配合使用 -->
<RouterLink
  :to="{
                path: '/news/detail/',
                params: {
                    id: '001'
                }
                  }"
  >{{ title }}</RouterLink
>

在组件中可以通过params.id形式来访问到传递的 params 参数 id 等。

<script setup>
  import { useRouter, useRoute } from "vue-router";
  console.log("参数id:", useRoute().params.id);
</script>

<template>
  <div>params参数:{{ $route.params.id }}</div>
</template>

# 2.1、实战应用:根据 params 参数渲染数据

TIP

针以前面的query参数案例,我们只需做以下相关修改,就可以实现与之相同的效果

  • 修改路由配置,在路由中添加:id占位符
const routes = [
  // .....
  {
    name: "news",
    path: "/news",
    component: News,
    redirect: { name: "tab1" }, // 重定向到路由命名name所在的路径
    children: [
      {
        name: "detail",
        path: "detail/:id",
        component: Detail,
      },
      // ....
    ],
  },
];
  • 修改Tab1.vue<route-link>组件内容如下: (把 query 改成了 params)
<RouterLink
  :to="{
                name: 'detail',
                params: {
                    id: `${id}`
                }
            }"
  >{{ title }}</RouterLink
>
  • Detail.vue组件中的route.query.id 修改成route.params.id即可
// 接受传递的参数id
const id = route.params.id;

# 3、路由的 props 配置

TIP

路由的 props 配置可以让路由组件以 props 的形式来接受传递的 params 或 query 参数,写法更简洁。

在没有设置props属性前,在路由组件中只能以如下方式接受params参数

<script setup>
  import { useRouter, useRoute } from "vue-router";
  console.log("参数id:", useRoute().params.id);
</script>

<template>
  <div>参数:{{ $route.params.id }}</div>
</template>

有了 props 配置后,可以以 props 形式来接受传递的参数,如下写法更简洁

<script setup>
  // 这里的属性名,必需要与路由组件的参数名相同
  const props = defineProps(["id"]);
  console.log("id参数", props.id);
</script>

<template>
  <div>参数:{{ props.id }}</div>
</template>

# 3.1、props 布尔模式

TIP

当 props 的值为布尔值 true 时,相当于route.params将被设置为组件的props

即:把所有 params 参数作为 props 传递给路由组件。

// 定义一些路由
const routes = [
  // ...
  {
    name: "detail",
    path: "detail/:id",
    component: Detail,
    // route.params将设置为组件的props
    // 相当于在组件中 <Detail v-bind:=route.params />
    props: true,
  },
];

我们可以在Deatil组件中以 props 的形式来接受传递的params参数

<script setup>
  import { ref } from "vue";
  import axios from "axios";

  // 接受params形式传递的参数 ----------------------
  const props = defineProps(["id"]);
  const id = props.id;

  const info = ref({});

  // 根据id来发请求,获取当前新闻的详细内容
  axios
    .get(
      `https://www.fastmock.site/mock/6ec78e345df340241e1f5043f0167833/icode/detail/${id}`
    )
    .then((res) => {
      info.value = res.data.data;
    })
    .catch((err) => {
      console.log(err);
    });
</script>

<template>
  <h3>{{ info.title }}</h3>
  <div class="main">{{ info.content }}</div>
</template>

# 3.2、props 对象模式

TIP

props是一个对象时,这个对象会被设置为组件的 props。

// 定义一些路由
const routes = [
  // ...
  {
    name: "detail",
    path: "detail/:id",
    component: Detail,
    // route.params将设置为组件的props
    // 相当于在组件中 <Detail v-bind:={a:1,b:2} />
    props: { a: 1, b: 2 },
  },
];

我们可以在路由组件中以props的形式来接受 props 配置传递的数据

<script setup>
  const props = defineProps(["a", "b"]);
  console.log("参数a", props.a); // 1
  console.log("参数b", props.b); // 2
</script>

<template>
  <div>关于我们</div>
  <div>参数a:{{ props.a }}</div>
  <div>参数b:{{ props.b }}</div>
</template>

# 3.3、props 函数模式

TIP

当 props 的值是一个函数时,允许返回值是一个 props 对象,函数的第一个参数为当前route路由对象。

我们可以利用 props 的函数模式,实现以下功能:

  • 将传递的query参数作为 props 传递给路由组件
  • 将传递的query参数与静态值一起作为 props 传递给路由组件
  • 将传递的params参数与静态值一起作为 props 传递给路由组件

将传递的query参数作为 props 传递给路由组件

  • 定义路由导航
<router-link to="/about?id=001">关于我们</router-link>
  • 定义路由
const routes = [
  // .....
  {
    name: "about",
    path: "/about",
    component: About,
    // 写法一 将query参数作为props传递
    props: (route) => route.query,
  },
];
  • 路由组件中通过 props 接受传递的 query 参数
const props = defineProps(['id');

将传递的query参数与静态值一起作为 props 传递给路由组件

  • 定义路由导航
<router-link to="/about?id=001&title=123">关于我们</router-link>
<!-- 或 -->
<router-link
  :to="{
                  name: 'about',
                  query: {
                      id: '001',
                      title: '123'
                  }
                  }"
  >关于我们</router-link
>
  • 定义路由
const routes = [
  // ....
  {
    name: "about",
    path: "/about",
    component: About,
    alias: "/myabout",
    // 将query参数与静态值组合,一起作为props传递
    props: (route) => ({
      id: route.query.id,
      title: route.query.title,
      msg: "其它数据",
    }),
  },
];
  • 路由组件中通过 props 接受传递的 query 参数
const props = defineProps(["id", "title", "msg"]);

将传递的params参数与静态值一起作为 props 传递给路由组件

  • 定义路由导航
<router-link
  :to="{
                  name: 'about',
                  params: {
                      id: '001',
                      title: '1233'
                  }
                  }"
  >关于我们</router-link
>
  • 定义路由
const routes = [
  // ....
  {
    name: "about",
    path: "/about/:id/:title",
    component: About,
    alias: "/myabout",
    // 写法三:将params参数与静态值组合,一起作为props传递
    props: (route) => ({
      id: route.params.id,
      title: route.params.title,
      msg: "其它数据",
    }),
  },
];
  • 路由组件中通过 props 接受传递的 query 参数
const props = defineProps(["id", "title", "msg"]);

# 4、响应路由参数的变化

TIP

我们来看下面这个带参的路由

const routes = [
  {
    name: "news", // 路由名
    path: "/news/:id", // 路径
    component: News, // 路径需要渲染的组件
  },
];

当用户从/news/1001导航到/news/1002/news/1003时,相同的组件实例将被重复使用

因为 3 个路由都渲染同个News组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会被调用,所以当路由的参数发生变化时,在<script setup>中并不能获取到变化后的参数。

<script setup>
  import { useRoute } from "vue-router";
  const route = useRoute();
  const id = route.params.id;
  const url = "http://wwww.xxx.com/...."; // 请求数据源
  // 只能在刚进到/news/1001时才能拿到id=1001。
  // 后面从从/news/1001导航到/news/1002或/news/1003时,拿不到变化后的id

  axios.get(`${url}${id}`).then((res) => {
    // 发请求拿数据
  });
</script>

要对同一个组件中参数的变化做出响应的话,你可以简单地 watch route 对象上的任意属性

<script setup>
  import { useRoute } from "vue-router";
  const route = useRoute();
  const url = "http://wwww.xxx.com/...."; // 请求数据源

  // 侦听 route.params的变化
  watch(
    () => route.params,
    (newValue, oldValue, onCleanup) => {
      // 对路由的变化做出响应
      axios.get(`${url}${newValue.id}`).then((res) => {
        // 发请求拿数据
      });
    }
  );
</script>

# 4.1、实战应用

TIP

我们来实现如下效果,当用户从/news/1001导航到/news/1002/news/1003时都能拿到变化后的参数,然后根据参数的值来发请求获取新闻数据列表。

GIF2023-8-320-06-15

  • src/router/router.js 创建路由实例
// 1、从vue-router中解构两个方法
import {
  createRouter,
  createWebHashHistory,
  createWebHistory,
} from "vue-router";
import News from "../views/News.vue";

// 定义一些路由:路径与组件的映射关系....
const routes = [
  {
    name: "home",
    path: "/",
    redirect: "/news/1001",
  },
  {
    name: "news",
    path: "/news/:id",
    component: News,
  },
];

// 创建router 路由实例
const router = createRouter({
  // 路由模式
  //  history:createWebHashHistory(),  // hash模式的路由
  history: createWebHistory(),
  // 配置一些路由
  // routes:routes
  routes,
});

// 对外暴露
export { router };
  • App.vue中,创建路由导航
<template>
  <ul class="router-link">
    <li>
      <router-link to="/news/1001">最新动态</router-link>
    </li>
    <li>
      <router-link to="/news/1002">执门推荐</router-link>
    </li>
    <li>
      <router-link to="/news/1003">历史动态</router-link>
    </li>
  </ul>

  <!-- url对应组件内容输出口 -->
  <div class="router-view">
    <router-view></router-view>
  </div>
</template>
<style>
  .router-link {
    display: flex;
    justify-content: center;
    text-align: center;
    list-style: none;
    margin-top: 30px;
  }

  .router-link li {
    margin: 0 10px;
  }

  .router-link li a {
    color: #000;
    font-size: 16px;
    text-decoration: none;
  }

  .router-link li a:hover {
    color: tomato;
  }

  .router-link li a.router-link-active {
    color: tomato;
  }

  .router-view {
    width: 80%;
    margin: 30px auto;
    font-size: 20px;
    min-height: 300px;
    background-color: rgb(251, 249, 249);
  }
</style>
<script setup>
  import { ref, watch } from "vue";
  import axios from "axios";
  import { useRoute } from "vue-router";
  const route = useRoute();
  const list = ref([]);
  const url =
    "https://www.fastmock.site/mock/6ec78e345df340241e1f5043f0167833/icode/new/list/";

  // 用来取消请求的对象
  let controller = null;

  // 侦听器侦听 route.params 的变化
  watch(
    () => route.params,
    (newValue, oldValue, onCleanup) => {
      controller = new AbortController();
      // 发请求,拿数据
      axios
        .get(`${url}${route.params.id}`, {
          // 配置取消请求
          signal: controller.signal,
        })
        .then((res) => {
          list.value = res.data.data;
        })
        .catch((err) => {
          console.log(err.message);
        });

      // 取消请求
      function cancle() {
        controller.abort();
      }

      // 取消之前的请求
      onCleanup(cancle);
    },
    {
      immediate: true,
    }
  );
</script>

<template>
  <ul>
    <li v-for="{ id, title } in list">
      <router-link to=""> {{ title }}</router-link>
    </li>
  </ul>
</template>

<style scoped>
  ul {
    padding: 20px;
  }

  ul li {
    line-height: 35px;
    border-bottom: 1px dashed #ddd;
    text-indent: 2em;
  }

  ul li a {
    color: #333;
    text-decoration: none;
  }
</style>

# 5、总结

TIP

本小节重点掌握路由传参和路由的 props 配置

路由传参两种方式:路由传递参数有 query 传参params 传参 两种方式。

具体如下:

传参方式 特点与用法
query 参数 query 传递显示参数,在地址栏中明确知道那部分是参数
query 参数在<script setup>中通过useRoute().query.id形式获取,在模板中以$route.query.id形式获取
params 参数 parmas 传递,不会显示参数,所以 params 传参相比 query 传参更安全一些
params 参数在<script setup>中通过useRoute().params.id形式获取,在模板中以$route.params.id形式获取

# 5.1、路由 props 配置

TIP

路由 props 配置的主要作用:

  • 使路由组件能以 props 的形式来接受传递的参数,写法更简洁。
  • 使路由组件可以以 props 形式接受一些静态数据。

路由 props 配置的三种写法,如下:

值的三种写法 应用场景
布尔值 当我们需要把params参数作为 props 传递给路由组件时,props 的值为true
对象写法 当我们需要给路由组件传递一些静态的 props 时,可以采用对象写法
函数写法 当我们需要把路由的query参数params参数与一些静态值一起作为 props 传递给路由组件时,可以采用函数写法

# 5.3、响应路由参数的变化

TIP

当路由带参时,比如/news/:id路由,当用户从/news/1001导航到/news/1002时,相同的组件实例将被重复使用,所以组件的生命周期钩子不会被调用,这样造成没有办法获取到变化后的参数值。

要对同一个组件中参数的变化做出响应的话,你可以简单地 watch $route 对象上的任意属性。

import { useRoute } from "vue-router";
const route = useRoute();
watch(
  () => router.params,
  () => {}
);

# 三、路由别名

TIP

在路由配置时,我们可以通过alias属性为路由取别名。

  • 当路由/about的别名为/myabout时,我们通过地址/myabout访问的是/about路由的内容,但路由地址显示的是/myabout
  • 当路由/news/tab2的别名为/newsInfo时,我们通过地址/newsInfo访问的是/news/tab2路由的内容,但地址显示的是/newsInfo

通过别名,你可以自由地将 UI 结构映射到一个任意的 URL,而不受配置的嵌套结构的限制

# 1、别名是一个绝对或相对路径

TIP

别名可以是一个以/开头的绝对路径,也可以是一个没有/开头的相对路径

const routes = [
  	// 省略部分代码......
    {
        name: 'about',
        path: "/about",
        component: About,
        alias: "/myabout"  // 别名 绝对路径,访问地址 /myabout
    },
    {
        name: 'news',
        path: "/news",
        component: News,
        redirect: { name: 'tab1' },
        children: [

                name: 'tab1',
                path: "tab1",
                component: Tab1,
            },
            {
                path: "tab2",
                component: Tab2,
                alias: '/newsInfo',  // 别名 ,绝对路径 访问地址 /newsInfo
            },
            {
                path: "tab3",
                component: Tab3,
                alias:"commonInfo"  // 别名,相对路径 访问地址 /news/commonInfo
            }

        ]
    }
]

接下来,我们可以把页面中访问路由的地址修改为访问别名,如下

<ul class="router-link">
  <li><router-link to="/">网站首页</router-link></li>
  <li><router-link to="/myabout">关于我们</router-link></li>
  <li><router-link to="/newsInfo">热门推荐</router-link></li>
  <li><router-link to="/news/commonInfo">历史动态</router-link></li>
</ul>

最终渲染效果如下:

image-20230803204324259

image-20230803204421894

image-20230803204444342

# 2、别名是一个数组

TIP

别名可以由多个以相对或绝对路径组成的数组

路由/news/tab3的别名是一个数组,如下:

const routes = [
  // ......
  {
    name: "news",
    path: "/news",
    component: News,
    redirect: { name: "tab1" },
    children: [
      // ......
      {
        path: "tab3",
        component: Tab3,
        // /Info  /news/commonInfo   最终访问的都是 /news/tab3路由的内容
        alias: ["/Info", "commonInfo"],
      },
    ],
  },
];

我们通过/Info /news/commonInfo 最终访问的都是/news/tab3路由的内容

# 3、别名携带参数

TIP

如果路由有参数,一定要在任何绝对别名中包含它们

const routes = [
  // .....
  {
    name: "about",
    path: "/about/:id",
    component: About,
    // 可以通过 /about/1001  、 /myabout/1001/   、 /1001  访问到about关于我们页面
    alias: [
      "/myabout/:id", // 绝对路径必须带上参数
      "/:id",
    ],
  },
];

以下导航访问的都是同一个页面关于我们

<li><router-link to="/about/1001">关于我们</router-link></li>
<li><router-link to="/myabout/1001">关于我们</router-link></li>
<li><router-link to="/1001">关于我们</router-link></li>

# 四、命名视图

TIP

本小节我们讲解命名视图和嵌套命名视图

# 1、命名视图

TIP

有时候想同时(同级)展示多个视图,而不是嵌套展示。例如创建一个布局,有 header(头部)、main(主内容)、footer(底部)三个视图,这个时候命名视图就派上用场了。

你可以在界面中拥有多个单独命名视图,而不是只有一个单独的出口,如下:

<router-view name="Header"></router-view>
<router-view></router-view>
<!-- 没有name属性,默认为name='default'  上面写法等同于下面写法-->
<!-- <router-view name="default"></router-view> -->

<router-view name="Footer"></router-view>

如果router-view 没有设置 name 名字,那么默认为name="default"

一个视图使用一个组件渲染,因此对于同个路由,有多个视图就需要多个组件渲染。确保正确使用 components 配置 (带上 s):

const router = createRouter({
  history: createWebHashHistory(),
  routes: [
    {
      path: "/",
      components: {
        default: Main,
        // Header: Header 的缩写
        Header,
        // 它们与 `<router-view>` 上的 `name` 属性匹配
        Footer,
      },
    },
  ],
});

# 2、命名视图应用

TIP

我们利用命名视图实现如下图所示的效果

GIF2023-8-322-53-49

  • App.vue
<script setup>
  import Header from "./views/Header.vue";
</script>
<template>
  <header></header>
  <!--多个命名视图, 注意这里的名字采用的是小写-->
  <router-view name="header"></router-view>
  <router-view></router-view>
  <router-view name="footer"></router-view>
</template>
  • Header.vue
<template>
  <div class="header">
    <ul class="router-link">
      <li><router-link to="/">网站首页</router-link></li>
      <li><router-link to="/about">关于我们</router-link></li>
      <li><router-link to="/news">新闻中心</router-link></li>
    </ul>
  </div>
</template>
<style scoped>
  .header {
    height: 50px;
  }

  .router-link {
    display: flex;
    justify-content: center;
    text-align: center;
    list-style: none;
  }

  .router-link li {
    line-height: 50px;
    margin: 0 10px;
  }

  .router-link li a {
    color: #000;
    font-size: 16px;
    text-decoration: none;
  }

  .router-link li a:hover {
    color: tomato;
  }

  .router-link li a.router-link-active {
    color: tomato;
  }
</style>
  • router/router.js
import { createRouter, createWebHistory } from "vue-router";

import HomeHeader from "../views/HomeHeader.vue";
import HomeMain from "../views/HomeMain.vue";
import HomeFooter from "../views/HomeFooter.vue";

import AboutHeader from "../views/AboutHeader.vue";
import AboutMain from "../views/AboutMain.vue";

import NewsMain from "../views/NewsMain.vue";

const routes = [
  {
    path: "/",
    // 当前路由对应多个组件
    components: {
      header: HomeHeader, // 对应默认视图
      default: HomeMain, // 对应命名视图 name='main'
      footer: HomeFooter, // 对应命名视图 name='footer'
    },
  },
  {
    path: "/about",
    components: {
      header: AboutHeader, // 对应默认视图
      default: AboutMain,
    },
  },
  {
    path: "/news",
    components: NewsMain,
    // 如果只渲染默认视图, components:{ default:NewHeader }可以简写成
    // component:NewHeader
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export { router };

# 3、嵌套命名视图

TIP

如果上面案例中 /news 路由还存在子路由,子路由对应的页面也可以由多个命名视图来展示

  • NewsMain.vue
<template>
  <div class="router-link">
    <router-link to="/news/tab1">最新动态</router-link>
  </div>
  <div class="router-view">
    <router-view name="Left" class="left"></router-view>
    <router-view name="Right" class="right"></router-view>
  </div>
</template>

<style scoped>
  .router-view {
    display: flex;
    justify-content: space-between;
  }
</style>
  • router/router.js
const routes = [
  {
    path: "/news",
    component: NewsMain,
    children: [
      {
        path: "tab1",
        components: {
          Left: TabLeft1,
          Right: TabRight1,
        },
      },
    ],
  },
];

# 五、动态路由的匹配语法

TIP

本小节我们来学习动态路由的匹配语法,主要内容有:

  • 参数中自定义正则
  • 可重复的参数
  • 可选参数
  • 注意事项
  • 捕获 404 Not Found 路由
  • Sensitive 与 strict 路由配置

# 1、参数中自定义正则

TIP

当我们使用user/:userId这样的动态路由时,:userId为路径参数,他能匹配任意的字符,所以user/12user/ab 都能匹配成功。

如果我们希望:userId只能匹配数字,也就是user/12 能匹配成功,但user/ab会匹配失败。我们可在路径参数 后面的括号中加入正则表达式来限定

const routes = [
  {
    // 只能匹配 /user/1  /user/123  等 但不能匹配 /user/ab
    path: "/user/:userId(\\d+)",
  },
];

注意:

字符中的\反斜杆有特殊用途,所以要加上\\来转义,确保能正确匹配\d

如果我们希望路由/user/:userId后面的:userId只能匹配数字或字母类字符,我们可以在路径参数后面的括号加入正则表达式来限定

const routes = [
  {
    // 只能匹配 /user/1  /user/123  等 但不能匹配 /user/ab
    path: "/user/:userId([a-zA-Z0-9]+)",
  },
];

# 2、可重复的参数

TIP

如果你需要匹配具有多个部分的路由,如/user/:urls 可以匹配 /user/one /user/one/two/user/one/two/123

则可以在路径参数后面使用 *(0 个或多个)和 +(1 个或多个)将参数标记为可重复

const routes = [
  {
    // 可以匹配 /user/one   /user/one/two    /user/one/two/123
    // 不能匹配 /user/
    path: "/user/:userId+",
  },
  {
    // 可以匹配 /product/one  /product/one/two  /product/one/two/123
    // 也能匹配 /user/
    path: "/product/:productId*",
  },
  {
    // 可以匹配 /news/12   /news/12/23   不能匹配 /news/a   /news/12/b
    // 只要重复的路径中包含非数字,都不能匹配成功
    path: "/news/:id(\\d+)+",
  },
];

# 3、可选参数

TIP

你可以在路径参数后面使用 ? 修饰符(0 个或 1 个)将一个参数标记为可选。

const routes = [
  {
    // 可以匹配 /user   /user/ab   /user/12 等
    path: "/user/:userId?",
  },
  {
    // 可以匹配 /news  /news/12   不能匹配 /user/ab
    path: "/news/:id(\\d+)?",
  },
];

总结

  • + 表示 1 个或多个,同时参数可以重复
  • * 表示 0 个或多个,同时参数可以重复
  • ? 表示 0 个或 1 个,参数不能重复

# 4、注意事项

我们来看下面两个路由

const routes = [
  {
    path: "/:keyword",
  },
  {
    path: "/:id(\\d+)",
  },
];

注:

当我们访问/12时,或/反斜杆后面跟着数字时,将匹配/:id(\\d+),其它情况将会匹配/:keyword

/:id(\\d+)/:keyword在 routes 数组中的顺序没有关系。

# 5、捕获 404 Not Found 路由

TIP

我们可以在定义routes时,配置一个匹配所有路由的路由对象,该路由所渲染的组件为NotFound显示 404 页面内容。

如果当前访问的路径没有被/:pathMatch(.*)之外的路由匹配,则就会被/:pathMatch(.*)匹配,显示NotFound组件

const routes = [
  // .......
  {
    name: "NotFound",
    // 匹配任意路由
    path: "/:pathMatch(.*)*",
    component: NotFound,
  },
  // .......
];

# 6、Sensitive 与 strict 路由配置

TIP

默认情况下路由/users将匹配 /users/users/、甚至 /Users/。我们可以在createRouter(options)的 options 中来配置sensitivestrict选项来区分大小写要禁用尾部斜线。

  • sensitive :设置路由是否区分大小写,true 表示区分大小写,false 表示不区分,默认值为 fasle
  • strict :路由是否禁止尾部斜线 true 表示禁用,false 表示不禁用,默认值为 false
const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: "/about",
      component: About,
    },
    {
      path: "/news",
      component: News,
    },
  ],
  strict: true, // 禁止尾部斜线
  sensitive: true, // 区分大小写
});

以上/about路由只能与/about匹配,与/About/about/都不会匹配成功。/news路由只能与/news匹配,与/News/news/都不会匹配成功。

sensitive 与 strict 选项即可以如上面一样应用在整个全局路由上,又可以应用于当前路由上,如下:

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: "/about",
      component: About,
      strict: true, // 禁止尾部斜线
    },
    {
      path: "/news",
      component: News,
      sensitive: true, // 区分大小写
    },
  ],
});

注:

  • 以上/about路由可以与/about/About匹配成功,但与/about/匹配失败
  • 以上/news路由可以与/news/news/匹配成功,但与/News匹配失败

# 六、vue Router 编程式导航

TIP

除了使用 <router-link> 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。

  • 在组件中插入<router-link>组件来创建导航,属于声明式写法
  • 通过router实例方法来创建导航,属于编程式导航写法。
声明式 编程式
<router-link :to="..."> router.push(...)

以下表格中列出的 router 实例方法,可以用来实现页面导航

router 实例方法 功能
push() 用来导航到一个新的 URL
replace() 用来导航到一个新的 URL,但会替换掉之前的 URL,无法后退到之前 URL
go() 导航到指定记录,用于前进或后退
back() 相当于后退按扭,后退到前一页
forward() 相当于前进按扭,前进到前一页

# 1、push 方法

TIP

push 方法用来导航到一个新的 URL,他接受一个参数,该参数与<router-link>组件中 to 属性值的写法一模一样,可以是一个字符串路径,或者一个描述地址对象。

// 字符串路径
router.push("/news/tab1");

// 带有路径的对象
router.push({ path: "/news/tab1" });

// 命名的路由,并加上参数,让路由建立 url
router.push({ name: "tab1", params: { id: "001" } });

// 带query查询参数,结果是 /news/tab1?id=001
router.push({ path: "/news/tab1", query: { id: "001" } });

// 带 hash,结果是 /about#team
router.push({ path: "/about", hash: "#team" });

注意事项

params参数只能与name属性配合,不能与path属性配合,以下写法 params 会被忽略

// 错误写法
router.push({ path: "/news/tab1", params: { id: "001" } });

# 2、实战应用

TIP

接下来,我们利用编程式导航实现下图所示的导航切换

GIF2023-5-3122-01-26

  • src目录下新建views目录,在此目录所需要的路由组件
views
   ├─ Home.vue    // 首页组件
   ├─ Login.vue   // 登录组件
   ├─ Register.vue // 注册组件
  • 各个路由组件内容如下
<!--Home.vue-->
<template>
  <h3>网站首页</h3>
</template>

<!--Login.vue-->
<template>
  <h3>用户登录</h3>
</template>

<!--Register.vue-->
<template>
  <h3>用户注册</h3>
</template>
  • src目录下新建router目录,在此目录下新建index.js文件,用来定义路由
import { createRouter, createWebHistory } from "vue-router";
import Home from "../views/Home.vue";
import Login from "../views/Login.vue";
import Register from "../views/Register.vue";
import User from "../views/User.vue";

// 定义一些路由
const routes = [
  {
    name: "home",
    path: "/",
    component: Home,
  },
  {
    name: "login",
    path: "/login",
    component: Login,
  },
  {
    name: "register",
    path: "/register",
    component: Register,
  },
];

// 创建路由实例
const router = createRouter({
  history: createWebHistory(),
  routes,
});

// 对外暴露
export { router };
  • App.vue 组件
<script setup>
  import { useRouter } from "vue-router";
  // 获取router实例
  const router = useRouter();
  // 切换URL方法
  function goTo(url) {
    router.push(url); // 切换到对应URL
  }
</script>
<template>
  <button @click="goTo('/login')">登录</button> |
  <button @click="goTo('/register')">注册</button>

  <div class="main">
    <router-view></router-view>
  </div>
</template>

<style>
  .main {
    margin-top: 50px;
    display: flex;
    justify-content: center;
    align-items: center;
  }
</style>

# 3、replace 方法

TIP

与 push 方法一样用来导航到一个新的 URL,但会替换掉之前的 URL,无法后退到之前 URL

声明式 编程式
<router-link :to="..." replace> router.replace(...)
router.replace({ path: "/home" });

如果把上面案例中的 push 方法,换成 replace 方法,发现浏览器顶部左侧的前进后退按扭一直是灰色的。

function goTo(url) {
  router.replace(url); // 切换到对应URL
}

# 4、go、back、forward 方法

router.go(1); // 向前移动一页,相当于前进按扭
router.back(); // 返回前一页 ,相当于后退按扭
router.forward(); // 与go(1) 是一样的

把上面案例App.vue中代码更改成如下:

<script setup>
  import { useRouter } from "vue-router";
  const router = useRouter();
  function goTo(url) {
    router.push(url);
  }
  function go() {
    router.go(1);
  }
  function back() {
    router.back();
  }
</script>
<template>
  <button @click="goTo('/login')">登录</button> |
  <button @click="goTo('/register')">注册</button> |

  <button @click="go">前进</button> |
  <button @click="back">后退</button>

  <div class="main">
    <router-view></router-view>
  </div>
</template>

<style>
  .main {
    margin-top: 30px;
    display: flex;
    justify-content: center;
    align-items: center;
  }
</style>

最终渲染效果如下,先点击登录、注册、再点击后退会退到登录页,再点前进按扭,会前进到注册页。

GIF2023-5-3122-08-59

# 5、总结

TIP

你可能已经注意到,router.pushrouter.replacerouter.gowindow.history.pushStatewindow.history.replaceStatewindow.history.go (opens new window) 的翻版,它们确实模仿了 window.history 的 API。

因此,如果你已经熟悉 Browser History APIs (opens new window),在使用 Vue Router 时,操作历史记录就会觉得很熟悉。

值得一提的是,无论在创建路由器实例时传递什么样的 history 配置 (opens new window),Vue Router 的导航方法( pushreplacego )都能始终正常工作。

# 七、实战应用:项目框架搭建

TIP

本小节结合前面学习过的 Vant UI 与 Vue Router 实现一个简单的应用导航框架,具体效果如下。

GIF2023-8-414-50-20

# 1、实现步骤

TIP

  • 路由设计
  • 安装 Vant 组件库
  • 实现页面路由
  • 单页面开发

# 2、路由设计

TIP

首先根据当前应用的需求设计好当前应用的路由。

当前项目只涉及一级导航(路由),各路由关系与路由对应的组件如下表

页面 一级路由 路由组件
首页 / Home
购物车 /mycart MyCart
好看 /nice Nice
我的 /my My

src/router/index.js中创建 router 实例,并对外暴露

import { createApp } from "vue";
import { createRouter, createWebHistory } from "vue-router";
import Home from "../views/Home.vue";
import MyCart from "../views/MyCart.vue";
import My from "../views/My.vue";
import Nice from "../views/Nice.vue";
const routes = [
  {
    path: "/",
    component: Home,
  },
  {
    path: "/mycart",
    component: MyCart,
  },
  {
    path: "/nice",
    component: Nice,
  },
  {
    path: "/my",
    component: My,
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export { router };

src/main.js中注册 router 实例

// .....此处省略部分代码
import { router } from "./router/";
// 注册路由
app.use(router);
// ......

# 3、安装 Vant 组件库

TIP

在此项目中,我们会用到 Vant 组件库,所以我们需要先安装好 Vant 组件库

  • 安装 UI 组件库
npm i vant
  • 按需引入组件,则需要安装以下插件
npm i unplugin-vue-components -D
  • 在 vite.config.js 文件中配置好此插件
import vue from "@vitejs/plugin-vue";
import Components from "unplugin-vue-components/vite";
import { VantResolver } from "unplugin-vue-components/resolvers";

export default {
  plugins: [
    vue(),
    Components({
      resolvers: [VantResolver()],
    }),
  ],
};

# 4、实现页面路由

TIP

根据路由实现各页面之间的链接(跳转)关系。

在此项目中,我主要构建当前应用底部的 Tabbar 导航。此导航采用 Vant 组件库中的 Tabbar 组件 (opens new window)实现。

<script setup>
  import { ref } from "vue";
  const active = ref(0);
</script>

<template>
  <router-view></router-view>

  <van-tabbar active-color="#ee0a24" v-model="active">
    <van-tabbar-item icon="home-o" to="/">首页</van-tabbar-item>
    <van-tabbar-item icon="shopping-cart-o" to="/mycart"
      >购物车</van-tabbar-item
    >
    <van-tabbar-item icon="tv-o" to="/nice">好看</van-tabbar-item>
    <van-tabbar-item icon="friends-o" to="/my">我的</van-tabbar-item>
  </van-tabbar>
</template>

最终实现效果如下,点击对应的导航可以显示不同的内容

image-20230804151401327

# 5、单页面开发

TIP

整个应用的页面路由(导航)实现好之后,我们就可以开发单个路由页面的功能了。

在这个应用中,每个页面(除首页)的顶部都有一个返回按扭,可以返回到到前一页。这里利用 Vant UI 的 NavBar 导航栏 (opens new window)组件来实现。

  • MyCart.vue组件内容
<script setup>
  // 返回前一页
  function onClickLeft() {
    history.back();
  }
</script>

<template>
  <!-- 导航栏 -->
  <van-nav-bar
    title="购物车"
    left-text="返回"
    left-arrow
    @click-left="onClickLeft"
  />
  <!--主体内容-->
  <div class="main">购物车.....</div>
</template>
<style scoped>
  .main {
    text-align: center;
  }

  /* 修改导航栏中 < 返回 的颜色为黑色*/
  :global(.van-nav-bar__text) {
    color: #000;
  }

  :global(.van-nav-bar .van-icon) {
    color: #000;
  }
</style>

最终实现 /mycart 路由访问到的效果如下:

image-20230804153919422

注:

其它带有返回导航栏的NiceMy组件的内容也按上面格式书写即可,针对修改导航栏中 < 返回 颜色的样式可以不用再重写。

上次更新时间: 8/14/2023, 2:31:40 PM

大厂最新技术学习分享群

大厂最新技术学习分享群

微信扫一扫进群,获取资料

X