Vue3小兔仙电商项目实战
Vue3小兔仙电商项目实战
项目技术栈
- create-vue
- Pinia
- ElementPlus
- Vue3-Setup
- Vue-Router
- VueUse
项目规模
项目亮点:
- 基于业务逻辑的组件拆分思想
- 长页面吸顶交互实现
- SKU电商组件封装
- 图片懒加载指令封装
- 通用逻辑函数封装
- 面板插槽组件等业务通用组件封装
- 路由缓存问题处理
- 支付宝第三方支付
项目初始化
src目录调整
配置别名路径联想提示
什么是别名路径联想提示?
在编写代码的过程中,一旦输入 @/ ,webstorm就会立即 联想出src下的所有子目录和文件,统一文件路径访问不容易出错。
如何进行配置?
1、在项目的根目录下新增 jsconfig.json 文件
2、添加json格式的配置项,如下:
jsconfig.json
{"compilerOptions":{"baseUrl":"./","paths": {"@/*": ["src/*"]}}
}
注意:该配置只做联想提示,不能进行实际路径转换。
实际路径转换在vite.config.js文件中
emelentPlus 组件库按需引入
element-plus官网 :快速开始 | Element Plus
1、安装element-plus
npm install element-plus --save
2、安装自动导入插件
npm install -D unplugin-vue-components unplugin-auto-import
-D 表示安装到开发环境
3、然后对项目根目录下的 vite.config.js 进行配置
import { fileURLToPath, URL } from 'node:url'import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'// https://vitejs.dev/config/
export default defineConfig({plugins: [vue(),AutoImport({resolvers: [ElementPlusResolver()],}),Components({resolvers: [ElementPlusResolver()],}),],resolve: {alias: {'@': fileURLToPath(new URL('./src', import.meta.url))}}
})
4、使用element-plus 组件
<script setup>
defineProps({msg: {type: String,required: true}
})
</script><template><div ><el-button>Default</el-button></div>
</template><style scoped></style>
5、运行结果:
emelentPlus 主题定制
为什么需要主题定制?
因为小兔仙的主题色和elementPlus 默认的主题色存在冲突,通过定制主题让elementPlus 的主题色和小兔仙项目保持一致。
如何定制?
通过scss变量替换方案。
因为elementPlus 采用的是SCSS语言,为了方便变量替换,本项目也需要安装SCSS。
1、安装sass
npm i sass -D
2、准备定制化的样式文件
styles/element/index.scss
// styles/element/index.scss
/* 修改elementPlus 的主题色 */
@forward 'element-plus/theme-chalk/src/common/var.scss' with ($colors: ('primary': (// 主色'base': #27ba9b,),'success': (// 成功颜色'base': #1dc779,),'warning': (// 警告颜色'base': #ffb302,),'danger': (// 危险颜色'base': #e26237,),'error': (// 错误颜色'base': #cf4444,),),
);
3、自定导入配置
import { fileURLToPath, URL } from 'node:url'import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'// element-plus 按需引入
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'// https://vitejs.dev/config/
export default defineConfig({plugins: [vue(),AutoImport({resolvers: [ElementPlusResolver()],}),Components({// 1、配置elementplus 采用sass 样式配色系统resolvers: [ElementPlusResolver({importStyle:'sass'})],}),],resolve: {alias: {'@': fileURLToPath(new URL('./src', import.meta.url))}},css: {preprocessorOptions: {scss: {// 2、自动导入定制化样式文件进行样式覆盖additionalData: `@use "@/styles/element/index.scss" as *;`,},},},
})
如何验证主题是否替换成功?
执行结果:
axios 基础配置
1、安装axios
npm i axios
2、对axios进行配置
https.js
import axios from 'axios';// 网络请求基础封装
const http = axios.create({baseURL:'http://pcapi-xiaotuxian-front-devtest.itheima.net',timeout:5000
})// 请求拦截器
http.interceptors.request.use(config => {return config
},error => Promise.reject(error))// 响应拦截器
http.interceptors.response.use(res => {console.log("response",res)return res.data;
},error => {console.log("response",error)return Promise.reject(error)
})export default http
apis/index.js
import http from "@/utils/http";/*** 获取分类* @returns {*}*/
export function getCategory() {return http({url:"home/category/head"})}
测试接口请求
// 测试接口是否可以获取到数据
import { getCategory } from '@/apis/index'getCategory().then(res => {console.log("getCategory-res",res)
}).catch(err => {console.log("getCategory-err",err)
})
执行结果:
如果项目里面不同的业务模块需要的接口基地址不同,该如何操作?
axios.create()方法可以执行多次,每次执行就会生成一个新的实例,比如:
// 网络请求基础封装
const http = axios.create({baseURL:'http://pcapi-xiaotuxian-front-devtest.itheima.net',timeout:5000
})// 基础地址2
const http2 = axios.create({baseURL:'http//xxx.xxx.com',timeout:5000
})
项目路由设计
设置首页和登录页的路由(一级路由)
路由设计原则:找内容切换的区域,如果是页面整体切换,则为一级路由。
创建Login.vue登录页面和Layout.vue 首页面
配置路由
设置一级路由出口
执行结果:
设置分类页和默认Home页路由(二级路由)
路由设计原则:找内容切换的区域,如果是在 一级路由页的内部切换 ,则为二级路由。
创建Home.vue和Category.vue页面
配置二级路由
设置二级路由出口
执行结果如下:
总结:
1、路由设计的依据是什么?
内容切换的方式
2、默认二级路由如何进行设置?
将path属性值设置为空
path:''
项目静态资源初始化
静态资源分为图片资源和样式资源。
- 图片资源。把 images 文件夹放到 assets 目录下
- 样式资源。把common.scss 文件夹放到 styles 目录下
SCSS文件自动导入
为什么要自动导入?
在项目中一些组件共享的色值会以scss变量的方式统一放到一个名为var.scss的文件中,正常组件中使用时需要先导入 scss 文件,然后再使用变量,比较繁琐,自动导入 可以免去手动导入的步骤,直接使用内部变量,如下图所示:
如何才能自动导入?
1、新建一个var.scss文件,存入色值变量。
2、然后通过 vite.config.js 配置自动导入文件。
var.scss文件
配置自动导入
@use "@/styles/var.scss" as *;
测试是否生效
Layout组件
静态模版结构搭建
编写Nav.vue、Header.vue、Fooler.vue组件
导入上述组件
执行结果:
字体图标引入
项目中使用的字体图标来自阿里图库,采用的是 font-class 引用的方式
font-class 引入步骤参见:iconfont-阿里巴巴矢量图标库
在线css文件如何获取?
1、首先搜索需要的图标
2、然后将指定的图标加入到购物车
3、然后点击购物车,添加到项目
4、然后选择font class, 然后点击生成代码
然后就得到在线css文件地址
在线css如何引用?
只需要在根目录下的index.html中引入即可
然后挑选相应图标并获取类名,应用于页面,如下代码:
<i class="iconfont icon-kefu"></i>
iconfont 为基础类名
icon-kefu 为选择的图标类名
执行结果:
一级导航渲染
使用后端接口渲染一级路由导航,如下截图:
执行结果:
吸顶导航交互实现
要求:当浏览器在上下滚动的过程中,如果距离顶部的滚动距离大于78px时,吸顶导航显示,小于78px隐藏,如下截图:
实现步骤:
- 准备吸顶导航组件 Fixed.vue
- 获取滚动距离 (通过VueUse 中的 useScroll 函数实现)
- 以滚动距离做判断条件控制组件盒子展示隐藏
Fixed.vue
<script setup>
import { ref,onMounted } from "vue"
import { getCategoryAPI } from '@/apis/layout'import { useScroll } from "@vueuse/core"const categoryData = ref([])const requestGetCategory = async () => {const res = await getCategoryAPI()console.log("requestGetCategory",res)categoryData.value = res.result;
}onMounted(() => {requestGetCategory()
})// 因为是基于window 滚动,所以传入的是window对象
const { y } = useScroll(window)</script><template><div class="app-header-sticky" :class="{ show: y > 78 }"><div class="container"><RouterLink class="logo" to="/" /><!-- 导航区域 --><ul class="app-header-nav"><li class="home"><RouterLink to="/">首页</RouterLink></li><liclass="home"v-for="item in categoryData":key="item.id"><RouterLink active-class="active" :to="`/category/${item.id}`">{{item.name}}</RouterLink></li></ul><div class="right"><RouterLink to="/">品牌</RouterLink><RouterLink to="/">专题</RouterLink></div></div></div>
</template><style scoped lang="scss">
.app-header-sticky {width: 100%;height: 80px;position: fixed;left: 0;top: 0;z-index: 999;background-color: #fff;border-bottom: 1px solid #e4e4e4;// 此处为关键样式!!!// 状态一:往上平移自身高度 + 完全透明transform: translateY(-100%);opacity: 0;// 状态二:移除平移 + 完全不透明&.show {transition: all 0.3s linear;transform: none;opacity: 1;}.container {display: flex;align-items: center;}.logo {width: 200px;height: 80px;background: url("@/assets/images/logo.png") no-repeat right 2px;background-size: 160px auto;}.right {width: 220px;display: flex;text-align: center;padding-left: 40px;border-left: 2px solid $xtxColor;a {width: 38px;margin-right: 40px;font-size: 16px;line-height: 1;&:hover {color: $xtxColor;}}}
}.app-header-nav {width: 820px;display: flex;padding-left: 40px;position: relative;z-index: 998;li {margin-right: 40px;width: 38px;text-align: center;a {font-size: 16px;line-height: 32px;height: 32px;display: inline-block;&:hover {color: $xtxColor;border-bottom: 1px solid $xtxColor;}}.active {color: $xtxColor;border-bottom: 1px solid $xtxColor;}}
}
</style>
Pinia 优化重复请求
为什么要优化?
因为Fixed.vue组件和Header.vue组件中,当DOM加载完成以后都会调用分类接口获取数据,发送了两次网络请求,比较浪费资源,可以通过Pinia 集中管理数据,再把数据给组件使用。
Fixed.vue
Header.vue
如何优化?
1、通过pinia 对导航分类数据进行管理
import { defineStore } from "pinia"
import { getCategoryAPI } from "@/apis/layout"
import { ref } from "vue"/*** 导航分类的数据管理* @type*/
export const useCategoryStore = defineStore("category", () => {// 导航列表的逻辑const categoryList = ref()const getCategory = async () => {const res = await getCategoryAPI()categoryList.value = res.result}return {categoryList,getCategory,}
})
2、然后在吸顶导航组件和常规导航组件的父容器Layout组件中触发请求
<script setup>
import { onMounted } from 'vue';
import Nav from "./components/Nav.vue"
import Header from "./components/Header.vue"
import Footer from "./components/Footer.vue"
import Fixed from "./components/Fixed.vue"
import { useCategoryStore } from '@/stores/category.js'const categoryStore = useCategoryStore()onMounted(() => {// 触发获取导航列表的actioncategoryStore.getCategory()
})</script><template><Fixed /><Nav /><Header /><RouterView /><Footer />
</template>
3、在吸顶导航组件化和常规导航组件中使用pinia 中的分类数据
<script setup>
import { useCategoryStore } from "@/stores/category";
import { useScroll } from "@vueuse/core"// 因为是基于window 滚动,所以传入的是window对象
const { y } = useScroll(window)// 使用pinia获取分类数据
const categoryStore = useCategoryStore()</script><template><div class="app-header-sticky" :class="{ show: y > 78 }"><div class="container"><RouterLink class="logo" to="/" /><!-- 导航区域 --><ul class="app-header-nav"><li class="home"><RouterLink to="/">首页</RouterLink></li><liclass="home"v-for="item in categoryStore.categoryList":key="item.id"><RouterLink active-class="active" :to="`/category/${item.id}`">{{item.name}}</RouterLink></li></ul><div class="right"><RouterLink to="/">品牌</RouterLink><RouterLink to="/">专题</RouterLink></div></div></div>
</template><style scoped lang="scss">
.app-header-sticky {width: 100%;height: 80px;position: fixed;left: 0;top: 0;z-index: 999;background-color: #fff;border-bottom: 1px solid #e4e4e4;// 此处为关键样式!!!// 状态一:往上平移自身高度 + 完全透明transform: translateY(-100%);opacity: 0;// 状态二:移除平移 + 完全不透明&.show {transition: all 0.3s linear;transform: none;opacity: 1;}.container {display: flex;align-items: center;}.logo {width: 200px;height: 80px;background: url("@/assets/images/logo.png") no-repeat right 2px;background-size: 160px auto;}.right {width: 220px;display: flex;text-align: center;padding-left: 40px;border-left: 2px solid $xtxColor;a {width: 38px;margin-right: 40px;font-size: 16px;line-height: 1;&:hover {color: $xtxColor;}}}
}.app-header-nav {width: 820px;display: flex;padding-left: 40px;position: relative;z-index: 998;li {margin-right: 40px;width: 38px;text-align: center;a {font-size: 16px;line-height: 32px;height: 32px;display: inline-block;&:hover {color: $xtxColor;border-bottom: 1px solid $xtxColor;}}.active {color: $xtxColor;border-bottom: 1px solid $xtxColor;}}
}
</style>
Home组件
整体结构搭建和分类实现
相关文章:

Vue3小兔仙电商项目实战
Vue3小兔仙电商项目实战 项目技术栈 create-vuePiniaElementPlusVue3-SetupVue-RouterVueUse 项目规模 项目亮点: 基于业务逻辑的组件拆分思想 长页面吸顶交互实现SKU电商组件封装图片懒加载指令封装通用逻辑函数封装面板插槽组件等业务通用组件封装路由缓存问题…...
MATLAB基础应用精讲-【数模应用】肯德尔协调系数(附MATLAB、R语言和python代码实现)
目录 前言 几个高频面试题目 肯德尔协调系数低原因? 知识储备 相关性分析对比 1 相关分析 2 Cochrans Q 检验 3 Kappa一致性检验 4 Kendall协调系数 5 组内相关系数 算法原理 数学模型 SPSSPRO:Kendall一致性检验 1、作用 2、输入输出描述 3、案例示例 4、案…...

计算函数(c语言)
1.描述 //小乐乐学会了自定义函数,BoBo老师给他出了个问题,根据以下公式计算m的值。 // //其中 max3函数为计算三个数的最大值,如: max3(1, 2, 3) 返回结果为3。 //输入描述: //一行,输入三个整数ÿ…...

Linux 7 x86平台上安装达梦8数据库
1、环境描述 2、安装前准备 2.1 操作系统信息调研 Linux平台需要通过命令查看操作系统版本、位数、磁盘空间、内存等信息。 CPU信息 [rootray1 ~]# cat /proc/cpuinfo | grep -E "physical id|core id|cpu cores|siblings|cpu MHz|model name|cache size"|tail -n…...

【老张的程序人生】我命由我不由天:我的计算机教师中级岗之旅
在计算机行业的洪流中,作为一名20年计算机专业毕业的博主,我深知这几年就业的坎坷与辉煌。今天,我想与大家分享我的故事,一段关于梦想、挑战与坚持的计算机教师中级岗之旅。希望我的经历能为大家提供一个发展方向,在计…...

1.Linux_常识
UNIX、Linux、GNU 1、UNIX UNIX是一个分时操作系统,特点是多用户、多任务 实时操作系统:来了请求就去解决请求 分时操作系统:来了请求先存着,通过调度轮到执行时执行 2、Linux Linux是一个操作系统内核 发行版本࿱…...
下载文件--后端返回文件数据,前端怎么下载呢
问题:有个功能是将tabel数据导出,并且后端写了个接口,这个接口返回你要下载的excel文件数据了。前端请求接口就行,然后下载下来,但前端该怎么操作(发起请求呢) /*** 导出文件* param {string} …...
CSS方向选择的艺术:深入探索:horizontal和:vertical伪类
CSS(层叠样式表)是构建网页视觉表现的核心工具。随着CSS规范的不断更新,我们拥有了更多的选择器来精确控制网页元素的样式。其中,:horizontal和:vertical伪类是CSS Level 4中引入的两个实验性选择器,它们允许开发者根据…...
探索PHP的心脏:流行CMS系统全解析
标题:探索PHP的心脏:流行CMS系统全解析 在数字化时代,内容管理系统(CMS)扮演着构建和维护网站的核心角色。PHP作为一种广泛使用的服务器端脚本语言,其强大的功能和灵活性使其成为开发CMS的首选。本文将详细…...

图片展示控件QGraphicsView、QGraphicsScene、QGraphicsItem的使用Demo
简介 /* * 图片展示控件 * Graphics View Framework的使用Demo * QGraphicsView、QGraphicsScene、QGraphicsItem的使用Demo * 支持图片的旋转与缩放,自动缩放至接触边框 */ 效果展示 坐标系示意图 Graphics View Framework的使用需要特别注意QGraphicsView、…...

C++仿C#实现事件处理
测试 #include "beacon/beacon.hpp" #include <cstdio> #include <thread>class mouseEvent : public beacon::args { public:mouseEvent(int x, int y) : x(x), y(y) {}int x, y; };class object : public beacon::sender { public:};class mouseHandl…...

SpringBoot-04--整合登录注册动态验证码
文章目录 效果展示1.导入maven坐标2.编写代码生成一个验证码图片3.前端如何拿到验证码4. 后端生成验证码5前端代码 效果展示 效果,每次进入页面展现出来不同的验证码。 技术 使用别人已经写好的验证码生成器,生成图片,转为Base64编码&#x…...
Qt如何打包桌面应用程序
Qt提供了一种便捷的方式来打包桌面应用程序,使其能够在不同操作系统上运行。以下是一些常用的打包工具和步骤: 1. **使用Qt Installer Framework**:Qt提供了一个名为Qt Installer Framework的工具,可以用来创建跨平台的安装程序。…...
AI作画提示词工程:技巧与最佳实践
在AI作画中,提示词工程(Prompt Engineering)是生成高质量图像的关键一步。以Midjourney为例,通过巧妙设计提示词,AI能够生成更符合预期的图像。本教程将分享如何有效利用提示词,掌握提示词的技巧与最佳实践…...

Ugandan Knuckles
目录 一、题目 二、思路 三、payload 四、思考与总结 一、题目 <!-- Challenge --> <div id"uganda"></div> <script>let wey (new URL(location).searchParams.get(wey) || "do you know da wey?");wey wey.replace(/[<…...
MVI、MVVM、MVP的对比
MVI 特点: 单向数据流:MVI采用单向数据流,从Model到View的数据流动,保证了数据流的可控性和可预测性。响应式编程:通过使用协程与RxJava等响应式编程库,简化了数据流的管理和处理。不可变性:MV…...

基于 Flutter 从零开发一款产品(一)—— 跨端开发技术介绍
前言 相信很多开发者在学习技术的过程中,常常会陷入一种误区当中,就是学了很多技术理论知识,但是仍做不出什么产品出来,往往学了很多干货,但是并无实际的用处。其实,不论是做什么,我们都需要从…...

React + Vite项目别名配置
Node版本:v20.16.0Vite版本:5.4.1 安装 types/node 依赖包 pnpm i types/node -D pnpm ls types/node配置 vite.config.js 文件: resolve: {alias: {"": join(__dirname, "./src/"),}, },使用配置好的别名 : 由上图我们…...
FFmpeg编译与配置 - Linux环境
Linux环境配置 环境:Ubuntu 22.04 step1. 首先下载安装依赖环境 更新软件源 sudo apt update下载依赖软件 sudo apt install \ autoconf \ automake \ build-essential \ cmake \ git-core \ libass-dev \ libfreetype6-dev \ libgnutls28-dev \ libsdl2-dev \…...
MyBatis-Plus 提供的一个通用服务层实现类
一、代码示例 Service public class CarriageServiceImpl extends ServiceImpl<CarriageMapper, CarriageEntity> implements CarriageService{Overridepublic List<CarriageDTO> findAll() {return List.of();} } 在这段代码中,CarriageServiceImpl …...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
用鸿蒙HarmonyOS5实现中国象棋小游戏的过程
下面是一个基于鸿蒙OS (HarmonyOS) 的中国象棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chinesechess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├──…...

算术操作符与类型转换:从基础到精通
目录 前言:从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符:、-、*、/、% 赋值操作符:和复合赋值 单⽬操作符:、--、、- 前言:从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...

java高级——高阶函数、如何定义一个函数式接口类似stream流的filter
java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用(Math::max) 2 函数接口…...
《Offer来了:Java面试核心知识点精讲》大纲
文章目录 一、《Offer来了:Java面试核心知识点精讲》的典型大纲框架Java基础并发编程JVM原理数据库与缓存分布式架构系统设计二、《Offer来了:Java面试核心知识点精讲(原理篇)》技术文章大纲核心主题:Java基础原理与面试高频考点Java虚拟机(JVM)原理Java并发编程原理Jav…...