当前位置: 首页 > ai >正文

【vue3】黑马程序员前端Vue3小兔鲜电商项目【五】

黑马程序员前端Vue3小兔鲜电商项目五一级分类页

image-20230622214952268

路由配置

配置路由

一级分类 URL:http://127.0.0.1:5173/categopry/1005000,一级分类页是通过 /category/categoryId 的方式控制跳转,所以我们需要让路由接收 id 参数,修改 src\router\index.js 文件:

routes: [{path: '/',component: Layout,children: [{path: '',component: Home,},{path: 'category/:id', // :id 动态接收分类id参数component: Category,}]}
]
修改导航区域链接
<RouterLink :to="`/category/${item.id}`">{{ item.name }}</RouterLink>

面包屑导航渲染

模板代码

修改 src\views\Category\index.vue 文件,添加以下模板代码:

<script setup></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>居家</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>
封装接口

获取二级分类页表,

import http from '@/utils/http'/*** @description: 获取分类数据 /category?id=10020* @param {*} id 分类id * @return {*}*/
export const getCategoryAPI = (id) => {return http({url: '/category',params: {id}})
}
渲染面包屑导航

image-20230622183941751

通过获取路由中传递的参数(分类 Id)获取分类数据,渲染到导航:

<script setup>
import {ref,onMounted} from 'vue'
import {getCategoryAPI} from '@/apis/category'
import {useRoute} from 'vue-router'const categoryData = ref({})
const route = useRoute()
const getCategoryData = async(id)=>{const res = await getCategoryAPI(id)console.log(res);categoryData.value = res.result
}onMounted(()=>{// 获取路由参数 id, useRoute() -> route 等价于this.$routegetCategoryData(route.params.id)
})</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>

分类Banner渲染

分类轮播图和首页轮播图的区别只有一个,接口参数不同,其余逻辑完成一致。

获取 Banner 数据的接口参数如下,我们可以通过传递不同的参数来区分分类轮播图和首页轮播图:

参数名位置类型必填说明
distributionSiteQueryString广告区域展示位置(投放位置 投放位置,1为首页,2为分类商品页) 默认是1
修改接口

我们需要对获取首页轮播图的接口进行修改,添加传递参数,让其适用于获取分类轮播图:

export function getBannerAPI(params={}) {// 默认为1 商品为2const {distributionSite='1'} = paramsreturn http({url: 'home/banner',params:{distributionSite}})
}

上述代码中,该语句是使用 ES6(ECMAScript 2015)中的解构赋值语法:

const {distributionSite='1'} = params

解构赋值的基本语法是使用花括号 {} 来表示一个对象,或使用方括号 [] 来表示一个数组,然后在里面使用变量名或数组下标等形式来提取值。在变量名后面使用等号 = 来设置默认值。

这行代码的意思是,如果调用该函数时不传递任何参数,distributionSite 默认值就会是字符串'1'。如果传递了params对象,但是params对象中没有distributionSite属性,也会将distributionSite默认值设为字符串'1'

渲染 Banner 模块

在 src\views\Category\index.vue 文件中,获取分类 Banner 数据,并添加轮播图模块:

<script setup>
import {ref,onMounted} from 'vue'
import {getCategoryAPI} from '@/apis/category'
import { getBannerAPI } from "@/apis/home";
import {useRoute} from 'vue-router'const categoryData = ref({})
const route = useRoute()
const getCategoryData = async(id)=>{const res = await getCategoryAPI(id)console.log(res);categoryData.value = res.result
}onMounted(()=>{// 获取路由参数 id, useRoute() -> route 等价于this.$routegetCategoryData(route.params.id)
})const bannerList = ref([])const getBanner = async () => {const res = await getBannerAPI({distributionSite:'2'})console.log(res)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><!-- Banner 轮播图 --><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;z-index: 98;img {width: 100%;height: 500px;}
}
</style>

导航激活设置分类列表渲染

当页面显示分类数据时,该分类在导航栏中的样式为激活样式:

image-20230622194603783

导航激活状态设置

RouterLink 组件默认支持激活样式显示的类名,只需要给 active-class 属性设置对应的类名即可。修改 src\views\Layout\components\LayoutHeader.vue 文件和 src\views\Layout\components\LayoutFixed.vue 文件:

<RouterLink active-class="active" :to="`/category/${item.id}`">{{ item.name }}</RouterLink>
分类数据模版

修改 src\views\Category\index.vue 文件,在【Banner 轮播图】模块下添加分类数据模板:

<!-- 分类数据 -->
<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>

在 script 中引入 GoodsItem 商品展示模块:

import GoodsItem from '../Home/components/GoodsItem.vue'

路由缓存问题

解决方案

在 Vue 中使用带有参数的路由时需要注意的是,当用户从 /users/johnny 导航到 /users/jolyne 时,相同的组件实例将被重复使用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。这也意味着组件的生命周期钩子不会被调用。

即我们在一级导航时出现的问题:一级分类的切换正好满足上面的条件,组件实例复用,导致分类数据无法更新。也就是当我们在【/category/1005000】居家页面时,点击【美食分类】页面的数据不会进行变化。

缓存问题:当路由 path 一样,参数不同的时候会选择直接复用路由对应的组件导致数据无法更新,解决方案:

  1. 让组件实例不复用,强制销毁重建

给 router-view 添加 key,破坏缓存

  1. 监听路由变化,变化之后执行数据更新操作

beforeRouteUpdate:钩子函数可以在每次路由更新之前执行,在回调中执行需要数据更新的业务逻辑即可

beforeRouteUpdate

在 src\views\Category\index.vue 文件中引入 onBeforeRouteUpdate 方法,通过给 getCategoryData 方法传递分类 Id 参数获取分类数据:

<script setup>
import { useRoute,onBeforeRouteUpdate } from 'vue-router'const categoryData = ref({})
const route = useRoute()
const getCategoryData = async (id) => {const res = await getCategoryAPI(id)console.log(res);categoryData.value = res.result
}//目标:路由参数变化的时候可以把分类数据接口重新发送
onBeforeRouteUpdate((to)=>{console.log("路由变化了");route.params.id 存在滞后性,无法及时获取路由参数//通过参数to目标路由对象获取路由参数console.log(to);getCategoryData(to.params.id)
})
</script>

基于业务逻辑的函数拆分

基于逻辑函数拆分业务是指把同一个组件中独立的业务代码通过函数做封装处理,提升代码的可维护性。基本思想:把组件内独立的业务逻辑通过 useXXX 函数做封装处理,在组件中做组合使用。

image.png

步骤如下:

  1. 按照业务声明以use打头的逻辑函数
  2. 把独立的业务逻辑封装到各个函数内部
  3. 函数内部把组件中需要用到的数据或者方法return出去
  4. 在组件中调用函数把数据或者方法组合回来使用
封装轮播图
封装 userBanner() 函数

新建 src\views\Category\composables\useBanner.js 文件,将 src\views\Category\index.vue 文件中关于 Banner 的部分剪切进来:

// 封装banner轮播图相关的业务代码
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};
}
修改原代码

修改 src\views\Category\index.vue 文件,导入封装好的 userBanner() 函数,通过解构获取 bannerList:

import { useBanner } from '@/views/Category/composables/useBanner'const { bannerList } = useBanner()
封装分类数据
封装 useCategory() 函数

新建 src\views\Category\composables\useCategory.js 文件,将 src\views\Category\index.vue 文件中关于 Banner 的部分剪切进来:

// 封装分类数据相关业务代码
import { ref, onMounted } from 'vue'
import { getCategoryAPI } from '@/apis/category'
import { useRoute, onBeforeRouteUpdate } from 'vue-router'export function useCategory() {const categoryData = ref({})const route = useRoute()const getCategoryData = async (id) => {const res = await getCategoryAPI(id)console.log(res);categoryData.value = res.result}onMounted(() => {// 获取路由参数 id, useRoute() -> route 等价于this.$routegetCategoryData(route.params.id)})//目标:路由参数变化的时候可以把分类数据接口重新发送onBeforeRouteUpdate((to) => {console.log("路由变化了");//route.params.id 存在滞后性,无法及时获取路由参数//通过参数to目标路由对象获取路由参数console.log(to);getCategoryData(to.params.id)})return { categoryData }
}
修改原代码

修改 src\views\Category\index.vue 文件,导入封装好的 useCategory() 函数,通过解构获取 categoryData:

import { useCategory } from '@/views/Category/composables/useCategory'const { categoryData } = useCategory()
http://www.xdnf.cn/news/2920.html

相关文章:

  • 问题排查:calss extends 后页面加载不出来(忘记加super),打包后不报错;遇到问题可以适当出去走一下,让脑子休息一下
  • AimRT 从零到一:官方示例精讲 —— 五、Parameter示例.md
  • WPF(Windows Presentation Foundation)的内容模型
  • 可视化图解算法: 判断是不是二叉搜索树(验证二叉搜索树)
  • SEO优化指南与实战技巧
  • centos安装部署配置kafka
  • Vue常用的修饰符有哪些有什么应用场景(含deep seek讲解)
  • 通用事件库IO多路复用技术选型与设计
  • 常见位运算总结
  • 塑料材料工程师简历模板
  • C#进阶学习(十七)PriorityQueue<TElement, TPriority>优先级队列的介绍
  • 阿里云服务器 篇十二:加入 Project Honey Pot 和使用 http:BL
  • 万象生鲜配送系统代码2025年4月29日更新日志
  • Java练习3
  • c语言的常用的预处理指令和条件编译
  • __proto__与prototype
  • 误在非开发分支上开发解决方案
  • LabVIEW实验室项目中使用类模块与仿真
  • Linux 怎么安装 Oracle Java 8
  • 通过logrotate和cronolog对日志进行切割
  • 什么是DNS缓存?怎么清理DNS缓存?
  • 网络安全攻防演练实训室建设方案
  • 9.idea中创建springboot项目
  • Next框架学习篇 ✅
  • Nginx部署与源码编译构建LAMP
  • Java基础 4.29
  • OpenJDK 1.8中-Xloggc参数下GC日志覆盖与追加模式深度解析
  • 软文发稿:媒体发稿的关键策略及实战价值
  • Android Studio中OpenCV应用详解:图像处理、颜色对比与OCR识别
  • 水污染检测数据集VOC+YOLO格式2487张4类别