最近发现职场前端用的框架大多为vue,所以最近也跟着黑马程序员vue3的课程进行学习,以下是我的学习记录
视频网址:
Day2-17.Layout-Pinia优化重复请求_哔哩哔哩_bilibili
学习日记:
vue3学习日记3 - 组合式API练习小案例-CSDN博客
一、整体认识和路由配置
1、想要实现效果,点击后跳转页面,后面根据id不同展示的页面数据不同
2、修改router文件夹下,index.js文件
3、修改LayoutHeader和LayoutFixed文件
4、运行截图
二、面包屑导航(路由传参)
1、想要实现的效果
点击居家,后面会显示居家,点击美食,后面会显示美食
2、修改以下文件夹下的文件
html
<script setup>
import {getCategoryAPI} from '@/apis/Category'
import { onMounted, ref } from 'vue'
// 引入useRoute
import { useRoute } from 'vue-router'
const categoryData = ref({})
const route = useRoute()
const getCategory = async () => {
// 使用route.params.id获取路由传递的参数,并传给接口
const res = await getCategoryAPI(route.params.id)
categoryData.value = res.result
}
onMounted(()=>getCategory())
</script>
<template>
<div class="top-category">
<div class="container m-top-20">
<!-- 面包屑 -->
<div class="bread-container">
<el-breadcrumb separator=">">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>{
{ categoryData.name }}</el-breadcrumb-item>
</el-breadcrumb>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.top-category {
h3 {
font-size: 28px;
color: #666;
font-weight: normal;
text-align: center;
line-height: 100px;
}
.sub-list {
margin-top: 20px;
background-color: #fff;
ul {
display: flex;
padding: 0 32px;
flex-wrap: wrap;
li {
width: 168px;
height: 160px;
a {
text-align: center;
display: block;
font-size: 16px;
img {
width: 100px;
height: 100px;
}
p {
line-height: 40px;
}
&:hover {
color: $xtxColor;
}
}
}
}
}
.ref-goods {
background-color: #fff;
margin-top: 20px;
position: relative;
.head {
.xtx-more {
position: absolute;
top: 20px;
right: 20px;
}
.tag {
text-align: center;
color: #999;
font-size: 20px;
position: relative;
top: -20px;
}
}
.body {
display: flex;
justify-content: space-around;
padding: 0 40px 30px;
}
}
.bread-container {
padding: 25px 0;
}
}
</style>
3、在以下文件夹下建立新文件
javascript
// 导入
import request from '@/utils/http'
// 获取Category页面数据,传参为id
export const getCategoryAPI = (id) => {
return request({
url:'/category',
params:{
id
}
})
}
4、运行结果
三、实现Banner轮播图(默认参数,传入参数)
1、修改Home中访问轮播图接口(主页为1,商品页为2)
javascript
// params = {} : 默认传入的是空对象
export function getBannerAPI(params = {}){
// 如果params中没有distributionSite的值默认是1,反之distributionSite的值为传入的值
const { distributionSite = '1'} = params
return httpInstance({
url:'/home/banner',
params:{
distributionSite
}
})
}
2、修改Categorize的页面代码
html
<script setup>
import {getBannerAPI} from '@/apis/Home.js'
import {getCategoryAPI} from '@/apis/Category'
import { onMounted, ref } from 'vue'
/**
* 面包屑
*/
// 引入useRoute
import { useRoute } from 'vue-router'
const categoryData = ref({})
const route = useRoute()
const getCategory = async () => {
// 使用route.params.id获取路由传递的参数,并传给接口
const res = await getCategoryAPI(route.params.id)
categoryData.value = res.result
}
onMounted(()=>getCategory())
/**
* banner轮播图
*/
// 设置响应式数据bannerList
const bannerList = ref([])
// 访问接口,将获取到的数据赋值给bannerList
const getBanner = async () => {
const res = await getBannerAPI({
distributionSite:'2'
})
bannerList.value = res.result
}
// 挂载时调用方法
onMounted(()=>getBanner())
</script>
<template>
<div class="top-category">
<div class="container m-top-20">
<!-- 面包屑 -->
<div class="bread-container">
<el-breadcrumb separator=">">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>{
{ categoryData.name }}</el-breadcrumb-item>
</el-breadcrumb>
</div>
<!-- 轮播图 -->
<div class="home-banner">
<el-carousel height="500px">
<!-- 将接口返回的数据,渲染在页面上 -->
<el-carousel-item v-for="item in bannerList" :key="item.id">
<img :src="item.imgUrl" alt="">
</el-carousel-item>
</el-carousel>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.top-category {
h3 {
font-size: 28px;
color: #666;
font-weight: normal;
text-align: center;
line-height: 100px;
}
.sub-list {
margin-top: 20px;
background-color: #fff;
ul {
display: flex;
padding: 0 32px;
flex-wrap: wrap;
li {
width: 168px;
height: 160px;
a {
text-align: center;
display: block;
font-size: 16px;
img {
width: 100px;
height: 100px;
}
p {
line-height: 40px;
}
&:hover {
color: $xtxColor;
}
}
}
}
}
.ref-goods {
background-color: #fff;
margin-top: 20px;
position: relative;
.head {
.xtx-more {
position: absolute;
top: 20px;
right: 20px;
}
.tag {
text-align: center;
color: #999;
font-size: 20px;
position: relative;
top: -20px;
}
}
.body {
display: flex;
justify-content: space-around;
padding: 0 40px 30px;
}
}
.bread-container {
padding: 25px 0;
}
}
.home-banner {
width: 1240px;
height: 500px;
margin: 0 auto;
img {
width: 100%;
height: 500px;
}
}
</style>
3、运行截图
四、激活状态控制
1、发现问题
2、修改部分代码
使用RouteLink中的active-class属性即可实现
设置样式
3、运行截图
五、分类列表的实现
1、模板
html
<div class="sub-list">
<h3>全部分类</h3>
<ul>
<li v-for="i in categoryData.children" :key="i.id">
<RouterLink to="/">
<img :src="i.picture" />
<p>{
{ i.name }}</p>
</RouterLink>
</li>
</ul>
</div>
<div class="ref-goods" v-for="item in categoryData.children" :key="item.id">
<div class="head">
<h3>- {
{ item.name }}-</h3>
</div>
<div class="body">
<GoodsItem v-for="good in item.goods" :good="good" :key="good.id" />
</div>
</div>
2、引入GoodItem
javascript
import GoodsItem from '@/views/Home/componments/GoodsItem.vue'
3、运行截图
六、路由缓存问题
1、发现问题
运行项目时可以发现,当点击"服饰"和"母婴",页面并没有变化
这是因为当使用带有参数的路由时,相同的组件实例将被复用,使其更高效,但与此同时,组件的生命周期不会被调用,页面数据不会更新
2、解决思路
1、让组件实例不复用,强制销毁重建
可以用:key,:key常常与v-for联合使用,但是它也可以用于强制替换一个元素或者钩子
运行结果
缺点
存在性能问题,比如说,上面的Banner在每个板块都是一样的,但是一切换,就需要访问banner的接口,影响性能
2、监听路由变化,变化后执行数据更新操作
使用onBeforeRouteUpdate导航钩子
javascript
onBeforeRouteUpdate钩子函数可以在每次路由更新之前执行,在回调中执行需要数据更新的业务逻辑即可
修改Categorize文件夹下index.vue文件
运行结果
3、问题小结
1、路由缓存问题产生的原因是什么?
路由只有参数变换时,会复用组件实例,但是生命周期不调用
2、两种方案都可以解决路由缓存问题,如何选择呢?
可以根据自己的实际开发环境进行抉择 1、key方法:简单粗暴,不考虑性能问题 2、onBeforeRouteUpdate方法:可以精细化控制,在意性能问题时可使用
七、使用逻辑函数拆分业务
1、概念
基于逻辑函数拆分业务是指把同一个组件中独立的业务代码通过函数做封装处理,提升代码的可维护性
2、步骤
javascript
1、按照业务声明以"use"打头的逻辑函数
2、把独立的业务逻辑封装到各个函数内部
3、函数内部把组件中需要用到的数据或者方法return方法
4、在组件中调用函数把数据或者方法组合回来使用
3、新建文件夹和文件
1、useBanner.js
javascript
/**
* banner轮播图
*/
import {getBannerAPI} from '@/apis/Home.js'
import {ref,onMounted} from 'vue'
export function useBanner(){
// 设置响应式数据bannerList
const bannerList = ref([])
// 访问接口,将获取到的数据赋值给bannerList
const getBanner = async () => {
const res = await getBannerAPI({
distributionSite:'2'
})
bannerList.value = res.result
}
// 挂载时调用方法
onMounted(()=>getBanner())
return {
bannerList
}
}
2、useCategory.js
javascript
import {getCategoryAPI} from '@/apis/Category'
import { ref,onMounted } from 'vue'
import { onBeforeRouteUpdate } from 'vue-router'
// 引入useRoute
import { useRoute } from 'vue-router'
/**
* 面包屑
*/
export function useCategory(){
const categoryData = ref({})
const route = useRoute()
// 默认id为route.params.id
const getCategory = async (id = route.params.id) => {
// 使用route.params.id获取路由传递的参数,并传给接口
const res = await getCategoryAPI(id)
categoryData.value = res.result
}
onMounted(()=>getCategory())
// 路由发生变化时,可以将分类数据重新获取
onBeforeRouteUpdate((to)=>{
// 由于由于route.params.id获取到的参数具有一定的滞后性,所以通过"to"获取实时路由参数
getCategory(to.params.id)
})
return {
categoryData
}
}
3、index.vue
javascript
<script setup>
// 引入js
import GoodsItem from '@/views/Home/componments/GoodsItem.vue'
import { useCategory } from '@/views/Categorize/composables/useCategory'
import {useBanner} from '@/views/Categorize/composables/useBanner'
const { bannerList } = useBanner()
const { categoryData } = useCategory()
</script>