Vue2:路由

news2024/12/26 22:35:20

Vue2:路由

Date: May 28, 2023
Sum: vue-router基本使用、高级用法


单页面应用程序

概念:SPA【Single Page Application】是指所有的功能都在一个html页面上实现

案例

单页应用网站: 网易云音乐 https://music.163.com/

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

**单页应用 VS 多页面应用:

Untitled

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

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

特点

单页面应用程序将所有的功能局限于一个 web 页面中,仅在该 web 页面初始化时加载相应的资源( HTML、JavaScript 和 CSS)。

一旦页面加载完成了,SPA 不会因为用户的操作而进行页面的重新加载或跳转。而是利用JavaScript 动态地变换 HTML 的内容,从而实现页面与用户的交互。

注意:SPA它是按需更新的,并不是整体更新。因此,它需要更加关注访问路径与组件的对应关系

优点

SPA 单页面应用程序最显著的 3 个优点如下:

① 良好的交互体验

单页应用的内容的改变不需要重新加载整个页面

获取数据也是通过 Ajax 异步获取

没有页面之间的跳转,不会出现“白屏现象”

② 良好的前后端工作分离模式

后端专注于提供 API 接口,更易实现 API 接口的复用

前端专注于页面的渲染,更利于前端工程化的发展

③ 减轻服务器的压力

服务器只提供数据,不负责页面的合成与逻辑的处理,吞吐能力会提高几倍

缺点

任何一种技术都有自己的局限性,对于 SPA 单页面应用程序来说,主要的缺点有如下两个:

① 首屏加载慢

可以通过路由懒加载、代码压缩、CDN 加速、网络传输压缩处理

② 不利于 SEO

SSR 服务器端渲染

如何快速创建 vue 的 SPA 项目

vue 官方提供了两种快速创建工程化的 SPA 项目的方式:

① 基于 vite 创建 SPA 项目 ② 基于 vue-cli 创建 SPA 项目

Untitled


总结:

概念:一个Web网站中只有唯一一个HTML页面,所有的功能与交互都在这唯一的一个页面内完成。

特点:

1.集中所有功能于单页面中,仅在该页面初始化时加载相应的资源。

2.通过JS动态变换页面内容,从而实现页面与用户的交互

优点:

  1. 良好的交互体验

    没有页面跳转,不会有“白屏”现象

  2. 良好的前后端分离模式

    前端专注页面渲染,更利于前端工程化发展;后端专注提供API接口,更易实现API接口复用

  3. 减轻服务端的压力

    服务器只提供数据,不负责页面的合成与逻辑,吞吐能力提高几倍

缺点:

  1. 首屏加载慢

    可以通过代码压缩、CDN加速、路由懒加载、网络传输压缩处理等

  2. 不利于SEO

    SSR服务器端渲染




前端路由的概念与原理

思考:

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

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

Untitled

比如当点击【发现音乐】和【关注】时,只是更新下面部分内容,对于头部是不更新的

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

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


路由概念:

概念:路由(英文:router)就是对应关系。路由分为两大类: ① 后端路由 ② 前端路由

回顾:后端路由

后端路由指的是:请求方式、请求地址与 function 处理函数之间的对应关系。

在 node.js 课程中,express路由的基本用法如下:

Untitled



前端路由:

SPA 与前端路由

SPA 指的是一个 web 网站只有唯一的一个 HTML 页面,所有组件的展示与切换都在这唯一的一个页面内完成。此时,不同组件之间的切换需要通过前端路由来实现。

结论:在 SPA 项目中,不同功能之间的切换,要依赖于前端路由来完成!

概念:Vue中的路由指 路径 与 组件 的映射关系,根据路由就能知道不同路径,应匹配渲染相应组件

通俗易懂的概念:Hash 地址与组件之间的对应关系。

我们点击不同的链接,会切换不同的哈希地址。当哈希地址切换,页面上就会展示对应组件。

工作方式

① 用户点击了页面上的路由链接
② 导致了 URL 地址栏中的 Hash 值发生了变化
③ 前端路由监听了到 Hash 地址的变化
④ 前端路由把当前 Hash 地址对应的组件渲染都浏览器中

Untitled

结论:前端路由,指的是 Hash 地址与组件之间的对应关系!

实现简易的前端路由:

步骤1:导入并注册 MyHome、MyMovie、MyAbout 三个组件。示例代码如下:

import MyHome from './MyHome.vue'
import MyAbout from './MyAbout.vue'
import MyMovie from './MyMovie.vue'

export default {
  components: {
    MyHome, MyMovie, MyAbout,
  },
}

步骤2:通过 标签的 is 属性,动态切换要显示的组件。示例代码如下:

<template>
  <div>
    <h1>App.vue</h1>
    <component :is="comName">This is a test</component>
  </div>
</template>

export default {
  data() {
    return {
      comName: 'MyName', // 要展示的组件的名称
    }
  },
}

步骤3:在组件的结构中声明如下 3 个 链接,通过点击不同的 链接,切换浏览器地址栏中的 Hash 值:

<a href="#/home">Home</a>&nbsp;
<a href="#/movie">Movie</a>&nbsp;
<a href="#/about">About</a>

步骤4:在 created 生命周期函数中监听浏览器地址栏中 Hash 地址的变化,动态切换要展示的组件的名称:

window.onhashchange = () => {
    switch(location.hash) {
      case '#/home': // 点击了“首页”的链接
        this.comName = 'my-home'
        break
      case '#/movie': // 点击了“电影”的链接
        this.comName = 'my-movie'
        break
      case '#/about': // 点击了“关于”的链接
        this.comName = 'my-about'
        break
    }
  }
},
  • Code: App.vue

    <template>
      <div>
        <h1>App.vue</h1>
    
        <a href="#/home">Home</a>&nbsp;
        <a href="#/movie">Movie</a>&nbsp;
        <a href="#/about">About</a>&nbsp;
        <hr />
    
        <component :is="comName">This is a test</component>
      </div>
    </template>
    
    <script>
    import MyHome from '../01.easy-router/MyHome.vue'
    import MyAbout from '../01.easy-router/MyAbout.vue'
    import MyMovie from '../01.easy-router/MyMovie.vue'
    
    export default {
      name: 'MyApp',
      data() {
        return {
          comName: 'MyName',
        }
      },
      components: {
        MyHome, MyMovie, MyAbout,
      },
      created() {
        window.onhashchange = () => {
          switch(location.hash) {
            case '#/home': // 点击了“首页”的链接
              this.comName = 'my-home'
              break
            case '#/movie': // 点击了“电影”的链接
              this.comName = 'my-movie'
              break
            case '#/about': // 点击了“关于”的链接
              this.comName = 'my-about'
              break
          }
        }
      },
    
    }
    </script>
    
    <style lang="scss" scoped>
    </style>
    



vue-router 的基本使用

基础概念:

概念

vue-router 是 vue.js 官方给出的路由解决方案。它只能结合 vue 项目进行使用,能够轻松的管理 SPA 项目中组件的切换。

作用修改地址栏路径时,切换显示匹配的组件

版本:

vue-router 目前有 3.x 的版本和 4.x 的版本。其中:

vue-router 3.x 只能结合 vue2 进行使用

vue-router 4.x 只能结合 vue3 进行使用

vue-router 3.x 的官方文档地址:https://router.vuejs.org/zh/
vue-router 4.x 的官方文档地址:https://next.router.vuejs.org/

补充官网:https://v3.router.vuejs.org/zh/

说明:Vue 官方的一个路由插件,是一个第三方包



VueRouter 3.x的使用(5+2)

固定5个固定的步骤(不用死背,熟能生巧)

1-下载 VueRouter 模块到当前工程,版本3.6.5

yarn add vue-router@3.6.5
npm install vue-router@3.6.5

2-main.js中引入VueRouter

import VueRouter from 'vue-router'

3-安装注册

Vue.use(VueRouter)

4-创建路由对象

const router = new VueRouter()

5-注入,将路由对象注入到new Vue实例中,建立关联

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

当我们配置完以上5步之后 就可以看到浏览器地址栏中的路由 变成了 /#/的形式。表示项目的路由已经被Vue-Router管理了

Untitled

案例

main.js

// 路由的使用步骤 5 + 2
// 5个基础步骤
// 1. 下载 v3.6.5
// yarn add vue-router@3.6.5
// 2. 引入
// 3. 安装注册 Vue.use(Vue插件)
// 4. 创建路由对象
// 5. 注入到new Vue中,建立关联

