一文带你了解Vue.js的路由🧨🎉

71 阅读4分钟

1.路由简介

Vue 作为一个渐进式框架,在使用 vue-router 之后可以是程序变为一个 SPA 的单页面应用,

它的本质是使用了 js 的 History API:

Vue Router 利用浏览器的 History API(如 pushState replaceState ),实现了无刷新的页面切换效果,同时保持 URL 的正确性。

1.SPA

单页应用程序: SPA【Single Page Application】是指所有的功能都在一个html页面上实现

多页应用: 每个功能都生成一个html页面来实现

单页应用网站: 网易云音乐

智慧园区: fe-hmzs-tj.itheima.net/rod/warnLis…

Hmzs%001

多页应用网站: 京东 jd.com/

单页应用 VS 多页面应用

单页应用类网站:系统类网站 / 内部网站 / 文档类网站 / 移动端站点

多页应用类网站:公司官网 / 电商类网站

单页面应用程序,之所以开发效率高,性能好,用户体验好

最大的原因就是:页面按需更新

要按需更新,首先就需要明确:访问路径组件的对应关系!

访问路径 和 组件的对应关系如何确定呢? 路由

Vue中的路由:路径和组件映射关系

2.VueRouter的使用

Vue中安装VueRouter的基本步骤:4步固定流程(无需记忆)

  1. 下载 VueRouter 模块到当前工程,版本3.6.5
yarn add vue-router@3.6.5 或 npm i vue-router@3.6.5

2. main.js中引入VueRouter 和 安装注册 路由

import VueRouter from 'vue-router'
Vue.use(VueRouter)

3. main.js中创建路由对象,配置路由规则,并且注入到Vue实例中

const router = new VueRouter({
    routes:[
      //{path:'/xxx',component:xxxx},
      //......其他路由
    ]
})

4. main.js中将路由对象注入到new Vue实例中,与Vue建立关联

new Vue({
  render: h => h(App),
  router
}).$mount('#app')

组件存放目录约定:

  1. src/views文件夹页面组件 - 页面展示 - 配合路由用

  2. src/components文件夹复用组件 - 展示数据 - 常用于复用

2.路由的用法

1. 模块抽离

// 承担的职责:封装路由相关的代码

import Vue from 'vue'

// 1. 导入vuerouter 
import VueRouter from 'vue-router'
// 2. Vue使用一下->注册组件 -> 能够去使用<router-view>  <router-link>
Vue.use(VueRouter)

// 3. 实例化VueRouter
import Find from '@/views/Find.vue'
import Friend from '@/views/Friend.vue'
import My from '@/views/My.vue'

const router = new VueRouter({
  routes: [
    { path: '/find', component: Find },
    { path: '/my', component: My },
    { path: '/part', component: Friend },
  ]
})


// 4. 将router对象导出(es6模块标准)
export default router
import router from './router'

new Vue({
  // 5. 关联路由对象和vue实例 data methods
  render: (h) => h(App),
  router,
}).$mount('#app')

2.声明式导航

定义: 使用 router-link方式进行的导航,叫做声明式导航

作用: router-link最终会生成一个a标签,用户点击即可跳转。

1. 导航高亮

router-link会自动给当前导航添加两个类名

<template>
  <div>
   <!-- 
    router-link会自动给当前的链接添加两个类名
    router-link-active: 激活的导航链接   模糊匹配
    router-link-exact-active:  激活的导航链接 精确匹配
    -->
  <router-link to="/find">发现音乐</router-link>
  <router-link to="/my">我的音乐</router-link>
  <router-link to="/part">朋友</router-link>
   
    <div class="top">
      <router-view></router-view>
    </div>
     
  </div>
</template>

<style scoped>
.router-link-exact-active {
  color: red;
}

.router-link-active {
  color: red;
}
</style>

可以修改默认高亮的类名

