首先,我们需要理清楚什么是一级分类,上一篇文章中,我们已经请求后台接口拿到了导航栏中不同商品分类相关的数据,那么,我们怎样编写代码来实现这样一个效果呢?当我们点击不同分类的时候,首页会渲染出该分类下的所有商品?在这里,就需要我们配置一级分类的路由,当我们点击每个分类的时候,拿到这个分类对应的id,然后携带着去向后台发起数据请求,然后在底下渲染出来。
详细步骤如下:
(1)配置占位符
{
path: 'category/:id',
component: Category
},
(2)点击跳转
<RouterLink active-class="active" :to="`/category/${item.id}`">
{{ item.name }}
</RouterLink>
2、一级分类-面包屑导航渲染
先来看下效果:在导航栏中,当我们点击某个商品分类的时候,例如,在这里点了“居家”
在底下会弹出这样一个效果,可以让用户看着更加舒服,
其实这个呢,就是面包屑,接下来,一起看看怎么实现吧?
首先,我们需要知道这个数据来自哪里?很显然是后端接口传过来的数据。那么,首先,我们就需要封装接口,去向后台请求这个数据。
(1)封装接口
export function getCategoryAPI (id) {
return request({
url: '/category',
params: {
id
}
})
}
(2)调用接口返回数据:
<script setup>
import { getCategoryAPI } from '@/apis/category'
import { tryOnMounted } from '@vueuse/core';
import { ref } from 'vue'
import {useRoute } from 'vue-router'
const route = useRoute()
const categoryData = ref([])
const getCategory = async () => {
const res = await getCategoryAPI(route.params.id)
categoryData.value = res.result
console.log(res)
}
tryOnMounted(()=>getCategory())
</script>
浏览器返回:
(3)模板渲染
3、一级分类-轮播图功能实现
通过观察整个项目,我们发现,一级分类下的轮播图和首页的轮播图结构是一模一样的,只是调用的接口不同,在这里,很显然,我们可以把之前首页的轮播图相关的逻辑代码直接拿过来复用,只需要修改一下参数即可。
(1)改造接口(apis/home.js)
export function getBannerAPI (params = {}) {
// 轮播图数据,1为首页, 2为商品分类页,默认为1
const { distributionSite = '1' } = params
return httpInstance({
url: '/home/banner',
params: {
distributionSite
}
})
}
(2)HomeBanner中获取数据
<script setup>
import { getBannerAPI } from '@/apis/home'
import { onMounted, ref } from 'vue'
const bannerList = ref([])
const getBanner = async () => {
const res = await getBannerAPI()
console.log(res)
bannerList.value = res.result
}
onMounted(() => getBanner())
</script>
查看接口是否发送成功:这里的接口是1
根据后台数据要求,更改参数设置,“2”为商品分类页
修改代码,这里的接口变成了2
把首页轮播图的相关代码拿过来:
<template>
<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>
</template>
样式代码也拿过来:
<style scoped lang='scss'>
.home-banner {
width: 1240px;
height: 500px;
position: absolute;
left: 0;
top: 0;
z-index: 98;
img {
width: 100%;
height: 500px;
}
}
</style>
保存之后,查看运行结果:
4、一级分类-激活状态控制和分类列表渲染
(1)激活状态显示:
直接添加类名即可
active-class="active"
(2)分类列表渲染
1)Category/index.vue中轮播图下面添加如下静态代码:
<div class="sub-list">
<h3>全部分类</h3>
<ul>
<li v-for="i in categoryData.children" :key="i.id">
<RouterLink :to="`/category/sub/${i.id}`">
<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" :goods="good" :key="good.id" />
</div>
</div>
2)V-for遍历已有数据
<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" :goods="good" :key="good.id" />
</div>
</div>
3)由于这里每个商品分类下的模块使用到了GoodsItem中的模板,所以需要将其引入。
import GoodsItem from '../Home/components/GoodsItem.vue'
5、一级分类-路由缓存问题解决
首先,来看看什么是路由缓存问题?为什么会出现这个问题?
由于路由在只有参数变化时,会复用组件实例,会导致生命周期函数不执行,这个问题体现在哪里呢?当我们从导航栏中的某一个商品分类切换到下一个分类的时候,我们可以看到这样一个问题:浏览器地址栏的数据确实在发生变化,导航栏中的下一个商品分类也确实被激活了,但是分类下的数据根本就没有发生变化,这样,很显然是不行的,那么,我们怎样去解决这个问题呢?既然是组件实例被复用了,那么,我们就寻思让组件实例不再复用。很显然,这种方法是可行的,来看一下实现过程。
<!-- 添加key,破坏复用机制,强制销毁重建 -->
<RouterView :key="$route.fullPath"/>
但是,这种方法虽然简单粗暴,设置之后,组件实例确实不复用了,但是,它也存在一个很严重的问题。
它把整个实例都销毁重建了。在浏览器我们看到这个效果(看最上面两行)
每次点击都会请求这两个接口
而在项目中,每个分类下面的轮播图是一样的,不需要重新请求数据,所以,这种方法仍然存在资源的浪费,那么,还有没有其它的办法呢?肯定是有的,只要思想不滑坡,办法总比困难多。beforeRouteUpdate这个导航钩子它提供了一种解决方案,它可以用来监听路由参数的变化,当路由参数发生变化时,重新向后台发送接口请求。
详细步骤如下:
(1)Category/index.vue
运行结果演示:
这样,当切换路由的时候,接口只有一个了。
6、一级分类-使用逻辑函数拆分业务
在代码实现之前,需要考虑以下三个问题:
什么是使用逻辑函数拆分业务?
为什么要使用逻辑函数来对业务进行拆分?
怎么拆分?
在项目开发中,我们会遇到这样一个问题。有一些独立的业务代码,为了方便后去维护它们,我们需要将其拆分出来,封装成一个独立的函数,后面需要维护哪一块代码,直接找到对应的函数来进行维护即可,这样做,可以答复提升工作效率。
实现步骤如下:
1)逻辑封装时一个拆分再组合的过程,函数以use打头,内部封装逻辑。
2)把index.vue中与banner和category相关的逻辑分别拆分出去放到Usebanner.js和useCategory.js中,注意最后要return出去
Usebanner.js
import { ref , onMounted} from 'vue'
import { getBannerAPI } from '@/apis/home'
export function useBanner() {
const bannerList = ref([])
const getBanner = async () => {
const res = await getBannerAPI({
distributionSite : '2'
})
console.log(res)
bannerList.value = res.result
}
onMounted(() => {
getBanner()
})
return {
bannerList
}
}
useCategory.js
import { ref,onMounted } from 'vue'
import { getCategoryAPI } from '@/apis/category'
import { onBeforeRouteUpdate } from 'vue-router'
import {useRoute } from 'vue-router'
const route = useRoute()
export function useCategory() {
const categoryData = ref([])
const getCategory = async (id= route.params.id) => {
const res = await getCategoryAPI(id)
categoryData.value = res.result
console.log(res)
}
onMounted(()=>getCategory())
//路由参数变化的时候,可以把分类数据接口重新发送
onBeforeRouteUpdate((to)=> {
console.log('路由变化了')
//存在的问题,使用最新的路由参数获取最新的分类数据
console.log(to)
getCategory(to.params.id)
})
return {
categoryData
}
}
3)在index.vue中导入解构使用