import VueRouter from 'vue-router'
Vue.use(VueRouter) // VueRouter插件初始化

const router = new VueRouter()

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

**两个核心步骤:

1-创建需要的组件 (views目录),配置路由规则

Untitled

2-配置导航,配置路由出口(路径匹配的组件显示的位置)

代码中的router-view是用来匹配导航栏的位置的,如果它在代码的上/下面,那么导航栏也在上/下面

如下图所示:

Untitled

App.vue

<div class="footer_wrap">
  <a href="#/find">发现音乐</a>
  <a href="#/my">我的音乐</a>
  <a href="#/friend">朋友</a>
</div>
<div class="top">
  <router-view></router-view>
</div>

案例

Untitled

  • Code:

    main.js

    import Vue from 'vue'
    import App from './App.vue'
    
    Vue.config.productionTip = false
    
    import Find from './views/Find'
    import My from './views/My'
    import Friend from './views/Friend'
    
    import VueRouter from 'vue-router'
    Vue.use(VueRouter)
    const router = new VueRouter({
      // routes 路由规则们
      // route 一条路有规则 { path: 路径, component: 组件}
      routes: [
        { path: '/find', component: Find},
        { path: '/my', component: My},
        { path: '/friend', component: Friend},
      ]
    })
    
    Vue.config.productionTip = false
    
    new Vue({
      render: h => h(App),
      router,
    }).$mount('#app')
    

    App.vue

    <template>
      <div>
        <div class="footer_wrap">
          <a href="#/find">发现音乐</a>
          <a href="#/my">我的音乐</a>
          <a href="#/friend">朋友</a>
        </div>
        <div class="top">
          <!-- 路由出口:匹配的组件所展示的位置 -->
          <router-view></router-view>
        </div>
      </div>
    </template>
    
    <script>
    export default {};
    </script>
    
    <style>
    body {
      margin: 0;
      padding: 0;
    }
    .footer_wrap {
      position: relative;
      left: 0;
      top: 0;
      display: flex;
      width: 100%;
      text-align: center;
      background-color: #333;
      color: #ccc;
    }
    .footer_wrap a {
      flex: 1;
      text-decoration: none;
      padding: 20px 0;
      line-height: 20px;
      background-color: #333;
      color: #ccc;
      border: 1px solid black;
    }
    .footer_wrap a:hover {
      background-color: #555;
    }
    </style>
    


vue-router 4.x的基本使用步骤:

vue-router 4.x 的基本使用步骤

① 在项目中安装 vue-router

② 定义路由组件

③ 声明路由链接和占位符

④ 创建路由模块

⑤ 导入并挂载路由模块

3.1 在项目中安装 vue-router

在 vue3 的项目中,只能安装并使用 vue-router 4.x。安装的命令如下:

npm install vue-router@next -S

注意:包名会被注册在package.json的dependencies里面,在生产环境下这个包的依赖依然存在。

3.2 定义路由组件

在项目中定义 MyHome.vue、MyMovie.vue、MyAbout.vue 三个组件,将来要使用 vue-router 来控制它们的展示与切换:

MyAbout, 其余同理:

<template>
  <h3>MyAbout</h3>
</template>

<script>
export default {
  name: 'MyAbout'
}
</script>

<style>

</style>

3.3 声明路由链接和占位符

可以使用 标签来声明路由链接,并使用 标签来声明路由占位符。

示例代码如下:

<template>
  <div>
    <h1>MyApp</h1>
    <!-- 声明路由链接 -->
    <router-link to="/home">首页</router-link>&nbsp;
    <router-link to="/movie">电影</router-link>&nbsp;
    <router-link to="/about">关于</router-link>
    <hr />

    <!-- 路由的占位符 -->
    <router-view></router-view>
  </div>
</template>

注意:在声明哈希属性时,需要用to属性来声明,而且哈希地址不再需要以#开头,vue中的router会自动为我们添加#。

3.4 创建路由模块

在项目中创建 router.js 路由模块,在其中按照如下 4 个步骤创建并得到路由的实例对象:

① 从 vue-router 中按需导入两个方法
② 导入需要使用路由控制的组件
③ 创建路由实例对象
④ 向外共享路由实例对象
⑤ 在 main.js 中导入并挂载路由模块

从 vue-router 中按需导入两个方法

// 1. 从 vue-router 中按需导入两个方法
// createRouter 方法用于创建路由的实例对象
// createWebHashHistory 用于指定路由的工作模式(hash模式)
import { createRouter, createWebHashHistory } from 'vue-router'

导入需要使用路由控制的组件

//2. 导入组件,这些组件将要以路由的方式,来控制它们的切换
import MyHome from './MyHome.vue'
import MyMoive from './MyMoive.vue'
import MyAbout from './MyAbout.vue'

创建路由实例对象

// 3. 创建路由实例对象
const router = createRouter({
  // 3.1 通过 history 属性指定路由的工作模式
  history: createWebHashHistory(),
  // 3.2 通过 routes 数组,指定路由规则
  routes: [
    // path 是 hash 地址, component 是要展示的组件
    { path: '/home', component: Home },
    { path: '/movie', component: Movie },
    { path: '/about', component: About },
  ]
})

向外共享路由实例对象

// 4. 向外共享路由实例对象,
//   供其他模块导入并使用
export default router

在 main.js 中导入并挂载路由模块

import { createApp } from 'vue'
import App from './components/App.vue'
import './index.css'

// 1. 导入路由模块
import router from './components/02.start/router'
const app = createApp(App)

// 2. 挂载路由模块
// app.use() 方法用来挂载“第三方的插件模块”
app.use(router)

app.mount('#app')

效果:

Untitled



整体流程:

当我们点击电影时,地址栏会切换成 #/movie,当哈希地址发生变化,就会被路由模块所监听到,然后进行哈希地址的匹配:

// router.js
routes: [
    // path 是 hash 地址, component 是要展示的组件
    { path: '/home', component: MyHome },
    { path: '/movie', component: MyMovie },
    { path: '/about', component: MyAbout },
  ]

当匹配到,我们就会把相应的组件比如MyMovie放到App.vue中的路由占位符中

// App.vue
<template>
  <div>
    <h1>MyApp</h1>
    <!-- 声明路由链接 -->
    <router-link to="/home">首页</router-link>
    <router-link to="/movie">电影</router-link>
    <router-link to="/about">关于</router-link>
    <hr />

    <!-- 路由的占位符 -->
    <router-view></router-view>
  </div>
</template>


组件的存放目录问题

注意: .vue文件 本质无区别

组件分类:

.vue文件分为2类,都是 .vue文件(本质无区别)

  • 页面组件 (配置路由规则时使用的组件)
  • 复用组件(多个组件中都使用到的组件)

Untitled

存放目录:

分类开来的目的就是为了 更易维护

  1. src/views

    文件夹页面组件 - 页面展示 - 配合路由用

  2. src/components

    文件夹复用组件 - 展示数据 - 常用于复用



路由的封装抽离

问题:所有的路由配置都在main.js中合适吗?

目标:将路由模块抽离出来。 好处:拆分模块,利于维护

Untitled

路径简写:

脚手架环境下 @指代src目录,可以用于快速引入组件

具体书写:

index.js

import Find from '@/views/Find'
import My from '@/views/My'
import Friend from '@/views/Friend'

// 1. 导入Vue
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)
const router = new VueRouter({
  // routes 路由规则们
  // route 一条路有规则 { path: 路径, component: 组件}
  routes: [
    { path: '/find', component: Find},
    { path: '/my', component: My},
    { path: '/friend', component: Friend},
  ]
})

// 2. 导出
export default router

main.js

import Vue from 'vue'
import App from './App.vue'
// 3. 导入 router
import router from './router/index'

Vue.config.productionTip = false

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

App.vue

<template>
  <div>
    <div class="footer_wrap">
      <a href="#/find">发现音乐</a>
      <a href="#/my">我的音乐</a>
      <a href="#/friend">朋友</a>
    </div>
    <div class="top">
      <!-- 路由出口:匹配的组件所展示的位置 -->
      <router-view></router-view>
    </div>
  </div>
</template>

<script>
export default {};
</script>

<style>
body {
  margin: 0;
  padding: 0;
}
.footer_wrap {
  position: relative;
  left: 0;
  top: 0;
  display: flex;
  width: 100%;
  text-align: center;
  background-color: #333;
  color: #ccc;
}
.footer_wrap a {
  flex: 1;
  text-decoration: none;
  padding: 20px 0;
  line-height: 20px;
  background-color: #333;
  color: #ccc;
  border: 1px solid black;
}
.footer_wrap a:hover {
  background-color: #555;
}
</style>



