Date: May 28, 2023
Sum: vue-router基本使用、高级用法
概念:SPA【Single Page Application】是指所有的功能都在一个html页面上实现
案例:
单页应用网站: 网易云音乐 https://music.163.com/
多页应用网站:京东 https://jd.com/
**单页应用 VS 多页面应用:
单页应用类网站:系统类网站 / 内部网站 / 文档类网站 / 移动端站点
多页应用类网站:公司官网 / 电商类网站
特点:
单页面应用程序将所有的功能局限于一个 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 项目
总结:
概念:一个Web网站中只有唯一一个HTML页面,所有的功能与交互都在这唯一的一个页面内完成。
特点:
1.集中所有功能于单页面中,仅在该页面初始化时加载相应的资源。
2.通过JS动态变换页面内容,从而实现页面与用户的交互
优点:
良好的交互体验
没有页面跳转,不会有“白屏”现象
良好的前后端分离模式
前端专注页面渲染,更利于前端工程化发展;后端专注提供API接口,更易实现API接口复用
减轻服务端的压力
服务器只提供数据,不负责页面的合成与逻辑,吞吐能力提高几倍
缺点:
首屏加载慢
可以通过代码压缩、CDN加速、路由懒加载、网络传输压缩处理等
不利于SEO
SSR服务器端渲染
单页面应用程序,之所以开发效率高,性能好,用户体验好
最大的原因就是:页面按需更新
比如当点击【发现音乐】和【关注】时,只是更新下面部分内容,对于头部是不更新的
要按需更新,首先就需要明确:访问路径和 组件的对应关系!
访问路径 和 组件的对应关系如何确定呢? 路由
概念:路由(英文:router)就是对应关系。路由分为两大类: ① 后端路由 ② 前端路由
回顾:后端路由
后端路由指的是:请求方式、请求地址与 function 处理函数之间的对应关系。
在 node.js 课程中,express路由的基本用法如下:
SPA 与前端路由
SPA 指的是一个 web 网站只有唯一的一个 HTML 页面,所有组件的展示与切换都在这唯一的一个页面内完成。此时,不同组件之间的切换需要通过前端路由来实现。
结论:在 SPA 项目中,不同功能之间的切换,要依赖于前端路由来完成!
概念:Vue中的路由指 路径 与 组件 的映射关系,根据路由就能知道不同路径,应匹配渲染相应组件
通俗易懂的概念:Hash 地址与组件之间的对应关系。
我们点击不同的链接,会切换不同的哈希地址。当哈希地址切换,页面上就会展示对应组件。
工作方式:
① 用户点击了页面上的路由链接
② 导致了 URL 地址栏中的 Hash 值发生了变化
③ 前端路由监听了到 Hash 地址的变化
④ 前端路由把当前 Hash 地址对应的组件渲染都浏览器中
结论:前端路由,指的是 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 属性,动态切换要显示的组件。示例代码如下:
App.vue
This is a test
export default {
data() {
return {
comName: 'MyName', // 要展示的组件的名称
}
},
}
步骤3:在组件的结构中声明如下 3 个 链接,通过点击不同的 链接,切换浏览器地址栏中的 Hash 值:
Home
Movie
About
步骤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
概念:
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 官方的一个路由插件,是一个第三方包
固定5个固定的步骤(不用死背,熟能生巧)
1-下载 VueRouter 模块到当前工程,版本3.6.5
yarn add [email protected]
npm install [email protected]
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管理了
案例:
main.js
// 路由的使用步骤 5 + 2
// 5个基础步骤
// 1. 下载 v3.6.5
// yarn add [email protected]
// 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目录),配置路由规则
2-配置导航,配置路由出口(路径匹配的组件显示的位置)
代码中的router-view是用来匹配导航栏的位置的,如果它在代码的上/下面,那么导航栏也在上/下面
如下图所示:
App.vue
案例:
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
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, 其余同理:
MyAbout
3.3 声明路由链接和占位符
可以使用 标签来声明路由链接,并使用 标签来声明路由占位符。
示例代码如下:
MyApp
首页
电影
关于
注意:在声明哈希属性时,需要用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')
效果:
当我们点击电影时,地址栏会切换成 #/movie,当哈希地址发生变化,就会被路由模块所监听到,然后进行哈希地址的匹配:
// router.js
routes: [
// path 是 hash 地址, component 是要展示的组件
{ path: '/home', component: MyHome },
{ path: '/movie', component: MyMovie },
{ path: '/about', component: MyAbout },
]
当匹配到,我们就会把相应的组件比如MyMovie放到App.vue中的路由占位符中
// App.vue
MyApp
首页
电影
关于
注意: .vue文件 本质无区别
组件分类:
.vue文件分为2类,都是 .vue文件(本质无区别)
存放目录:
分类开来的目的就是为了 更易维护
src/views
文件夹页面组件 - 页面展示 - 配合路由用
src/components
文件夹复用组件 - 展示数据 - 常用于复用
问题:所有的路由配置都在main.js中合适吗?
目标:将路由模块抽离出来。 好处:拆分模块,利于维护
路径简写:
脚手架环境下 @指代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
需求:实现导航高亮效果
如果使用a标签进行跳转的话,需要给当前跳转的导航加样式,同时要移除上一个a标签的样式,太麻烦!!!
**解决方案:
vue-router 提供了一个全局组件 router-link (取代 a 标签)
语法: 发现音乐
通过router-link自带的两个样式进行高亮:
使用router-link跳转后,我们发现。当前点击的链接默认加了两个class的值 router-link-exact-active
和router-link-active
我们可以给任意一个class属性添加高亮样式即可实现功能
代码:
Code:
App.vue
当我们使用跳转时,自动给当前导航加了两个类名
router-link-active
模糊匹配(用的多)
to=“/my” 可以匹配 /my /my/a /my/b …
只要是以/my开头的路径 都可以和 to="/my"匹配到
router-link-exact-active
精确匹配
to=“/my” 仅可以匹配 /my
在地址栏中输入二级路由查看类名的添加
理解:即使地址栏后面加上了 /one ,也就是所谓的二级地址,我们通过模糊匹配router-link-active
也能够让他变色。
问题:router-link的两个高亮类名 太长了,我们希望能定制怎么办
解决方案
我们可以在创建路由对象时,额外配置两个配置项即可。 linkActiveClass
和linkExactActiveClass
const router = new VueRouter({
routes: [...],
linkActiveClass: "类名1",
linkExactActiveClass: "类名2"
})
详细操作过程:
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;
}
目标:在跳转路由时,进行传参
比如:当在搜索页点击了热门搜索链接,跳转到详情页,要把点击的内容带到详情页,该怎么办呢?
**跳转传参:
我们可以通过两种方式,在跳转的时候把所需要的参数传到其他页面中
**查询参数传参:
案例:
Code:
Home.vue
热门搜索:
黑马程序员
前端培训
如何成为前端大牛
Search.vue
搜索关键字: {{ $route.query.key }}
搜索结果:
- .............
- .............
- .............
- .............
注意:如果在像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后面的参数名要和动态路由配置的参数保持一致
案例:
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, 配置导航链接
Vue-test
Search.vue
搜索关键字: {{ $route.params.words }}
搜索结果:
- .............
- .............
- .............
- .............
查询参数传参 VS 动态路由传参:
注意:动态路由也可以传多个参数,但一般只传一个
问题:配了路由 path:“/search/:words” 为什么按下面步骤操作,会未匹配到组件,显示空白?
原因:/search/:words 表示,必须要传参数。如果不传参数,也希望匹配,可以加个可选符"?"
const router = new VueRouter({
routes: [
...
{ path: '/search/:words?', component: Search }
]
})
**问题:**网页打开时, url 默认是 / 路径,未匹配到组件时,会出现空
解决方案:重定向 → 匹配 / 后, 强制跳转 /home 路径
语法:
{ path: 匹配路径, redirect: 重定向到的路径 },
比如:
{ path:'/' ,redirect:'/home' }
代码:
const router = new VueRouter({
routes: [
{ path: '/', redirect: '/home'},
...
]
})
**作用:**当路径找不到匹配时,给个提示页面
位置:404的路由,虽然配置在任何一个位置都可以,但一般都配置在其他路由规则的最后面
**语法:**path: “*” (任意路径) – 前面不匹配就命中最后这个
import NotFind from '@/views/NotFind'
const router = new VueRouter({
routes: [
...
{ path: '*', component: NotFind } //最后一个
]
})
问题:
路由的路径看起来不自然, 有#,能否切成真正路径形式?
理解:
1-hash模式:底层是由a标签,锚链接实现的
2-history模式:由新增的HTML5中的historyAPI实现
语法:
const router = new VueRouter({
mode:'histroy', //默认是hash
routes:[]
})
注意:一旦采用了 histroy 模式,地址栏就没有 # ,需要后台配置访问规则
**问题:**点击按钮跳转如何实现?
**方案:**编程式导航:用JS代码来进行跳转
语法:
两种语法:
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 路径长的场景
语法:
index.js
{ name: '路由名', path: '/path/xxx', component: XXX },
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',
})
}
}
}
问题:点击搜索按钮,跳转需要把文本框中输入的内容传到下一个页面如何实现?
**两种传参方式:
1.查询参数
2.动态路由传参
传参:
两种跳转方式,对于两种传参方式都支持:① 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
搜索关键字: {{ $route.query.p1 }}
**path路径跳转传参(动态路由传参):
index.js 先在index中写出动态路由
{ path: '/search/:words', component: Search },
Home.vue
//简单写法
this.$router.push('/路径/参数值')
//完整写法
this.$router.push({
path: '/路径/参数值'
})
Search.vue 接受参数的方式依然是:$route.params.参数值
搜索关键字: {{ $route.params.words }}
**注意:**path不能配合params使用
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
搜索关键字: {{ $route.query.key }}
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
搜索关键字: {{ $route.params.words }}
编程式导航,如何跳转传参?
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路径长的场景
面经效果演示
功能分析
1-配路由
首页 和 面经 详情,两个一级路由
首页内嵌四个可切换页面(嵌套二级路由)
2-实现功能
首页请求渲染
跳转传参 到 详情页,详情页渲染
组件缓存,优化性能
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.语法
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
....
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
实现思路
代码实现
Layout.vue
---------------------------
a.router-link-active {
color: orange
}
Code:
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.页面动态渲染
{{ item.stem }}
{{ item.creatorName }} | {{ item.createdAt }}
{{item.content}}
点赞 {{item.likeCount}} | 浏览 {{item.views}}
1.说明
跳转详情页需要把当前点击的文章id传给详情页,获取数据
2.查询参数传参实现
关键点:
1-点击跳转文章详情
Article.vue
@click="$router.push(`/detail?id=${item.id}`)"
2-实现返回上一级:采用 $router.back()
ArticleDetail.vue
1.实现步骤
2.代码实现
改造路由
router/index.js
3.额外优化功能点-点击回退跳转到上一页
1.实现步骤分析
2.代码实现
接口文档
请求地址: https://mock.boxuegu.com/mock/3083/articles/:id
请求方式: get
这里id走的是动态参数
关键:
优化空白界面:
问题:在跳转页面的过程中,有空白浏览量和点赞等空白界面
原理:在请求和响应中间,不让template先去加载空的article对象
解决方案:
在 article.id 存在之后,再去做页面渲染
-----------------------------------------------------
data() {
return {
article: {},
}
},
-
Code:
ArticleDetail.vue
{{ this.article.stem }}
{{ this.article.createdAt }} | {{ this.article.views }} 浏览量 | {{ this.article.likeCount }} 点赞数
{{ this.article.creatorName }}
{{ this.article.content }}
缓存组件
问题:
从面经列表 点到 详情页,又点返回,数据重新加载了 → 希望回到原来的位置
原因:
当路由被跳转后,原来所看到的组件就被销毁了(会执行组件内的beforeDestroy和destroyed生命周期钩子),重新返回后组件又被重新创建了(会执行组件内的beforeCreate,created,beforeMount,Mounted生命周期钩子),所以数据被加载了
**解决方案:**利用keep-alive把原来的组件给缓存下来
keep-alive
概念:它是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件中。
优点:
在组件切换过程中把切换出去的组件保留在内存中,防止重复渲染DOM,
减少加载时间及性能消耗,提高用户体验性。
keep-alive的三个属性
① include : 组件名数组,只有匹配的组件会被缓存
② exclude : 组件名数组,任何匹配的组件都不会被缓存
③ max : 最多可以缓存多少组件实例
案例:
对Layout页面进行缓存
如右边红框所示,当我们拉到这个位置时,再点击‘前端小白’这篇文章,然后退回来,会发现拖动条仍然在这个位置。
原理:被缓存的组件会多两个生命周期
额外的两个生命周期钩子:keep-alive的使用会触发两个生命周期函数
activated 当组件被激活(使用)的时候触发 → 进入这个页面的时候触发
deactivated 当组件不被使用的时候触发 → 离开这个页面的时候触发
组件缓存后就不会执行组件的created, mounted, destroyed 等钩子了
所以其提供了actived 和deactived钩子,帮我们实现业务需求。
理解:
如下图所示:当我们为 Layout 组件的生命周期钩子绑定输出函数,但是他们只会执行一次,之后尽管我们切换页面到 ArticleDetail 中再切回去,也不会再触发生命周期函数。
为了弥补无法使用之前生命函数的问题,Vue提供了 activated 和 deactivated 两个函数
如图,当我们第一次加载页面时,就会有 created mounted activated 三个函数被触发
当我们切换到 ArticleDetail 时,就会触发deactivated函数
总结:
- keep-alive是什么
vue 的内置组件,包裹动态组件时,可以缓存
- keep-alive的优点
组件切换过程中 把切换出去的组件保留在内存中(提升性能)
- keep-alive的三个属性(了解)
1-include:组件名数组,只有匹配的组件会被缓存
2-exclude :组件名数组,任何匹配的组件都不会被缓存
3-max:最多可以缓存多少组件实例
- keep-alive的使用会触发两个生命周期函数(了解)
activated 当组件被激活(使用)的时候触发 → 进入页面触发
deactivated 当组件不被使用的时候触发 → 离开页面触发
面经总结:
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;
}
效果:
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 },
]
})
嵌套路由
通过路由实现组件的嵌套展示,叫做嵌套路由。
① 声明子路由链接和子路由占位符
② 在父路由规则中,通过 children 属性嵌套声明子路由规则
3.1 声明子路由链接和子路由占位符
在 About.vue 组件中,声明 tab1 和 tab2 的子路由链接以及子路由占位符。示例代码如下:
MyApp
Tab1
Tab2
Tab3
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中的内容:
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 个路由链接:
电影1
电影2
电影3
定义如下 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 对象访问到动态匹配的参数值。
MyMoive --- {{ $route.params.id }}
4.3 使用 props 接收路由参数
为了简化路由参数的获取形式,vue-router 允许在路由规则中开启 props 传参。示例代码如下:
// 在定义路由规则时,声明 props: true 选项
// 即可在 Movie 组件中,以 props 的形式接收到路由规则匹配到的参数项
{ path: '/movie/:id', component: MyMovie , props: true},
MyMoive --- {{ id }}
编程式导航
通过调用 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 地址,从而展示对应的组件页面。示例代码如下:
MyHome
5.2 $router.go
调用 this.$router.go() 方法,可以在浏览历史中进行前进和后退。示例代码如下:
MyMoive --- {{ id }}
效果:
总结:
使用对应的API来实现导航跳转,就叫做编程式导航。
命名路由
通过 name 属性为路由规则定义名称的方式,叫做命名路由。示例代码如下:
{
// 使用 name 属性为当前的路由规则定义一个“名称”
name: 'mov',
path: '/movie/:id',
component: MyMovie ,
props: true,
},
注意:命名路由的 name 值不能重复,必须保证唯一性!
6.1 使用命名路由实现声明式导航
为 标签动态绑定 to 属性的值,并通过 name 属性指定要跳转到的路由规则。期间还可以用params 属性指定跳转期间要携带的路由参数。示例代码 如下:
MyHome
go to Movie
6.2 使用命名路由实现编程式导航
调用 push 函数期间指定一个配置对象,name 是要跳转到的路由规则、params 是携带的路由参数:
MyHome
go to Movie
总结:
如果在实际项目中,哈希地址比较长的话,建议采用name来进行命名跳转
导航守卫
导航守卫可以控制路由的访问权限。示意图如下:
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 种调用方式最终导致的结果:
直接放行: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来进入网站:
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 自定义创建项目
创建步骤:
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 选自定义
- 手动选择功能
- 选择vue的版本
3.x
> 2.x
- 是否使用history模式
- 选择css预处理
- 选择eslint的风格 (eslint 代码规范的检验工具,检验代码是否符合规范)
- 比如:const age = 18; => 报错!多加了分号!后面有工具,一保存,全部格式化成最规范的样子
- 选择校验的时机 (直接回车)
- 选择配置文件的生成方式 (直接回车)
- 是否保存预设,下次直接使用? => 不保存,输入 N
- 等待安装,项目初始化完成
- 启动项目
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')
按下保存代码之后:
你将会看在控制台中输出如下错误:
eslint 是来帮助你的。心态要好,有错,就改。
3.手动修正
根据错误提示来一项一项手动修正。
如果你不认识命令行中的语法报错是什么意思,你可以根据错误代码(func-call-spacing, space-in-parens,…)去 ESLint 规则列表中查找其具体含义。
打开 ESLint 规则表,使用页面搜索(Ctrl + F)这个代码,查找对该规则的一个释义。
通过eslint插件来实现自动修正
eslint会自动高亮错误显示
通过配置,eslint会自动帮助我们修复错误
- 如何安装
- 如何配置
// 当保存的时候,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
}