Vue学习笔记 (2023黑马版 第六天)
第六天
1.路由进阶
路由的封装分离
好处:拆分模块,利于维护
步骤:
- 新建router文件夹,并在里面新建文件index.js
- 再main.js中将跟路由有关的代码剪切到index.js中
- 再在main.js中引入router
注意事项:
快速引入组件(从根路径开始)
声明式导航 - 导航链接
需求:实现导航高亮
vue-router提供了一个全局组件 router-link(取代a标签)
好处:
- 能跳转,配置to属性指定路径(必须)。本质还是a标签,to前缀无需"#"
- 能高亮,默认就会提供高亮类名,可以直接设置高亮样式
声明式导航 - 两个类名
router-link自动给当前导航栏添加了两个高亮类名
- router-link-active 模糊匹配(用的多)
to = "/my" 可以匹配 "/my" "/my/a" "/my/b" ...
- router-link-exact-active 精确匹配
to = "\my" 仅可以匹配 "/my"
自定义router-link的两个高亮类名:
const router = new VueRouter({
routes: [
{ path: '/find', component: Find },
{ path: '/my', component: My },
{ path: '/friend', component: Friend },
],
linkActiveClass:'active',
linkExactActiveClass:'exact-active'
})
声明式导航 - 跳转传值
- 查询参数传参(比较适合传多个参数)
①语法:
<router-link to="/path?参数名=值">我是router-link</router-link>
②对应页面组件接收传递过来的值
$route.query.参数名
- 动态路由传参(优雅简洁,传单个参数比较方便)
①配置动态路由
const router = new VueRouter({
routes: [
{ path: '/search/:words', component: Search }
]
})
注意事项:/search/:words 表示,必须要传参数。如果不传参数,也希望匹配,可以加个可选符"?",例如以下
{ path: '/search/:words?', component: Search }
②配置导航链接
<router-link to="/path/参数值">我是router-link</router-link>
③对应页面组件接收传递过来的值
$route.params.参数名
2.Vue路由
Vue路由 - 重定向
语法:
const router = new VueRouter({
routes: [
{ path: '/A', redirect: '/B'},//匹配到A路径时,自动跳转到B路径
]
})
Vue路由 - 404
作用:当路径找不到匹配时,给个提示页面
位置:配在路由最后
语法:path:“*”(任意路径)
const router = new VueRouter({
routes: [
{ path: '/', redirect: '/home'},
{ path: '/home', component: Home },
{ path: '/search/:words?', component: Search },
{ path: '*', component: NotFound }//配置在最后一条
]
})
Vue路由 - 模式设置
- hash路由(默认)
例如:http://localhost/8080/#/home - history路由(常用)
例如 http://localhost/8080/home(以后上线需要服务器端支持)
const router = new VueRouter({
mode:'history',
})
3.编程式导航
编程式导航 - 基本跳转
编程式导航:用js代码来进行跳转
- path路径跳转(简易方便)
//简易写法
this.$router.push('路由路径')
或
//完整写法
this.$router.push({
path:'路由路径'
})
- name命名路由跳转(适合path路径长的场景)
this.$router.push({
name: '路由名'
})
{ name: '路由名', path: '/path/xxx', component: xxx },
编程式导航 - 跳转传参
- path路径跳转
① query传参
this.$router.push('/路径?参数名1=参数值1&参数名2=参数值2')
//或者
this.$router.push({
path:'/路径',
query: {
参数名1: '参数值1',
参数名2: '参数值2'
}
})
②动态路由传参(需要配动态路由)
this.$router.push('/路径/参数值1')
//或者
this.$router.push({
path: '/路径/参数值'
})
- name命名路由跳转
①query传参
this.$router.push({
name: '路由名字',
query: {
参数名1: '参数值1',
参数名2: '参数值2'
}
})
②动态路由传参(需要配动态路由)
this.$router.push({
name: '路由名字',
params: {
参数名: '参数值',
}
})
4.keep-alive
keep-alive:Vue的内置组件,包裹动态时,可以缓存
优点:组件切换时,把切换出去的组件保留在内存中(提升性能)
keep-alive的三个属性(了解)
- include:组件名数组,只有匹配的组件会被缓存
- exclude:组件名数组,任何匹配的组件都不会背缓存
- max:最多可以缓存多少组件实例
<template>
<div class="h5-wrapper">
<keep-alive :include="['组件名']">
<router-view></router-view>
</keep-alive>
</div>
</template>
keep-alive的使用触发两个生命周期函数(了解)
activated 当组件被激活(使用)的时候触发 → 进入页面触发
deactivated 当组件不被使用的时候触发 → 离开页面触发
5.案例实践:面经基础版
分析:配路由 + 实现功能
- 配路由
- ①首页和面经详情,两个一级路由
- ②首页内嵌四个可切换页面(嵌套二级路由)
嵌套路由的关键配置项:children
- 实现功能
- ①首页请求渲染
- ②跳转传参到详情页,详情页渲染
查询参数传参, $ route.query.参数名(适合多个参数)
动态路由传参,$route.params.参数名(简洁直观) - ③组件缓存,优化性能
内置组件 keep-alive
效果示意图:
核心代码:
<!--Layout.vue-->
<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>
<!--Article.vue-->
<template>
<div class="article-page">
<div
v-for="item in articles"
:key="item.id"
@click="$router.push(`/detail/${item.id}`)"
class="article-item"
>
<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>
<script>
// 请求地址: https://mock.boxuegu.com/mock/3083/articles
// 请求方式: get
import axios from 'axios'
export default {
name: 'ArticlePage',
data () {
return {
articles: []
}
},
async created () {
const res = await axios.get('https://mock.boxuegu.com/mock/3083/articles')
this.articles = res.data.result.rows
console.log(this.articles)
}
}
</script>
<style lang="less" scoped>
.article-page {
background: #f5f5f5;
}
.article-item {
margin-bottom: 10px;
background: #fff;
padding: 10px 15px;
.head {
display: flex;
img {
width: 40px;
height: 40px;
border-radius: 50%;
overflow: hidden;
}
.con {
flex: 1;
overflow: hidden;
padding-left: 15px;
p {
margin: 0;
line-height: 1.5;
&.title {
text-overflow: ellipsis;
overflow: hidden;
width: 100%;
white-space: nowrap;
}
&.other {
font-size: 10px;
color: #999;
}
}
}
}
.body {
font-size: 14px;
color: #666;
line-height: 1.6;
margin-top: 10px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.foot {
font-size: 12px;
color: #999;
margin-top: 10px;
}
}
</style>
<!--ArticleDetail.vue-->
<template>
<div class="article-detail-page" v-if="article.id">
<nav class="nav"><span @click="$router.back()" class="back"><</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>
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.params.id
const { data } = await axios.get(`https://mock.boxuegu.com/mock/3083/articles/${id}`)
this.article = data.result
console.log(this.article)
}
}
</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>
<!--App.vue-->
<template>
<div class="h5-wrapper">
<keep-alive :include="['LayoutPage']">
<router-view></router-view>
</keep-alive>
</div>
</template>
<script>
export default {
name: "h5-wrapper",
}
</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>
//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({
routes: [
{ path: '/', component: Layout, redirect: '/article',
children: [
{
path: '/article', component:Article
},
{
path: '/collect', component: Collect
},
{
path: '/like', component: Like
},
{
path: '/user', component: User
}
]
},
{ path: '/detail/:id', component: ArticleDetail}
]
})
export default router