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

Vue3+Pinia+Koa+Three.js 全栈电商项目总结复盘

前言

前几天一个朋友去义乌旅游,带回来很多小商品,就是一整个物美价廉,但是为什么线下购物和网购有的时候差别这么大(网购经常要退换货啊😭😭😭),为此我萌生了一个想法,3D是不是就可以实现在线看商品的细节了,退换货这么麻烦是不是可以省省了😏

一、项目概述

这个项目是对义务购app的一个模仿,相对于其官方app,我新增的亮点如下:

  • 商品排列布局使用瀑布流布局

  • 实现3D看商品功能

  • 实现3D看义乌商贸城
    同时,基础功能如下:

  • 使用 MySQL 实现登录注册的功能

  • 使用 MySQL 实现商品搜索功能

  • 使用 MySQL 实现对用户的购物车及收货地址的增删改查功能

  • 技术栈:Vue3 + Pinia + Three.js + Koa

二、项目展示

  1. 首页
    在这里插入图片描述

  2. 商品展示
    在这里插入图片描述

  3. 圈子
    在这里插入图片描述

  4. 商品搜索
    在这里插入图片描述

  5. 购物车+地址管理
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/917ac781c6b645b2a0e92c7ac029e6c3.png

三、项目思路

  1. 登录采用 sessionStorage 做数据持久化,保存当前账号的登录状态,在登录的时候向后端发起接口请求,将当前账号的数据返回给前端。
  2. 商品搜索历史采用 localStorage 做数据持久化,保存当前账号的搜索历史,在搜索的时候向后端发起接口请求,将当前账号的数据返回给前端。
  3. 对于官方的商品展示以及商城的内部,增加一个3D 预览模块。
  4. 将不同的页面封装成一个组件,然后通过 Vue-router 对路由进行集中管理,实现不同商品页面展示不同商品。
  5. 借助 Pinia 保存横向导航栏(商品种类)的 id ,购物车数量角标,glb文件的路径。

四、项目主体结构

客户端目录结构:

  • client
    • public //商品3D模型
      • draco
      • model
    • src
      • api //自己封装的axios用于响应拦截
      • assets //图片及基本css的初始化
      • components //组件
      • router //路由配置
      • store //仓库
      • views //页面

服务端目录结构:

  • server
    • config //mysql配置文件
    • controllers //控制器
    • data //商品数据
    • routes //路由客户端目录结构:
  • client
    • public //商品3D模型
      • draco
      • model
    • src
      • api //自己封装的axios用于响应拦截
      • assets //图片及基本css的初始化
      • components //组件
      • router //路由配置
      • store //仓库
      • views //页面

服务端目录结构:

  • server
    • config //mysql配置文件
    • controllers //控制器
    • data //商品数据
    • routes //路由客户端目录结构:
  • client
    • public //商品3D模型
      • draco
      • model
    • src
      • api //自己封装的axios用于响应拦截
      • assets //图片及基本css的初始化
      • components //组件
      • router //路由配置
      • store //仓库
      • views //页面

服务端目录结构:

  • server
    • config //mysql配置文件
    • controllers //控制器
    • data //商品数据
    • routes //路由

五、前端实现

  • UI组件库:Vant
  • 移动端适配:lib-flexible
  • CSS预处理器:less
  • 滚动:BetterScroll

1. 组件

众所周知,组件可以省去很多代码的编写,这个项目中我将头部导航栏,底部导航栏,商品瀑布流布局做成组件便于引用。这里我主要介绍下头部导航栏及商品瀑布流布局的实现。

(1) 头部导航栏(对不同类别的商品的展示)
实现过程:后端数据中每个类别的商品数据都包含id这个字段,我将导航栏的每个类别的id和后端给的id对应起来,并将这个id存储在pinia仓库中,这样只要在页面用watch监听仓库id的变化去向后端请求相应类别的数据即可。
在这里插入图片描述
(2) 商品瀑布流布局(提供更好的用户体验)
实现过程:利用flex布局,它可以实现两栏以上的瀑布流布局,我这里是两栏瀑布流布局,故将父容器设置为弹性容器,子容器为两个弹性容器,将这两个子容器的排列方向设置为垂直排列,并用flex:1;两列平分区域占满整个视窗。
在这里插入图片描述