声明式导航

导航链接

需求:实现导航高亮效果

Untitled

如果使用a标签进行跳转的话,需要给当前跳转的导航加样式,同时要移除上一个a标签的样式,太麻烦!!!

**解决方案:

vue-router 提供了一个全局组件 router-link (取代 a 标签)

  • 跳转,配置 to 属性指定路径(必须) 。本质还是 a 标签 ,to 无需 #
  • 能高亮,默认就会提供高亮类名,可以直接设置高亮样式

Untitled

语法: 发现音乐

<div>
  <div class="footer_wrap">
    <router-link to="/find">发现音乐</router-link>
    <router-link to="/my">我的音乐</router-link>
    <router-link to="/friend">朋友</router-link>
  </div>
  <div class="top">
    <!-- 路由出口 → 匹配的组件所展示的位置 -->
    <router-view></router-view>
  </div>
</div>

通过router-link自带的两个样式进行高亮:

使用router-link跳转后,我们发现。当前点击的链接默认加了两个class的值 router-link-exact-activerouter-link-active

我们可以给任意一个class属性添加高亮样式即可实现功能

代码

  • Code:

    App.vue

    <template>
      <div>
        <div class="footer_wrap">
          <router-link to="/find">发现音乐</router-link>
          <router-link to="friend">我的音乐</router-link>
          <router-link to="my">我的朋友</router-link>
        </div>
        <div class="top">
          <!-- 路由出口 → 匹配的组件所展示的位置 -->
          <router-view></router-view>
        </div>
      </div>
    </template>
    
    <script>
    export default {};
    </script>
    
    <style>
    body {
      margin: 0;
      padding: 0;
    }
    .footer_wrap {
      position: relative;
      left: 0;
      top: 0;
      display: flex;
      width: 100%;
      text-align: center;
      background-color: #333;
      color: #ccc;
    }
    .footer_wrap a {
      flex: 1;
      text-decoration: none;
      padding: 20px 0;
      line-height: 20px;
      background-color: #333;
      color: #ccc;
      border: 1px solid black;
    }
    .footer_wrap a.router-link-active {
      background-color: purple;
    }
    .footer_wrap a:hover {
      background-color: #555;
    }
    </style>
    


两个类名

当我们使用跳转时,自动给当前导航加了两个类名

Untitled

router-link-active

模糊匹配(用的多)

to=“/my” 可以匹配 /my /my/a /my/b …

只要是以/my开头的路径 都可以和 to="/my"匹配到

router-link-exact-active

精确匹配

to=“/my” 仅可以匹配 /my

在地址栏中输入二级路由查看类名的添加

Untitled

理解:即使地址栏后面加上了 /one ,也就是所谓的二级地址,我们通过模糊匹配router-link-active

也能够让他变色。


自定义类名

问题:router-link的两个高亮类名 太长了,我们希望能定制怎么办

解决方案

我们可以在创建路由对象时,额外配置两个配置项即可。 linkActiveClasslinkExactActiveClass

const router = new VueRouter({
  routes: [...],
  linkActiveClass: "类名1",
  linkExactActiveClass: "类名2"
})

Untitled

详细操作过程

Style:

.footer_wrap a.router-link-active {
  background-color: purple;
}

经过main.js配置过后

const router = new VueRouter({
  // routes 路由规则们
  // route  一条路由规则 { path: 路径, component: 组件 }
  routes: [
    { path: '/find', component: Find },
    { path: '/my', component: My },
    { path: '/friend', component: Friend },
  ],
  // link 自定义高亮
  linkActiveClass: 'active',
  linkExactActiveClass: 'exact-active'
})

style可以简化成如下:

.footer_wrap a.active {
  background-color: purple;
}


跳转传参:

目标:在跳转路由时,进行传参

Untitled

比如:当在搜索页点击了热门搜索链接,跳转到详情页,要把点击的内容带到详情页,该怎么办呢?

**跳转传参:

我们可以通过两种方式,在跳转的时候把所需要的参数传到其他页面中

  • 查询参数传参
  • 动态路由传参

**查询参数传参:

  • 如何传参?
  • 如何接受参数固定用法:
    • $router.query.参数名

案例

Untitled

  • Code:

    Home.vue

    <template>
      <div class="home">
        <div class="logo-box"></div>
        <div class="search-box">
          <input v-model="inpValue" type="text">
          <button @click="goSearch">搜索一下</button>
        </div>
        <div class="hot-link">
          热门搜索:
          <router-link to="/search/黑马程序员?key=test">黑马程序员</router-link>
          <router-link to="/search/前端培训">前端培训</router-link>
          <router-link to="/search/如何成为前端大牛">如何成为前端大牛</router-link>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: 'FindMusic',
      data() {
        return {
          inpValue: '',
        }
      },
      methods: {
        goSearch() {
          this.$router.push(`/search${this.inpValue}`)
        }
      }
    }
    </script>
    
    <style>
    .logo-box {
      height: 150px;
      background: url('@/assets/logo.jpeg') no-repeat center;
    }
    .search-box {
      display: flex;
      justify-content: center;
    }
    .search-box input {
      width: 400px;
      height: 30px;
      line-height: 30px;
      border: 2px solid #c4c7ce;
      border-radius: 4px 0 0 4px;
      outline: none;
    }
    .search-box input:focus {
      border: 2px solid #ad2a26;
    }
    .search-box button {
      width: 100px;
      height: 36px;
      border: none;
      background-color: #ad2a26;
      color: #fff;
      position: relative;
      left: -2px;
      border-radius: 0 4px 4px 0;
    }
    .hot-link {
      width: 508px;
      height: 60px;
      line-height: 60px;
      margin: 0 auto;
    }
    .hot-link a {
      margin: 0 5px;
    }
    </style>
    

    Search.vue

    <template>
      <div class="search">
        <p>搜索关键字: {{ $route.query.key }}</p>
        <p>搜索结果: </p>
        <ul>
          <li>.............</li>
          <li>.............</li>
          <li>.............</li>
          <li>.............</li>
        </ul>
      </div>
    </template>
    
    <script>
    export default {
      name: 'MyFriend',
      created () {
        // 在created中,获取路由参数
        // this.$route.query.参数名 获取
        console.log(this.$route.query.key);
      }
    }
    </script>
    
    <style>
    .search {
      width: 400px;
      height: 240px;
      padding: 0 20px;
      margin: 0 auto;
      border: 2px solid #c4c7ce;
      border-radius: 5px;
    }
    </style>
    

注意:如果在像created这种的js代码中,不要忘记加上this.


动态路由传参方式

配置动态路由步骤:

1-配置动态路由

动态路由后面的参数可以随便起名,但要有语义

index.js

const router = new VueRouter({
  routes: [
    ...,
    { 
      path: '/search/:words', 
      component: Search 
    }
  ]
})

2-配置导航链接

Home.vue

to="/path/参数值"

3-对应页面组件接受参数

Search.vue

{{ $route.params.参数名 }}

params后面的参数名要和动态路由配置的参数保持一致

案例

Untitled

  • Code:

    index.js

    import Home from '@/views/Home'
    import Search from '@/views/Search'
    import Vue from 'vue'
    import VueRouter from 'vue-router'
    Vue.use(VueRouter) // VueRouter插件初始化
    
    // 创建了一个路由对象
    const router = new VueRouter({
      routes: [
        { path: '/home', component: Home },
    		// *** 1, 配置动态路由
        { path: '/search/:words', component: Search }
      ]
    })
    
    export default router
    

    Home.vue

    // *** 2, 配置导航链接
    <router-link to="/search/letstry">Vue-test</router-link>
    

    Search.vue

    <template>
      <div class="search">
    		<!-- *** 3, 对应页面组件接收参数 -->
        <p>搜索关键字: {{ $route.params.words }}</p>
        <p>搜索结果: </p>
        <ul>
          <li>.............</li>
          <li>.............</li>
          <li>.............</li>
          <li>.............</li>
        </ul>
      </div>
    </template>
    
    <script>
    export default {
      name: 'MyFriend',
      created () {
        // 在created中,获取路由参数
        // this.$route.query.参数名 获取
        console.log(this.$route.query.key);
      }
    }
    </script>
    
    <style>
    .search {
      width: 400px;
      height: 240px;
      padding: 0 20px;
      margin: 0 auto;
      border: 2px solid #c4c7ce;
      border-radius: 5px;
    }
    </style>
    