const router = new VueRouter({
  linkActiveClass: 'act',
  linkExactActiveClass: 'exact',
  // route: 一条规则
}

2.路由传参

目标: 在跳转路由时, 可以给路由对应的组件内传值

在router-link上的to属性传值, 语法格式如下

  • 查询参数传参(query): /path?参数名=值
  • 动态路由传参(params): /path/参数名”
查询参数传参

① 导航链接中传入参数: to="/path?参数名=值"

② 对应页面组件接收传递过来的值: $route.query.参数名

注意:$route在template中无需加this,在script中需要加this

动态路由传参

① 配置动态路由 -> router/index.js

② 配置导航链接to="/path/参数值"

③ 对应页面组件接收传递过来的值$route.params.参数名

  1. 创建views/Search.vue - 准备接收路由上传递的参数和值
<template>
  <div>
      <p>关键字: {{ $route.params.words }}</p>
  </div>

</template>

2. 路由定义

{
    path: "/search/:keywords",
    component: Search
}

3. App.vue中导航跳转, 传值给Seach.vue组件

<router-link to="/search/黑马程序员">黑马程序员</router-link>

两种传参方式的区别:

3.编程式导航

定义: 用$router.push()等 跳转的方式,我们叫做编程式导航

作用: 一般用在方法中通过 编写 $router.push()代码 来完成跳转

语法: query / params 任选 一个
query传参方式:


params传参方式:

this.$router.push({
    name: "路由名",
  // query传参 -> 对应路由接收   $route.query.参数名    取值
    query: {
        "参数名": 值
    }
      
  // params传参 ->对应路由接收   $route.params.参数名   取值
    params: {
        "参数名": 值
    }
})

4.路由的配置

1.重定向

匹配path后, 强制切换到目标path上

作用: 一般用在匹配上/后,强制切换到 其他path上

例如: 网页默认打开, 匹配路由"/", 强制切换到"/find"上

const routes = [
  {
    path: "/", // 默认hash值路径
    redirect: "/find" // 重定向到/find
    // 浏览器url中#后的路径被改变成/find-重新匹配数组规则
  }
]

2.404

作用:如果路由hash值, 没有和数组里规则匹配,则给一个默认的404页面

3.路由模式

目标: 修改路由在地址栏的模式

hash路由例如: http://localhost:8080/#/home

history路由例如: http://localhost:8080/home (以后上线需要服务器端支持)

const router = new VueRouter({
  routes,
  mode: "history" // 打包上线后需要后台支持, 模式是hash
})

4.组件缓存(keep-alive)

就是想不销毁原有组件,缓存组件原有的状态

基本语法
思考:从面经 点到 详情页,又点返回,希望回到回来的位置!但是数据重新加载了 => 因为组件被销毁重建了。
如果希望组件被缓存下来,可以在外面包一个 keep-alive 组件

<template>
  <div class="h5-wrapper">
    <keep-alive>
      <router-view></router-view>
    </keep-alive>
  </div>
</template>
keep-alive对应的两个钩子

当组件被keep-alive管理时,会多出两个生命周期钩子,activated / deactivated

export default {
  ...
  activated() {
    console.log('缓存组件被激活')
  },
  deactivated() {
    console.log('缓存组件被隐藏')
  }
};

3. 案例

路由案例.rar

<!--
 * @Date: 2025-01-13 16:24:37
 * @LastEditors: zl 1077167261@qq.com
 * @LastEditTime: 2025-01-13 18:12:10
 * @FilePath: \app_demo\src\App.vue
-->
<template>
  <div class="h5-wrapper">
    <keep-alive include="LayoutPage">
      <router-view></router-view>
    </keep-alive>
  </div>
</template>

<script>
export default {
  name: "AppVue",
};
</script>

<style>
body {
  margin: 0;
  padding: 0;
}
</style>
<style lang="less" scoped>
.h5-wrapper {
  .content {
    margin-bottom: 51px;
  }
  .tabbar {
    position: fixed;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 50px;
    line-height: 50px;
    text-align: center;
    display: flex;
    background: #fff;
    border-top: 1px solid #e4e4e4;
    a {
      flex: 1;
      text-decoration: none;
      font-size: 14px;
      color: #333;
      -webkit-tap-highlight-color: transparent;
      &.router-link-active {
        color: #fa0;
      }
    }
  }
}
</style>
<template>
  <div class="h5-wrapper">
    <div class="content">
      <!-- 二级路由出口, -->
      <router-view></router-view>
    </div>
    <nav class="tabbar">
      <!-- 导航高亮
           1. 将a标签,替换成router-link (to)
           2. 结合高亮类名实现高亮效果 (router-link-active 模糊匹配)
      -->
      <router-link to="/article">面经</router-link>
      <router-link to="/collect">收藏</router-link>
      <router-link to="/like">喜欢</router-link>
      <router-link to="/user">我的</router-link>
    </nav>
  </div>
</template>

<script>
export default {
  name:'LayoutPage'
};
</script>

<style>
body {
  margin: 0;
  padding: 0;
}
</style>
<style lang="less" scoped>
.h5-wrapper {
  .content {
    margin-bottom: 51px;
  }
  .tabbar {
    position: fixed;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 50px;
    line-height: 50px;
    text-align: center;
    display: flex;
    background: #fff;
    border-top: 1px solid #e4e4e4;
    a {
      flex: 1;
      text-decoration: none;
      font-size: 14px;
      color: #333;
      -webkit-tap-highlight-color: transparent;
    }
    a.router-link-active {
      color: orange;
    }
  }
}
</style>
<template>
  <div class="article-detail-page" v-if="article.id">
    <nav class="nav">
      <span class="back" @click="$router.back()">&lt;</span> 面经详情
    </nav>
    <header class="header">
      <h1>{{ article.stem }}</h1>
      <p>
        {{ article.createdAt }} | {{ article.views }} 浏览量 |
        {{ article.likeCount }} 点赞数
      </p>
      <p>
        <img :src="article.creatorAvatar" alt="" />
        <span>{{ article.creatorName }}</span>
      </p>
    </header>
    <main class="body">{{ article.content }}</main>
  </div>
</template>


<script>
// 请求地址: https://mock.boxuegu.com/mock/3083/articles/:id
// 请求方式: get
import axios from "axios";
export default {
  name: "article-detail-page",
  data() {
    return {
      article: {},
    };
  },
  async created() {
    this.article = {};
    const { data } = await axios.get(
      `https://mock.boxuegu.com/mock/3083/articles/${this.$route.params.id}`
    );
    this.article = data.result;
  },
};
</script>

<style lang="less" scoped>
.article-detail-page {
  .nav {
    height: 44px;
    border-bottom: 1px solid #e4e4e4;
    line-height: 44px;
    text-align: center;
    .back {
      font-size: 18px;
      color: #666;
      position: absolute;
      left: 10px;
      top: 0;
      transform: scale(1, 1.5);
    }
  }
  .header {
    padding: 0 15px;
    p {
      color: #999;
      font-size: 12px;
      display: flex;
      align-items: center;
    }
    img {
      width: 40px;
      height: 40px;
      border-radius: 50%;
      overflow: hidden;
    }
  }
  .body {
    padding: 0 15px;
  }
}
</style>
/*
 * @Date: 2025-01-13 16:21:45
 * @LastEditors: zl 1077167261@qq.com
 * @LastEditTime: 2025-01-13 17:12:04
 * @FilePath: \app_demo\src\router\index.js
 */
/*
 * @Date: 2025-01-13 16:21:45
 * @LastEditors: zl 1077167261@qq.com
 * @LastEditTime: 2025-01-13 16:54:00
 * @FilePath: \app_demo\src\router\index.js
 */
import Vue from 'vue';
import VueRouter from 'vue-router';
import LayoutPage from '@/views/LayoutPage.vue';
import ArticleDetailPage from '@/views/ArticleDetailPage.vue';
// 二级路由
import ArticlePage from '@/views/ArticlePage.vue';
import CollectPage from '@/views/CollectPage.vue';
import LikePage from '@/views/LikePage.vue';
import UserPage from '@/views/UserPage.vue';
Vue.use(VueRouter);

const routes = [
  {
    path: '/',
    component: LayoutPage,
    redirect: '/article',
    children: [
      {
        path: '/article',
        component: ArticlePage,
      },
      {
        path: '/collect',
        component: CollectPage,
      },
      {
        path: '/like',
        component: LikePage,
      },
      {
        path: '/user',
        component: UserPage,
      },
    ],
  },
  {
    path: '/detail/:id',
    name: 'detail',
    component: ArticleDetailPage,
  },
];

const router = new VueRouter({
  routes,
});

export default router;

内容-1.gif