2. 仓库

仓库的出现让我们可以在不同的页面进行数据共享,简直不要太爽,再也不用担心跨组件通信了!

这里简单介绍一下购物车角标的实现:
因为添加或删除商品,购物车角标将立即更新,不管是在主页还是购物车页面还是商品详情页面,角标都得实时更新它的数值,我们将变量值、更新角标重新获取购物车数据的方法定义在仓库中,这样在页面就可以直接引入并使用就好啦~

import {defineStore} from 'pinia'
import axios from 'axios'const useCartStore=defineStore('cart',{state:()=>{  return{badge:0   //响应式数据badge}},actions:{async changeBadge(){const res =await axios.post('/cartList', {  //获取购物车数据username: JSON.parse(sessionStorage.getItem('userInfo')).username})this.badge=res.data.length}}
})export default useCartStore

3. 搜索模块

实现过程:利用localStorage对搜索的词进行数据持久化,这样就能方便的从localStorage中拿到搜索的历史词段,并将其传给后端使用mysql检索相应的数据,并可以对其进行删除(也就是清除历史记录)

后面发现的一个小优化:逛淘宝发现我啥都不输入点击搜索可以搜索默认的字段,那还不简单?这只需要发一次接口请求将默认字段传给后端即可啦~

4. 3D商品预览

使用 Three.js 将引入的商品模型放入页面中,项目中模型来源于此:sketchfab.com[1]

由于模型的展示是通过点击商品图片后,以 遮罩层 + 动画 的形式呈现出来,不同商品展示不同模型,我们将其做成一个组件便于引用。部分代码如下:

import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { ref, onMounted } from 'vue';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import { useRoute } from 'vue-router';const route = useRoute()
const { id } = route.paramsconst canvasDom = ref(null)//场景
const scene = new THREE.Scene()
//渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true, setAlpha: true })  //setAlpha让其可设置透明度
renderer.setSize(window.innerWidth, window.innerHeight)
//镜头
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
camera.position.set(10, 10, 10)
camera.lookAt(0, 0, 0)
const controls = new OrbitControls(camera, renderer.domElement)// 渲染函数
const render = () => {renderer.render(scene, camera)controls.update()requestAnimationFrame(render)
}onMounted(() => {//渲染canvasDom.value.appendChild(renderer.domElement)// 设置背景颜色并启用透明度renderer.setClearColor(0x000000, 0.2);render()//网格地面const gridHelper = new THREE.GridHelper(80)gridHelper.material.transparent = truegridHelper.material.opacity = 0scene.add(gridHelper)//加载gltf模型const loader = new GLTFLoader()const dracoLoader = new DRACOLoader()dracoLoader.setDecoderPath('../../public/draco/gltf/')loader.setDRACOLoader(dracoLoader)loader.load(`../../public/model/${id}.glb`, (gltf) => {  //传id让其点击不同商品展示不同模型 id对应商品的id// console.log(gltf.scene);const bmw = gltf.scenebmw.scale.set(0.2, 0.2, 0.2); //模型缩放scene.add(bmw) //将整个模型组添加到场景中})});//洒满灯光
const light = new THREE.DirectionalLight(0xffffff, 1)
light.position.set(0, 0, 10)
scene.add(light)
const light2 = new THREE.DirectionalLight(0xffffff, 1);
light2.position.set(0, 0, -10);
scene.add(light2);
const light3 = new THREE.DirectionalLight(0xffffff, 1);
light3.position.set(10, 0, 0);
scene.add(light3);
const light4 = new THREE.DirectionalLight(0xffffff, 1);
light4.position.set(-10, 0, 0);
scene.add(light4);
const light5 = new THREE.DirectionalLight(0xffffff, 1);
light5.position.set(0, 10, 0);
scene.add(light5);
const light6 = new THREE.DirectionalLight(0xffffff, 0.3);
light6.position.set(5, 10, 0);
scene.add(light6);
const light7 = new THREE.DirectionalLight(0xffffff, 0.3);
light7.position.set(0, 10, 5);
scene.add(light7);
const light8 = new THREE.DirectionalLight(0xffffff, 0.3);
light8.position.set(0, 10, -5);
scene.add(light8);
const light9 = new THREE.DirectionalLight(0xffffff, 0.3);
light9.position.set(-5, 10, 0);
scene.add(light9);

5. 商贸城3D预览

实现过程与3D商品预览类似,我们只需用这段代码将全景图作为场景的背景图即可(全景图的资源路径存在仓库中):

cubeTextureLoader.load(store.loadUrl, (texture) => {const crt = new THREE.WebGLCubeRenderTarget(texture.image.height)crt.fromEquirectangularTexture(renderer, texture)  //把全景图转换为纹理格式scene.background = crt.texture})

六、后端实现

使用 Koa 框架搭建后端开发环境,后端分为四块:

  • 配置文件:对mysql的配置
  • 路由:定义接口请求路径及响应体
  • 控制器:当接口被请求时,需要向前端响应的操作,即数据库的增删改查
  • 数据:后端提供给前端的数据

数据库中创建了三个表:

  • users表:存储用户账号密码
  • cart表: 存储用户的购物车信息
  • address表: 存储用户的地址信息

这里以登录注册模块为例,路由代码如下:

const router = require('koa-router')()
//引入抛出的对象里的方法
const userService = require('../controllers/mySqlController.js')router.prefix('/users')//登录接口
router.post('/login', async (ctx, next) => {console.log(ctx.request.body);const { username, password } = ctx.request.body//去读取数据库中的users表,判断读取到的值和前端传过来的值是否匹配try {const result = await userService.userLogin(username, password)console.log(result);if (result.length) {let data = {id: result[0].id,username: result[0].username}ctx.body = {code: '80000',data: data,msg: '登陆成功'}} else {ctx.body = {code: '80004',data: 'error',msg: '账号或密码错误'}}} catch (error) {ctx.body = {code: '80002',data: error,msg: '服务器异常'}}
})//注册接口
router.post('/register', async (ctx, next) => {const { username, password } = ctx.request.body//判断账号或密码是否为空if (!username || !password) {ctx.body = {code: '80001',msg: '账号或密码不能为空'}return}//判断该账号是否在数据库中存在try {let findres = await userService.userfind(username)if (findres.length) {  //如找到数据则向前端报错ctx.body = {code: '80003',data: 'error',msg: '用户名已存在!'}} else {  //如没找到则注册成功,往数据库添加这条数据await userService.userRegister([username, password]).then(res => {// console.log(res);if (res.affectedRows !== 0) {ctx.body = {code: '80000',data: 'success',msg: '注册成功!'}} else {ctx.body = {code: '80004',data: 'error',msg: '注册失败!'}}})}} catch (error) {ctx.body = {code: '80002',data: error,msg: '服务器异常'}}
})
module.exports = router

七、总结

这个项目让我对Vue3这个框架的使用更熟练了,整个过程中也遇到了很多bug及问题,以前我是一个很怕代码出bug的小白,一遇到问题就问别人哈哈哈,这个项目让我学会了怎样一步一步寻找错误并分析原因,现在自己能够解决大多数的bug,也回顾了很多基础的js知识,体验了一把理论联系实践了。不过,这个项目还是有需要改进完善的地方,后续再见!

注意

自己维护会有点累,暂时先到者,喜欢的同学可以一起合作把他做成一个完整项目哈~

github:https://gitee.com/chao-diangen/vue3-pinia-koa-three.js

相关文章:

Vue3+Pinia+Koa+Three.js 全栈电商项目总结复盘

前言 前几天一个朋友去义乌旅游,带回来很多小商品,就是一整个物美价廉,但是为什么线下购物和网购有的时候差别这么大(网购经常要退换货啊😭😭😭),为此我萌生了一个想法&…...

【大模型AIGC系列课程 2-3】动手为ChatGPT打造第二大脑

文本向量的应用 one-hot 文本向量 !pip install jiebaimport jieba # 中文分词包text = 6月27日,世界经济论坛发布了《2023年10大新兴技术》报告。重点介绍了在未来3—5年对全球经济、工作、生活、医疗等产生积极影响的创新技术。其中,生成式AI首次入选并排名第2位。世界经…...

【ARM AMBA AXI 入门 10 - AXI 总线 DATA信号与 STRB 信号之间的关系 】

文章目录 AXI STRB 信号 AXI STRB 信号 AXI总线是ARM公司设计的高性能处理器接口,其中STRB和DATA信号在AXI协议中有特殊的含义和关系。 DATA信号:在AXI中,DATA信号用于在读写操作中传输实际的数据。数据的大小可以根据AXI接口的位宽来变化&…...

软引用的使用场景-链路日志

我司自研的链路系统中的agent层记录日志时,使用的是异步打印日志的机制。异步打印会使用队列,现将待打印的日志对象,记录在队列中。 但这块的日志,为了不影响业务,例如不能因为链路记录的日志过多,导致业务…...

【java】【项目实战】[外卖七]手机短信开发

目录 一、发送短信 1 短信服务介绍 2 阿里云短信服务(个人现在不太好申请了) 2.1 介绍 2.2 注册账号 2.3 设置短信签名 2.4 设置短信模版 2.5 设置AccessKey 3 代码开发 3.1 导包 3.2 短信发送工具类SMSUtils 二、手机验证码登录 1 需求分析 …...

Web 开发 Django 模板

上次为大家介绍了 Django 的模型和自带的管理工具,有了这个工具就可以全自动地根据模型创建后台管理界面,以供网站管理者更方便的管理网站数据。有了网站数据,那怎么样更方便又好看的展示给用户看呢?目前流行的 Web 框架基本都采用…...

动态可编辑表单项

遇到的问题&#xff1a;业务需要用户输入对应的username以发送私信给指定对象 方案1-input 输入就完事了 缺陷&#xff1a;要输入&#xff0c;麻烦 <form><label for"recipient-name">发给&#xff1a;</label><input type"text"…...

【Docker入门第一篇】

Docker简介 Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言 并遵从 Apache2.0 协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。 容器是完全使…...

数据集收集列表(opencv,机器学习,深度学习)持续更新

opencv 车牌识别数据集 opencv 手写数字识别数据集 机器学习 印第安糖尿病 Pima Indians数据集 &#xff0c;下载地址 Boston波士顿房价数据集 &#xff0c;下载...

springboot整合rabbitmq发布确认高级

在生产环境中由于一些不明原因&#xff0c;导致 rabbitmq 重启&#xff0c;在 RabbitMQ 重启期间生产者消息投递失败&#xff0c;导致消息丢失&#xff0c;需要手动处理和恢复。于是&#xff0c;我们如何才能进行 RabbitMQ 的消息可靠投递。 发布确认 发布确认方案 架构 配置…...

【linux命令讲解大全】010. mapfile命令和tempfile命令的用法及示例

文章目录 mapfile概要主要用途选项参数返回值例子 tempfile补充说明tempfile 命令$$ 变量 从零学 python mapfile 从标准输入读取行并赋值到数组。 概要 mapfile [-d delim] [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array] 主要用途 …...

在 Python 中构建卷积神经网络; 从 0 到 9 的手绘数字的灰度图像预测数字

一、说明 为了预测从0到9的数字&#xff0c;我选择了一个基于著名的Kaggle的MNIST数据集的数据集。数据集包含从 <0> 到 <9> 的手绘图数字的灰度图像。在本文中&#xff0c;我将根据像素数据&#xff08;即数值数据&#xff09;和卷积神经网络预测数字。 二、 卷积…...

前端分页处理

页面中实现的分页效果&#xff0c;要么后端提供接口&#xff0c;每次点击下一页就调用接口&#xff0c;若不提供接口&#xff0c;分页得前端自己去截取。 方法一&#xff1a;slice方法 slice(参数1&#xff0c;参数2)方法是返回一个新的数组对象&#xff0c;左开右闭 参数1&…...

【C语言】位操作符的一些题目与技巧

初学者在学完位操作符之后&#xff0c;总是不能很好的掌握&#xff0c;因此这篇文章旨在巩固对位操作符的理解与使用。 有的题目可能会比较难以接受&#xff0c;但是看完一定会有收获 目录 位操作符&#xff1a;一些题目&#xff1a;不创建临时变量交换整数整数转换二进制中1的…...

爬虫逆向实战(二十二)--某恩数据电影票房

一、数据接口分析 主页地址&#xff1a;某恩数据 1、抓包 通过抓包可以发现数据接口是API/GetData.ashx 2、判断是否有加密参数 请求参数是否加密&#xff1f; 无请求头是否加密&#xff1f; 无响应是否加密&#xff1f; 通过查看“响应”模块可以发现&#xff0c;响应是…...

火山引擎发布自研视频编解码芯片

2023年8月22日&#xff0c;火山引擎视频云宣布其自研的视频编解码芯片已成功出片。经验证&#xff0c;该芯片的视频压缩效率相比行业主流硬件编码器可提升30%以上&#xff0c;未来将服务于抖音、西瓜视频等视频业务&#xff0c;并将通过火山引擎视频云开放给企业客户。 火山引…...

投递技术类简历的注意事项

简历修改的背景 作为程序员&#xff0c;随着工作年限的增加&#xff0c;要定期的去修改自己的简历中的工作项目&#xff0c;一方面可以促进自己复盘一下工作成果和个人成长&#xff0c;另外也能给自己换工作提供一个前置的便捷性。 注意事项 修改简历的时候有哪些需要注意的…...

每日一题——柱状图中最大的矩形

柱状图中最大的矩形 题目链接 用什么数据结构&#xff1f; 要得到柱状图中最大的矩形&#xff0c;我们就必须要知道对于每一个高度heights[i]&#xff0c;他所能勾勒出的矩形最大是多少&#xff08;即宽度最大是多少&#xff09;。 而对应到图上我们可以知道&#xff0c;要知…...

Banana Pi推出基于龙芯2K1000LA处理器的信创工业控制开发平台

Banana Pi推出基于龙芯2K1000LA处理器的信创工业控制开发平台&#xff1a;BPI-5202信创工业控制开发平台 BPI-5202 龙芯2K1000LA 信创工业控制开发平台 1.1 工控机的应用场景 物联网的狂潮&#xff0c;既是一场众多的计算机软硬件厂家&#xff08;也包括通讯方案和产品厂家&…...

springCloud整合Zookeeper的时候调用找不到服务

SpringCloud整合Zookeeper的时候调用找不到服务 首先&#xff0c;我们在注册中心注册了这个服务&#xff1a; 然后我们使用RestTemplate 调用的时候发现失败了&#xff1a;找不到这个服务&#xff1a; 找了很多资料发现这个必须要加上负载才行 BeanLoadBalanced //负载publi…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

GitHub 趋势日报 (2025年06月08日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

3403. 从盒子中找出字典序最大的字符串 I

3403. 从盒子中找出字典序最大的字符串 I 题目链接&#xff1a;3403. 从盒子中找出字典序最大的字符串 I 代码如下&#xff1a; class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中&#xff0c;新增了一个本地验证码接口 /code&#xff0c;使用函数式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…...

Git常用命令完全指南:从入门到精通

Git常用命令完全指南&#xff1a;从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...