查询参数传参 VS 动态路由传参

  1. 查询参数传参 (比较适合传多个参数)
    1. 跳转:to=“/path?参数名=值&参数名2=值”
    2. 获取:$route.query.参数名
  2. 动态路由传参 (优雅简洁,传单个参数比较方便)
    1. 配置动态路由:path: “/path/:参数名”
    2. 跳转:to=“/path/参数值”
    3. 获取:$route.params.参数名

注意:动态路由也可以传多个参数,但一般只传一个


动态路由参数的可选符(了解)

问题:配了路由 path:“/search/:words” 为什么按下面步骤操作,会未匹配到组件,显示空白?

Untitled

原因:/search/:words 表示,必须要传参数。如果不传参数,也希望匹配,可以加个可选符"?"

const router = new VueRouter({
  routes: [
 	...
    { path: '/search/:words?', component: Search }
  ]
})


Vue路由

重定向

**问题:**网页打开时, url 默认是 / 路径,未匹配到组件时,会出现空

Untitled

解决方案:重定向 → 匹配 / 后, 强制跳转 /home 路径

语法:

{ path: 匹配路径, redirect: 重定向到的路径 },
比如:
{ path:'/' ,redirect:'/home' }

代码:

const router = new VueRouter({
  routes: [
    { path: '/', redirect: '/home'},
 	 ...
  ]
})


404

**作用:**当路径找不到匹配时,给个提示页面

位置:404的路由,虽然配置在任何一个位置都可以,但一般都配置在其他路由规则的最后面

**语法:**path: “*” (任意路径) – 前面不匹配就命中最后这个

import NotFind from '@/views/NotFind'

const router = new VueRouter({
  routes: [
    ...
    { path: '*', component: NotFind } //最后一个
  ]
})


模式设置

问题:

路由的路径看起来不自然, 有#,能否切成真正路径形式?

  • hash路由(默认) 例如: http://localhost:8080/#/home
  • history路由(常用) 例如: http://localhost:8080/home (以后上线需要服务器端支持,开发环境webpack给规避掉了history模式的问题)

理解

1-hash模式:底层是由a标签,锚链接实现的

2-history模式:由新增的HTML5中的historyAPI实现

语法:

const router = new VueRouter({
    mode:'histroy', //默认是hash
    routes:[]
})

注意:一旦采用了 histroy 模式,地址栏就没有 # ,需要后台配置访问规则



两种路由跳转方式

两种路径直接跳转

**问题:**点击按钮跳转如何实现?

Untitled

**方案:**编程式导航:用JS代码来进行跳转

语法:

两种语法:

  • path 路径跳转 (简易方便)
  • name 命名路由跳转 (适合 path 路径长的场景)

path路径跳转语法:

特点:简易方便

Home.vue

//简单写法
this.$router.push('路由路径')
//eg
this.$router.push(`/search${this.inpValue}`)

//完整写法
this.$router.push({
  path: '路由路径'
})

代码

  • Code:

    Home.vue中的script

    export default {
      name: 'FindMusic',
      methods: {
        goSearch() {
          // Plan1: 简易写法
          // this.$router.push('/search')
          // Plan2: 完整写法
          this.$router.push({
            path: '/search'
          })
        }
      }
    }
    

**name命名路由跳转:

特点:适合 path 路径长的场景

语法:

  • 路由规则,必须配置name配置项

index.js

{ name: '路由名', path: '/path/xxx', component: XXX },
  • 通过name来进行跳转

Home.vue

this.$router.push({
  name: '路由名'
})

理解:给routes下的route起别名,方便自己之后push路径

代码演示通过name命名路由跳转:

  • Code:

    index.js

    { name: 'test', path: '/search', component: Search}
    

    Home.vue

    export default {
      name: 'FindMusic',
      methods: {
        goSearch() {
          this.$router.push({
            name: 'search',
          })
        }
      }
    }
    


问题:点击搜索按钮,跳转需要把文本框中输入的内容传到下一个页面如何实现?

Untitled

**两种传参方式:

1.查询参数

2.动态路由传参


path路径跳转传参

传参:

两种跳转方式,对于两种传参方式都支持:① path 路径跳转传参 ② name 命名路由跳转传参

**path路径跳转传参(query传参):

//简单写法
this.$router.push('/路径?参数名1=参数值1&参数2=参数值2')
//完整写法
this.$router.push({
  path: '/路径',
  query: {
    参数名1: '参数值1',
    参数名2: '参数值2'
  }
})

接受参数的方式依然是:$route.query.参数名

举例:

Home.vue

goSearch() {
  this.$router.push({
    path: '/search',
    query: {
      p1: this.inpValue,
    }
  })
}

Search.vue

<p>搜索关键字: {{ $route.query.p1 }}</p>

**path路径跳转传参(动态路由传参):

index.js 先在index中写出动态路由

{ path: '/search/:words', component: Search },

Home.vue

//简单写法
this.$router.push('/路径/参数值')
//完整写法
this.$router.push({
  path: '/路径/参数值'
})

Search.vue 接受参数的方式依然是:$route.params.参数值

<p>搜索关键字: {{ $route.params.words }}</p>

**注意:**path不能配合params使用



name命名路由传参

1.name 命名路由跳转传参 (query传参)

this.$router.push({
  name: '路由名字',
  query: {
    参数名1: '参数值1',
    参数名2: '参数值2'
  }
})

案例

index.js

{ name: 'test', path: '/search', component: Search}

Home.vue

goSearch() {
  this.$router.push({
    name: 'test',
    query: {
      key: this.inpValue
    }
  })
}

Search.vue

<p>搜索关键字: {{ $route.query.key }}</p>

2.name 命名路由跳转传参 (动态路由传参)

this.$router.push({
  name: '路由名字',
  params: {
    参数名: '参数值',
  }
})

案例

index.js

{ name: 'test', path: '/search/:words', component: Search}

Home.vue

goSearch() {
  this.$router.push({
    name: 'test',
    params: {
      words: this.inpValue
    }
  })
}

Search.vue

<p>搜索关键字: {{ $route.params.words }}</p>

总结

编程式导航,如何跳转传参?

1.path路径跳转

query传参

this.$router.push('/路径?参数名1=参数值1&参数2=参数值2')
this.$router.push({
  path: '/路径',
  query: {
    参数名1: '参数值1',
    参数名2: '参数值2'
  }
})

—动态路由传参

this.$router.push('/路径/参数值')
this.$router.push({
  path: '/路径/参数值'
})

query与动态路由的区别:前者适合传多个参数,后者适合传入单个参数

2.name命名路由跳转

query传参

this.$router.push({
  name: '路由名字',
  query: {
    参数名1: '参数值1',
    参数名2: '参数值2'
  }
})

动态路由传参 (需要配动态路由)

this.$router.push({
  name: '路由名字',
  params: {
    参数名: '参数值',
  }
})

path路径跳转和name命名路由跳转区别:前者简易方便,后者适合path路径长的场景




面经基础版本-案例效果分析

效果与功能分析:

面经效果演示

Untitled

功能分析

1-配路由

首页 和 面经 详情,两个一级路由

首页内嵌四个可切换页面(嵌套二级路由)

2-实现功能

首页请求渲染

跳转传参 到 详情页,详情页渲染

组件缓存,优化性能

Untitled



一级路由配置:

1.把文档中准备的素材拷贝到项目中

2.针对router/index.js文件 进行一级路由配置

...
import Layout from '@/views/Layout.vue'
import ArticleDetail from '@/views/ArticleDetail.vue'
...

const router = new VueRouter({
  routes: [
    {
      path: '/',
      component: Layout
    },
    {
      path: '/detail',
      component: ArticleDetail
    }
  ]
})


二级路由配置

二级路由也叫嵌套路由,当然也可以嵌套三级、四级…

1.使用场景

当在页面中点击链接跳转,只是部分内容切换时,我们可以使用嵌套路由

2.语法

  • 在一级路由下,配置children属性即可
  • 配置二级路由的出口

1.在一级路由下,配置children属性

注意:一级的路由path 需要加 /

const router = new VueRouter({
  routes: [
    {
      path: '/',
      component: Layout,
      children:[
        //children中的配置项 跟一级路由中的配置项一模一样 
        {path:'xxxx',component:xxxx.vue},
        {path:'xxxx',component:xxxx.vue},
      ]
    }
  ]
})

技巧:二级路由应该配置到哪个一级路由下呢?

这些二级路由对应的组件渲染到哪个一级路由下,children就配置到哪个路由下边

2.配置二级路由的出口

注意: 配置了嵌套路由,一定配置对应的路由出口,否则不会渲染出对应的组件

Layout.vue

<template>
  <div class="h5-wrapper">
    <div class="content">
      <router-view></router-view>
    </div>
  ....
  </div>
</template>
  • Code:

    index.js

    import Vue from 'vue'
    import VueRouter from "vue-router";
    import Layout from '@/views/Layout'
    import Article from '@/views/Article'
    import Collect from '@/views/Collect'
    import Like from '@/views/Like'
    import User from '@/views/User'
    import ArticleDetail from '@/views/ArticleDetail'
    Vue.use(VueRouter)
    
    const router = new VueRouter({
      // article 路径 -> Article 组件
      // 通过 children 配置项,可以配置嵌套路由
      // 1. 在 children 配置项中,配规则
      // 2. 准备二级路由出口
      routes: [
        {
          path: '/',
          component: Layout,
          children: [
            {
              path: '/article',
              component: Article,
            },
            {
              path: '/collect',
              component: Collect,
            },
            {
              path: '/like',
              component: Like,
            },
            {
              path: '/user',
              component: User,
            },
          ]
        },
        {
          path: '/detail',
          component: ArticleDetail
        }
      ]
    })
    
    export default router
    

    Layout.vie

    <template>
      <div class="h5-wrapper">
        <div class="content">
          <!-- 二级路由出口,匹配到的二级路由组件就会展示 -->
          <router-view></router-view>
        </div>
        <nav class="tabbar">
          <a href="#/article">面经</a>
          <a href="#/collect">收藏</a>
          <a href="#/like">喜欢</a>
          <a href="#/user">我的</a>
        </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;
        }
      }
    }
    </style>
    


二级导航高亮

实现思路

  • 将a标签替换成 组件,配置to属性,不用加 #
  • 结合高亮类名实现高亮效果 (推荐模糊匹配:router-link-active)

代码实现

Layout.vue

<nav class="tabbar">
  <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>

---------------------------

a.router-link-active {
  color: orange
}
  • Code:

    <template>
      <div class="h5-wrapper">
        <div class="content">
          <!-- 二级路由出口,匹配到的二级路由组件就会展示 -->
          <router-view></router-view>
        </div>
        <nav class="tabbar">
          <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>
    


首页请求渲染

1.步骤分析

1.安装axios

2.看接口文档,确认请求方式,请求地址,请求参数

3.created中发送请求,获取数据,存储到data中

4.页面动态渲染

2.代码实现

1.安装axios

yarn add axios  npm i axios

2.接口文档

请求地址: https://mock.boxuegu.com/mock/3083/articles
请求方式: get

3.created中发送请求,获取数据,存储到data中

data() {
  return {
    articelList: [],
  }
},
async created() {
  const {  data: { result: { rows } }} = await axios.get('https://mock.boxuegu.com/mock/3083/articles')
  this.articelList = rows
},

4.页面动态渲染

<template>
  <div class="article-page">
    <div class="article-item" v-for="item in articelList" :key="item.id">
      <div class="head">
        <img :src="item.creatorAvatar" alt="" />
        <div class="con">
          <p class="title">{{ item.stem }}</p>
          <p class="other">{{ item.creatorName }} | {{ item.createdAt }}</p>
        </div>
      </div>
      <div class="body">
        {{item.content}}
      </div>
      <div class="foot">点赞 {{item.likeCount}} | 浏览 {{item.views}}</div>
    </div>
  </div>
</template>


查询参数传参

1.说明

跳转详情页需要把当前点击的文章id传给详情页,获取数据

  • 查询参数传参 this.$router.push(‘/detail?参数1=参数值&参数2=参数值’)
  • 动态路由传参 先改造路由 在传参 this.$router.push(‘/detail/参数值’)

2.查询参数传参实现

关键点

1-点击跳转文章详情

Article.vue

@click="$router.push(`/detail?id=${item.id}`)"

2-实现返回上一级:采用 $router.back()

Untitled

ArticleDetail.vue

<nav class="nav"><span @click="$router.back()" class="back">&lt;</span> 面经详情</nav>


动态路由传参

1.实现步骤

  • 改造路由
  • 动态传参
  • 在详情页获取参数

2.代码实现

改造路由

router/index.js

3.额外优化功能点-点击回退跳转到上一页



详情页渲染

1.实现步骤分析

  • 导入axios
  • 查看接口文档
  • 在created中发送请求
  • 页面动态渲染

2.代码实现

接口文档

请求地址: https://mock.boxuegu.com/mock/3083/articles/:id
请求方式: get

这里id走的是动态参数

关键

优化空白界面:

Untitled

问题:在跳转页面的过程中,有空白浏览量和点赞等空白界面

原理:在请求和响应中间,不让template先去加载空的article对象

解决方案:

在 article.id 存在之后,再去做页面渲染

<div class="article-detail-page" v-if="article.id">

-----------------------------------------------------

data() {
  return {
    article: {},
  }
},
  • Code:

    ArticleDetail.vue

    <template>
      <div class="article-detail-page" v-if="article.id">
        <nav class="nav"><span @click="$router.back()" class="back">&lt;</span> 面经详情</nav>
        <header class="header">
          <h1>{{ this.article.stem }}</h1>
          <p>{{ this.article.createdAt }} | {{ this.article.views }} 浏览量 | {{ this.article.likeCount }} 点赞数</p>
          <p>
            <img
              :src=this.article.creatorAvatar
              alt=""
            />
            <span>{{ this.article.creatorName }}</span>
          </p>
        </header>
        <main class="body">
          {{ this.article.content }}
        </main>
      </div>
    </template>
    
    <script>
    import axios from 'axios'
    // 请求地址: https://mock.boxuegu.com/mock/3083/articles/:id
    // 请求方式: get
    export default {
      name: "ArticleDetailPage",
      data() {
        return {
          article: {},
        }
      },
      async created() {
        const id = this.$route.query.id
        const { data } = await axios.get(`https://mock.boxuegu.com/mock/3083/articles/${id}`)
        // console.log(data);
        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>
    


缓存组件

问题:

从面经列表 点到 详情页,又点返回,数据重新加载了 → 希望回到原来的位置

Untitled

原因:

当路由被跳转后,原来所看到的组件就被销毁了(会执行组件内的beforeDestroy和destroyed生命周期钩子),重新返回后组件又被重新创建了(会执行组件内的beforeCreate,created,beforeMount,Mounted生命周期钩子),所以数据被加载了


**解决方案:**利用keep-alive把原来的组件给缓存下来

keep-alive

概念:它是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。

keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件中。

优点:

在组件切换过程中把切换出去的组件保留在内存中,防止重复渲染DOM,

减少加载时间及性能消耗,提高用户体验性。

keep-alive的三个属性

① include : 组件名数组,只有匹配的组件会被缓存

② exclude : 组件名数组,任何匹配的组件都不会被缓存

③ max : 最多可以缓存多少组件实例

案例

对Layout页面进行缓存

Untitled

如右边红框所示,当我们拉到这个位置时,再点击‘前端小白’这篇文章,然后退回来,会发现拖动条仍然在这个位置。

Untitled


原理:被缓存的组件会多两个生命周期

额外的两个生命周期钩子:keep-alive的使用会触发两个生命周期函数

activated 当组件被激活(使用)的时候触发 → 进入这个页面的时候触发

deactivated 当组件不被使用的时候触发 → 离开这个页面的时候触发

组件缓存后不会执行组件的created, mounted, destroyed 等钩子了

所以其提供了actived 和deactived钩子,帮我们实现业务需求。

理解:

如下图所示:当我们为 Layout 组件的生命周期钩子绑定输出函数,但是他们只会执行一次,之后尽管我们切换页面到 ArticleDetail 中再切回去,也不会再触发生命周期函数。

Untitled

为了弥补无法使用之前生命函数的问题,Vue提供了 activated 和 deactivated 两个函数

如图,当我们第一次加载页面时,就会有 created mounted activated 三个函数被触发

Untitled

当我们切换到 ArticleDetail 时,就会触发deactivated函数

Untitled

总结

  1. keep-alive是什么
    vue 的内置组件,包裹动态组件时,可以缓存
  2. keep-alive的优点
    组件切换过程中 把切换出去的组件保留在内存中(提升性能)
  3. keep-alive的三个属性(了解)
    1-include:组件名数组,只有匹配的组件会被缓存
    2-exclude :组件名数组,任何匹配的组件都不会被缓存
    3-max:最多可以缓存多少组件实例
  4. keep-alive的使用会触发两个生命周期函数(了解)
    activated 当组件被激活(使用)的时候触发 → 进入页面触发
    deactivated 当组件不被使用的时候触发 → 离开页面触发


面经总结:

Untitled




vue-router 的高级用法(参考21版Vue)

路由重定向

路由重定向指的是:用户在访问地址 A 的时候,强制用户跳转到地址 C ,从而展示特定的组件页面。通过路由规则的 redirect 属性,指定一个新的路由地址,可以很方便地设置路由的重定向:

const router = createRouter({
  // 3.1 通过 history 属性指定路由的工作模式
  history: createWebHashHistory(),
  // 3.2 通过 routes 数组,指定路由规则
  routes: [
    // path 是 hash 地址, component 是要展示的组件
    { path: '/', redirect: '/home' },
    { path: '/home', component: MyHome },
    { path: '/movie', component: MyMovie },
    { path: '/about', component: MyAbout },
  ]
})


路由高亮

可以通过如下的两种方式,将激活的路由链接进行高亮显示:

① 使用默认的高亮 class 类 ② 自定义路由高亮的 class 类

2.1 默认的高亮 class 类

被激活的路由链接,默认会应用一个叫做 router-link-active 的类名。开发者可以使用此类名选择器,为激活的路由链接设置高亮的样式:

/* 在 index.css 全局样式表中,重新 router-link-active 的样式 */
.router-link-active {
  background-color: red;
  color: white;
  font-weight: bold;
}

效果:

Untitled


2.2 自定义路由高亮的 class 类

在创建路由的实例对象时,开发者可以基于 linkActiveClass 属性,自定义路由链接被激活时所应用的类名:

// 3. 创建路由实例对象
const router = createRouter({
  // 3.1 通过 history 属性指定路由的工作模式
  history: createWebHashHistory(),
  // 指定被激活的路由链接,会应用 router-active 这个类名
  // 默认的 router-link-active 类名会被覆盖掉
  linkActiveClass: 'router-active',
  // 3.2 通过 routes 数组,指定路由规则
  routes: [
    // 其中,path 表示需要被重定向的“原地址”,redirect表示将要被重定向到的“新地址”
    { path: '/', redirect: '/movie' },
    // path 是 hash 地址, component 是要展示的组件
    { path: '/home', component: MyHome },
    { path: '/movie', component: MyMovie },
    { path: '/about', component: MyAbout },
  ]
})


嵌套路由

通过路由实现组件的嵌套展示,叫做嵌套路由。

Untitled

① 声明子路由链接和子路由占位符

② 在父路由规则中,通过 children 属性嵌套声明子路由规则

3.1 声明子路由链接和子路由占位符

在 About.vue 组件中,声明 tab1 和 tab2 的子路由链接以及子路由占位符。示例代码如下:

<template>
  <div>
    <h1>MyApp</h1>
    <!-- 在关于页面中,声明两个子路由链接 -->
    <router-link to="/about/tab1">Tab1</router-link>&nbsp;
    <router-link to="/about/tab2">Tab2</router-link>&nbsp;
    <router-link to="/about/tab3">Tab3</router-link>
    <hr />

    <!-- 在关于页面中,声明 tab1 和 tab2 的路由占位符 -->
    <router-view></router-view>
  </div>
</template>

3.2 通过 children 属性声明子路由规则

在 router.js 路由模块中,导入需要的组件,并使用 children 属性声明子路由规则。示例代码如下:

import Tab1 from './tabs/MyTab1.vue'
import Tab2 from './tabs/MyTab2.vue'

// 创建路由对象
const router = createRouter({
  // 指定路由的工作模式
  history: createWebHashHistory(),
  // 自定义路由高亮的 class 类
  linkActiveClass: 'active-router',
  // 声明路由的匹配规则
  routes: [
    { path: '/', redirect: '/home'},
    { path: '/home', component: Home },
    { path: '/movie', component: Movie },
    { path: '/about', component: About, children: [
      // 通过 children 属性嵌套子级路由规则
      { path: 'tab1', component: Tab1 }, // 访问 /about/tab1 时, 展示 Tab1 组件
      { path: 'tab2', component: Tab2 }, // 访问 /about/tab2 时,展示 Tab2 组件
    ] },
  ]
})

注意:子路由规则的 path 不要以 / 开头!

3.3 子路由重定向

当我们点击‘关于’时,可以直接显示出Tab1中的内容:

Untitled

routes: [
  // 其中,path 表示需要被重定向的“原地址”,redirect表示将要被重定向到的“新地址”
  { path: '/', redirect: '/movie' },
  // path 是 hash 地址, component 是要展示的组件
  { path: '/home', component: MyHome },
  { path: '/movie', component: MyMovie },
  { path: '/about', component: MyAbout , redirect: '/about/tab1',children: [
    { path: 'tab1', component: Tab1 },
    { path: 'tab2', component: Tab2 },
  ]},
]


动态路由匹配

思考:有如下 3 个路由链接:

<router-link to="/movie/1">电影1</router-link>&nbsp;
<router-link to="/movie/2">电影2</router-link>&nbsp;
<router-link to="/movie/3">电影3</router-link>&nbsp;

定义如下 3 个路由规则,是否可行???

{ path: '/movie/1', component: Movie },
{ path: '/movie/2', component: Movie },
{ path: '/movie/3', component: Movie },

缺点:路由规则的复用性差。

4.1 动态路由的概念

动态路由指的是:把 Hash 地址中可变的部分定义为参数项,从而提高路由规则的复用性。在 vue-router 中使用英文的冒号(:)来定义路由的参数项。

示例代码如下:

// 路由中的动态参数以:进行声明,冒号后面的是动态参数的名称
{ path: '/movie/:id', component: Movie },

// 将以下3个路由规划,合并成了一个,提高了路由规划的复用性
{ path: '/movie/1', component: Movie }
{ path: '/movie/2', component: Movie }
{ path: '/movie/3', component: Movie }

4.2 $route.params 参数对象

通过动态路由匹配的方式渲染出来的组件中,可以使用 $route.params 对象访问到动态匹配的参数值。

<template>
  <!-- $route.params 是路由的“参数对象” -->
  <h3>MyMoive --- {{ $route.params.id }}</h3>
</template>

<script>
export default {
  name: 'MyMovie'
}
</script>

4.3 使用 props 接收路由参数
为了简化路由参数的获取形式,vue-router 允许在路由规则中开启 props 传参。示例代码如下:

// 在定义路由规则时,声明 props: true 选项
    // 即可在 Movie 组件中,以 props 的形式接收到路由规则匹配到的参数项
{ path: '/movie/:id', component: MyMovie , props: true},

<template>
  <!-- 3.直接使用 props 中接收路由参数 -->
  <h3>MyMoive --- {{ id }}</h3>
</template>

<script>
export default {
  name: 'MyMovie',
  props: ['id'] // 2. 使用 props 接收路由规则中匹配到的参数项
}
</script>


编程式导航

通过调用 API 实现导航的方式,叫做编程式导航。与之对应的,通过点击链接实现导航的方式,叫做声明式导航。例如:

普通网页中点击链接、vue 项目中点击 都属于声明式导航

普通网页中调用 location.href 跳转到新页面的方式,属于编程式导航

5.1 vue-router 中的编程式导航 API

vue-router 提供了许多编程式导航的 API,其中最常用的两个 API 分别是:

① this.$router.push(‘hash 地址’)

跳转到指定 Hash 地址,从而展示对应的组件

② this.$router.go(数值 n)

实现导航历史的前进、后退

**5.2 r o u t e r . p u s h ∗ ∗ 调用 t h i s . router.push** 调用 this. router.push调用this.router.push() 方法,可以跳转到指定的 hash 地址,从而展示对应的组件页面。示例代码如下:

<template>
  <h3>MyHome</h3>
  <button @click="goToMovie(3)">导航到Movie页面</button>
</template>

<script>
export default {
  name: 'MyHome',
  methods: {
    goToMovie(id) {
      this.$router.push('/movie/' + id)
			// this.$router.push(`/movie/${id}`)
    }
  }
}
</script>

5.2 $router.go

调用 this.$router.go() 方法,可以在浏览历史中进行前进和后退。示例代码如下:

<template>
  <!-- 3.直接使用 props 中接收路由参数 -->
  <h3>MyMoive --- {{ id }}</h3>
  <button @click="goBack()">回上一个</button>
</template>

<script>
export default {
  name: 'MyMovie' ,
  props: ['id'] ,// 2. 使用 props 接收路由规则中匹配到的参数项
  methods: {
    goBack() {
      this.$router.go(-1)
    }
  }
}
</script>

效果:

Untitled

总结:

使用对应的API来实现导航跳转,就叫做编程式导航。



命名路由

通过 name 属性为路由规则定义名称的方式,叫做命名路由。示例代码如下:

{ 
	// 使用 name 属性为当前的路由规则定义一个“名称”
	name: 'mov', 
	path: '/movie/:id', 
	component: MyMovie ,
	props: true,
},

注意:命名路由的 name 值不能重复,必须保证唯一性!

6.1 使用命名路由实现声明式导航

为 标签动态绑定 to 属性的值,并通过 name 属性指定要跳转到的路由规则。期间还可以用params 属性指定跳转期间要携带的路由参数。示例代码 如下:

<template>
  <h3>MyHome</h3>
  <router-link :to="{ name: 'mov' , params: {id: 3}}">go to Movie</router-link>
</template>

<script>
export default {
  name: 'MyHome'
}
</script>

6.2 使用命名路由实现编程式导航

调用 push 函数期间指定一个配置对象,name 是要跳转到的路由规则、params 是携带的路由参数:

  <template>
  <div>
    <h3>MyHome</h3>
    <router-link :to="{ name: 'mov' , params: {id: 3}}">go to Movie</router-link>
    <button @click="goToMovie(1)"> go to movie </button>
  </div>
</template>

<script>
export default {
  name: 'MyHome',
  methods: {
    goToMovie(id) {
      this.$router.push({
        name: 'mov',
        params: {
          id: id,
        }
      })
    }
  }
}
</script>

总结:

如果在实际项目中,哈希地址比较长的话,建议采用name来进行命名跳转



导航守卫

导航守卫可以控制路由的访问权限。示意图如下:

Untitled

7.1 如何声明全局导航守卫

全局导航守卫会拦截每个路由规则,从而对每个路由进行访问权限的控制。可以按照如下的方式定义全局导航守卫:

// 创建路由实例对象
const router = createRouter({ ... })

// 声明全局的导航守卫
	// 调用路由实例对象的 beforeEach 函数,声明 "全局前置守卫"
	// fn 必须是一个函数,每次拦截到路由的请求,都会调用 fn 进行处理
		// 因此 fn 叫做 “守卫方法”
router.beforeEach(fn)

也可以是箭头函数:

router.beforeEach(() => {
  console.log('ok');
})

7.2 守卫方法的 3 个形参

全局导航守卫的守卫方法中接收 3 个形参,格式为:

// 创建路由实例对象
const router = createRouter({ ... })

// 全局前置守卫
router.beforeEach((to, from, next) => {
		// to 目标路由对象
		// from 当前导航正要离开的路由对象
		// next 是一个函数,表示放行
})

注意:
① 在守卫方法中如果不声明 next 形参,则默认允许用户访问每一个路由!
② 在守卫方法中如果声明了 next 形参,则必须调用 next() 函数,否则不允许用户访问任何一个路由!

7.3 next 函数的 3 种调用方式

参考示意图,分析 next 函数的 3 种调用方式最终导致的结果:

Untitled

直接放行:next()

案例:

router.beforeEach((to, from, next) => {
  if(to.path === '/main') {
    // 证明用户要访问后台 主页
    next()
  } else {
    // 访问的不是后台主页
    next()
  }
})

强制其停留在当前页面:next(false)

router.beforeEach((to, from, next) => {
  if(to.path === '/main') {
    // 证明用户要访问后台 主页
    next(false)
  } else {
    // 访问的不是后台主页
    next()
  }
})

强制其跳转到登录页面:next(‘/login’)

 router.beforeEach((to, from, next) => {
  if(to.path === '/main') {
    // 证明用户要访问后台 主页
    next('/login')
  } else {
    // 访问的不是后台主页
    next()
  }
})

7.4 结合 token 控制后台主页的访问权限

目的:通过token来控制后台主页的访问权限

我们先通过localStorage来获取token值,再判断目的地址是否为main以及token值是否存在,

如果用户的token值存在且目的地址为main,则让用户通过,否则让用户进入登陆页面。

这里我通过在Application中设置假token来进入网站:

Untitled

router.beforeEach((to, from, next) => {
  const tokenStr = localStorage.getItem('token') // 1. 获取 token
  if(to.path === '/main' && !token) {  // 2. 想要访问“后台主页”,且token值不存在
    // next(false) // 3.1 不允许跳转
    next('/login') // 3.2 强制跳转到"登陆页面"
  } else {
    next()  // 3.3 直接放行,允许访问“后台主页”
  }
})

理解:从localstorage值获取token值,并且根据访问路径以及token值是否存在来进行下面的逻辑处理




VueCli 自定义创建项目

创建步骤:

Untitled

1.安装脚手架 (已安装)

npm i @vue/cli -g

2.创建项目

vue create hm-exp-mobile
  • 选项
Vue CLI v5.0.8
? Please pick a preset:
  Default ([Vue 3] babel, eslint)
  Default ([Vue 2] babel, eslint)
> Manually select features     选自定义
  • 手动选择功能

Untitled

  • 选择vue的版本
  3.x
> 2.x
  • 是否使用history模式

Untitled

  • 选择css预处理

Untitled

  • 选择eslint的风格 (eslint 代码规范的检验工具,检验代码是否符合规范)
  • 比如:const age = 18; => 报错!多加了分号!后面有工具,一保存,全部格式化成最规范的样子

Untitled

  • 选择校验的时机 (直接回车)

Untitled

  • 选择配置文件的生成方式 (直接回车)

Untitled

  • 是否保存预设,下次直接使用? => 不保存,输入 N

Untitled

  • 等待安装,项目初始化完成

Untitled

  • 启动项目
npm run serve


ESlint代码规范及手动修复

代码规范:一套写代码的约定规则。

例如:赋值符号的左右是否需要空格?一句结束是否是要加;?…

没有规矩不成方圆

ESLint:是一个代码检查工具,用来检查你的代码是否符合指定的规则(你和你的团队可以自行约定一套规则)。在创建项目时,我们使用的是 JavaScript Standard Style 代码风格的规则。

1.JavaScript Standard Style 规范说明

建议把:https://standardjs.com/rules-zhcn.html 看一遍,然后在写的时候, 遇到错误就查询解决。

下面是这份规则中的一小部分:

  • 字符串使用单引号 – 需要转义的地方除外
  • 无分号 – 这没什么不好。不骗你!
  • 关键字后加空格 if (condition) { ... }
  • 函数名后加空格 function name (arg) { ... }
  • 坚持使用全等 === 摒弃 == 一但在需要检查 null || undefined 时可以使用 obj == null

2.代码规范错误

如果你的代码不符合standard的要求,eslint会跳出来刀子嘴,豆腐心地提示你。

下面我们在main.js中随意做一些改动:添加一些空行,空格。

import Vue from 'vue'
import App from './App.vue'

import './styles/index.less'
import router from './router'
Vue.config.productionTip = false

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

按下保存代码之后:

你将会看在控制台中输出如下错误:

Untitled

eslint 是来帮助你的。心态要好,有错,就改。

3.手动修正

根据错误提示来一项一项手动修正。

如果你不认识命令行中的语法报错是什么意思,你可以根据错误代码(func-call-spacing, space-in-parens,…)去 ESLint 规则列表中查找其具体含义。

打开 ESLint 规则表,使用页面搜索(Ctrl + F)这个代码,查找对该规则的一个释义。

Untitled



通过eslint插件来实现自动修正

eslint会自动高亮错误显示
通过配置,eslint会自动帮助我们修复错误

  • 如何安装

Untitled

  • 如何配置
// 当保存的时候,eslint自动帮我们修复错误
"editor.codeActionsOnSave": {
    "source.fixAll": true
},
// 保存代码,不自动格式化
"editor.formatOnSave": false

注意:eslint的配置文件必须在根目录下,这个插件才能才能生效。打开项目必须以根目录打开,一次打开一个项目

注意:使用了eslint校验之后,把vscode带的那些格式化工具全禁用了 Beatify

settings.json 参考

{
    "window.zoomLevel": 2,
    "workbench.iconTheme": "vscode-icons",
    "editor.tabSize": 2,
    "emmet.triggerExpansionOnTab": true,
    // 当保存的时候,eslint自动帮我们修复错误
    "editor.codeActionsOnSave": {
        "source.fixAll": true
    },
    // 保存代码,不自动格式化
    "editor.formatOnSave": false
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/860643.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

8.10 用redis实现缓存功能和Spring Cache

什么是缓存? 缓存(Cache), 就是数据交换的缓冲区,俗称的缓存就是缓冲区内的数据,一般从数据库中获取,存储于本地代码。 通过Redis来缓存数据&#xff0c;减少数据库查询操作; 逻辑 每个分类的菜品保存一份缓存数据 数据库菜品数据有变更时清理缓存数据 如何将商品数据缓存起…

Spring AOP(AOP概念,组成成分,实现,原理)

目录 1. 什么是Spring AOP&#xff1f; 2. 为什么要用AOP&#xff1f; 3. AOP该怎么学习&#xff1f; 3.1 AOP的组成 &#xff08;1&#xff09;切面&#xff08;Aspect&#xff09; &#xff08;2&#xff09;连接点&#xff08;join point&#xff09; &#xff08;3&a…

Qt画波浪球(小费力)

画流动波浪 #ifndef WIDGET3_H #define WIDGET3_H#include <QWidget> #include <QtMath> class widget3 : public QWidget {Q_OBJECT public:explicit widget3(QWidget *parent nullptr);void set_value(int v){valuev;}int get_value(){return value;} protecte…

FineReport 使用汇总(不定期更新)

1&#xff0c;下载地址 免费下载FineReport - FineReport报表官网 这里注意 2&#xff0c;后台统计 sql 还是需要自己写 就会有数据 而直接查询表&#xff0c; 没有数据 不过&#xff0c;可能是我不会用。还需要再研究。

Java ThreadLocal是什么

文章目录 引子&#xff1a;SimpleDateFormat类ThreadLocal是什么ThreadLocal 的另一个用途**总结**ThreadLocal的两大用途ThreadLocal 的源代码ThreadLocalMapThreadLocalMap 的问题ThreadLocal的key为什么设置成弱引用&#xff1f;value为什么不是弱引用&#xff1f;Thread、T…

ubuntu 安装 nvidia 驱动

ubuntu 安装 nvidia 驱动 初环境与设备查询型号查询对应的驱动版本安装驱动验证驱动安装结果 本篇文章将介绍ubuntu 安装 nvidia 驱动 初 希望能写一些简单的教程和案例分享给需要的人 环境与设备 系统&#xff1a;ubuntu 设备&#xff1a;Nvidia GeForce RTX 4090 查询型…

每天一道leetcoed:剑指 Offer 28. 对称的二叉树(适合初学者树)

今日份题目&#xff1a; 请实现一个函数&#xff0c;用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样&#xff0c;那么它是对称的。 例如&#xff0c;二叉树 [1,2,2,3,4,4,3] 是对称的。 1 / \ 2 2 / \ / \ 3 4 4 3 但是下面这个 [1,2,2,null,3,nu…

【打印100之内的素数——筛选法】

打印100之内的素数——筛选法 筛选法 1.题目分析 素数&#xff1a;约数为1和该数本身的数字称为素数&#xff0c;即质数 2.方法解析 筛选法&#xff1a;又称为筛法。先把N个自然数按次序排列起来。1不是质数&#xff0c;也不是合数&#xff0c;要划去。第二个数2是质数留下来…

[C++ 网络协议] 套接字

目录 1. 套接字 1.1 在Linux平台下构建套接字 1.1.1 用于接听的套接字(服务器端套接字) 1.1.2 用于发送请求的套接字(客户端套接字) 1.2 在Windows平台下构建套接字 1.2.1 Winsock的初始化 1.2.2 用于接听的套接字(服务器端套接字) 1.2.3 用于发送请求的套接字(客户端套…

Linux Linux系统上C程序的编译与调试

一、环境配置 在Linux操作系统中&#xff0c;打开终端&#xff0c;以管理员root模式登录 1.更新&#xff1a;输入命令apt update 2.下载vim&#xff1a;输入命令apt install vim -y 3.下载gcc&#xff1a;输入命令apt install gcc -y 4.下载g&#xff1a;输入命令apt install …

7.6 通俗易懂解读残差网络ResNet 手撕ResNet

一.举例通俗解释ResNet思想 假设你正在学习如何骑自行车&#xff0c;并且想要骑到一个遥远的目的地。你可以选择直接骑到目的地&#xff0c;也可以选择在途中设置几个“中转站”&#xff0c;每个中转站都会告诉你如何朝着目的地前进。 在传统的神经网络中&#xff0c;就好比只…

八、复用(2)

本章概要 结合组合和继承 保证适当的清理名称隐藏 组合与继承的选择protected向上转型 再论组合和继承 结合组合与继承 你将经常同时使用组合和继承。下面的例子展示了使用继承和组合创建类&#xff0c;以及必要的构造函数初始化: class Plate {Plate(int i) {System.out.…

君子签“签约+存证+诉讼”为银行建立可靠的契约关系和信任机制

随着互联网金融业的发展&#xff0c;商业银行经营转型与创新发展任重而道远。根据现有银行开展的业务来看&#xff0c;业务拓展过程中遇到的瓶颈越来越明显&#xff0c;集中体现在以下几个方面&#xff1a; 传统签署方式存在多种弊端&#xff0c;亟需转型 互联网金融服务采用…

【广州华锐视点】AR电力职业技能培训系统让技能学习更“智慧”

随着科技的发展&#xff0c;教育方式也在不断地进步和创新。其中&#xff0c;增强现实(AR)技术的出现&#xff0c;为教育领域带来了全新的可能。AR电力职业技能培训系统就是这种创新教学方法的完美实践&#xff0c;它将虚拟与现实相结合&#xff0c;为学生提供了一个沉浸式的学…

Android T 窗口层级其一 —— 容器类

窗口在App端是以PhoneWindow的形式存在&#xff0c;承载了一个Activity的View层级结构。这里我们探讨一下WMS端窗口的形式。 可以通过adb shell dumpsys activity containers 来看窗口显示的层级 窗口容器类 —— WindowContainer类 /*** Defines common functionality for c…

中睿天下入选河南省网信系统2023年度网络安全技术支撑单位

近日&#xff0c;河南省委网信办发布了“河南省网信系统2023年度网络安全技术支撑单位名单”&#xff0c;中睿天下凭借出色的网络安全技术能力和优势成功入选。 本次遴选由河南省委网信办会同国家计算机网络与信息安全管理中心河南分中心&#xff08;以下简称安全中心河南分中心…

MySQL高级-存储引擎+存储过程+索引(详解01)

目录 1.mysql体系结构 2.存储引擎 2.1.存储引擎概述 2.2.1.InnoDB 2.2.2.MyISAM 2.2.3.存储引擎选择 3.存储过程 3.1.存储过程和函数概述 3.2.创建存储过程 3.3.调用存储过程 3.4.查看存储过程 3.5.删除存储过程 3.6.语法 3.6.1.变量 3.6.2.if条件判断 3.6.3.…

【STM32】利用CubeMX对FreeRTOS用按键控制任务

对于FreeRTOS中的操作&#xff0c;最常用的就是创建、删除、暂停和恢复任务。 此次实验目标&#xff1a; 1.创建任务一&#xff1a;LED1每间隔1秒闪烁一次&#xff0c;并通过串口打印 2.创建任务二&#xff1a;LED2每间隔0.5秒闪烁一次&#xff0c;并通过串口打印 3.创建任…

[oeasy]python0083_[趣味拓展]字体样式_正常_加亮_变暗_控制序列

字体样式 回忆上次内容 上次了解了 一个新的转义模式 \033 逃逸控制字符 esc esc 让输出 退出 标准输出流进行 控制信息的设置 可以 清屏也可以 设置光标输出的位置 还能做什么呢&#xff1f; 可以 设置 字符的颜色吗&#xff1f;&#xff1f;&#xff1f;&#x1f914; 查…

Vue3 组件基础简单应用

去官网学习→组件基础 | Vue.js 运行示例&#xff1a; 自定义组件 代码&#xff1a; MyComponent.vue <template><h2>MyComponent.vue 组件</h2> </template><script>// 导出export default{name:"MyComponent"} </script><…