原文博客地址

藤原拓鞋的博客

开始

Vue是目前前端领域教火的框架,被广泛应用,插件和依赖包生态强大,诞生了许多大型应用

Vue 官网

学习了 vue 之后,利用所学知识搭建了个简易的商城,用到了 vue 开发中常用的技巧以及扩展包,通过这个小项目加深了对 vue 的学习
但因为没有做对应的后台,所以用到了浏览器本地储存作为临时仓库的模拟,以达到计算商品剩余量的目的

同样,对于登录和注册功能,也是通过 localStorage 暂存用户名与密码,所以只能暂时有一个用户~~

后台系统的话,以后我会用 node 或者 django 搭建出来的,这里先给出本前端项目地址:
https://github.com/li-car-fei/vue_shopping

注册与登录

如上所述,本项目暂用 localStorage 存储用户名和密码,以达到登录和注册功能

首先,我们先需要配置路由
npm i vue-router --save 安装包,然后在 index.js 中引入并且使用:

import VueRouter from "vue-router";
import Routers from "./router";Vue.use(VueRouter);//路由配置
const RouterConfig = {//使用H5 history模式mode: "history",routes: Routers,
};const router = new VueRouter(RouterConfig);//跳转前设置title
router.beforeEach((to, from, next) => {window.document.title = to.meta.title;next();
});
//跳转后设置scroll为原点
router.afterEach((to, from, next) => {window.scrollTo(0, 0);
});

记得最后一定要在 new Vue() 是添加 router 路由器:

const app = new Vue({el: "#app",router, // 添加routerstore, // 添加vuex//render代替app声明render: (h) => {return h(App);},
});

配置的路由,写在了 router.js 中,如下:

//meta中存有title,用作跳转路由时改变title标题
const routers = [{path: "/list",meta: {title: "商品列表",},component: (resolve) => require(["./views/list.vue"], resolve),},{path: "/product/:id",meta: {title: "商品详情",},component: (resolve) => require(["./views/product.vue"], resolve),},{path: "/cart",meta: {title: "购物车",},component: (resolve) => require(["./views/cart.vue"], resolve),},{path: "/login/:loginStatus",meta: {title: "登录注册",},component: (resolve) => require(["./views/login.vue"], resolve),},{path: "*",redirect: "/login/login", //重定向路由至登录页面,且传入的loginStatus参数是 login},
];
export default routers;

想了解vue-router源码解析,可看我另一篇博客:vue-router 源码解析

注意两点:

  • 可通过设置默认路由跳转至登陆界面,使用 redirect 属性即可
  • 可能会 index.js 有疑问,大多数项目可能是 main.js 作为入口文件的文件名,但这是基于 webpack 配置的项目,修改 webpack 入口配置即可,文件名无关紧要

关于登录界面,最关键的是在 created 生命周期准确地判断状态

  • 退出登录的状态
  • localStorage 有用户信息,已登录的状态
  • 未登录或注册的状态
created(){//获取路由中的传来的参数loginStatusif(this.$route.params.loginStatus === 'logout'){//如果是退出登录跳转过来,清空缓存window.localStorage.clear();//直接commit触发mutations,设置state的loginstatus为falsethis.$store.commit('getLoginStatus', false);return;}//获取state中登录状态const loginStatus = this.$store.state.loginStatus;if(loginStatus){//如果是已经登录的用户,url跳到listthis.login = false;this.register = false;window.alert('您已经是登录状态')window.location.href = '/list'}}

关于登录注册界面的其他操作及参数,请自行查看,注释都较为清楚

商城主页

首先,我们介绍除了 router 以外,另一个全局共用的变量 vuex

vuex 可以将全局使用的变量进行统一管理,在 index.js 中 new Vue() 时进行注册即可,它可以帮组我们更好地管理全局共用的数据,防止出现多组件之间传变量,传方法的复杂应用

通过vuexdispatch,commit ,getters 机制,对状态进行统一管理,想了解vuex源码解析,可以看我的另一篇博客:vuex 源码解析

以下是 index.js 中配置的 vuex

//配置Vuex状态管理
const store = new Vuex.Store({state: {//商品列表信息productList: [],//购物车数据,数组形式,数据元素为对象(商品id,购买数量count)cartList: [],//当前用户账号username: window.localStorage.getItem("username"),//登录状态    !!转换为bool值loginStatus: !!window.localStorage.getItem("loginStatus"),},getters: {//返回品牌队列brands: (state) => {//获取所有product的brand并形成数组const brands = state.productList.map((item) => item.brand);//数组去重并返回return util.getFilterArray(brands);},//返回颜色队列colors: (state) => {//获取所有product的color并形成数组const colors = state.productList.map((item) => item.color);//数组去重并返回return util.getFilterArray(colors);},},//mutations更新state,更新视图mutations: {//初始化时,添加商品列表setProductList(state, data) {state.productList = data;},//添加商品到购物车addCart(state, obj) {const id = obj.id;//获取具体的product,以便修改其stocks库存let product = state.productList.find((item) => item.id === id);if (obj.inCart) {//此商品在购物车中,数量+1,对应库存-1let Added = state.cartList.find((item) => item.id === id);Added.count++;product.stocks--;} else {//此商品不在购物车中,加入到购物车中state.cartList.push({id: id,count: 1,});//对应库存-1product.stocks--;}},//修改购物车商品数量editCartCount(state, payload) {//浅拷贝,所以修改product中count可以直接修改carList,productList//先获取购物车与商品列表对应商品对象const product_L = state.productList.find((item) => item.id === payload.id);const product_C = state.cartList.find((item) => item.id === payload.id);//修改数量与对应库存product_L.stocks -= payload.count;product_C.count += payload.count;},//删除某个购物车商品deleteCart(state, id) {//获取购物车中此商品的数量,用于归还库存const count = state.cartList.find((item) => item.id === id).count;//findIndex,用于切割购物车列表const index = state.cartList.findIndex((item) => item.id === id);//获取对应的商品在productList中的对象const product = state.productList.find((item) => item.id === id);//修改库存product.stocks += count;//修改购物车列表state.cartList.splice(index, 1);},//添加评论addComment(state, comment_obj) {const product = state.productList.find((item) => item.id === comment_obj.product_id);console.log(product);const content = comment_obj.content;const username = state.username;product.comments.push({username: username,content: content,});},//清空购物车即购买,无需恢复库存emptyCart(state) {state.cartList = [];},//username是commit来的用户名,修改state中用户名getUser(state, username) {state.username = username;},//flag是commit过来的登录状态,修改state中的状态getLoginStatus(state, flag) {state.loginStatus = flag;},},actions: {//ajax请求商品列表,暂且使用setTimeoutgetProductList(context) {setTimeout(() => {//设置state.productList 为引入的product_datacontext.commit("setProductList", product_data);}, 150);},//购买buy(context) {//模拟ajax请求服务端响应后再清空购物车return new Promise((resolve) => {setTimeout(() => {context.commit("emptyCart");resolve("购买成功");}, 150);});},//添加商品到购物车add(context, id) {//首先查看登录状态,若未登录则跳转到登陆界面if (!context.state.loginStatus) {window.alert("请先登录,为您跳转到登录界面");//路由跳转window.location.href = "/login/login";return undefined;}//先查看购物车中是否有该商品const isAdded = context.state.cartList.find((item) => item.id === id);//查看对应商品的库存const stocks = context.state.productList.find((item) => item.id === id).stocks;return new Promise((resolve, reject) => {if (stocks) {//有库存,修改数据//如果购物车中不存在商品,设置count为1,存在count++if (isAdded) {context.commit("addCart", {inCart: true,id: id,});resolve("加一");} else {context.commit("addCart", {inCart: false,id: id,});resolve("加入购物车");}} else {//无库存,返回信息reject("库存不足");}});},//修改购物车数量editCart(context, payload) {//先查看对应商品的库存const stocks = context.state.productList.find((item) => item.id === payload.id).stocks;return new Promise((resolve, reject) => {//如果是+1,根据库存返回结果与修改if (payload.count > 0) {if (stocks) {//有库存context.commit("editCartCount", payload);resolve("修改成功(+1)");} else {//无库存reject("库存不足");}} else {//如果是-1,直接修改即可resolve("修改成功(-1)");context.commit("editCartCount", payload);}});},},
});

同样的,要记得在 new Vue() 的时候添加配置

以上,我们可以看到项目的基本流程,包括添加购物车,清空购物车等等
可以看到在 add(context, id) 方法中,需要先判断登录状态,再进行库存的判断,是否加购的判断,最终返回一个 Promise 对象或者跳转路由
同样的,在 editCart(context, payload) 方法中,也是返回一个 Promise 对象,判断库存然后resolve()或者reject()
而在商品组件添加商品和在购物车页面中修改数量时,通过如下方式调用:

//添加购物车
handleAddCart(){//增加1件此商品至购物车,返回一个Promisethis.$store.dispatch('add', this.id).then(text=>{//console.log(text)},error=>{window.alert(error)})},//修改购物车数量,返回一个PromisehandleCount(index, count){//判断是否只剩一件if(count < 0 && this.cartList[index].count === 1) return;this.$store.dispatch('editCart', {id: this.cartList[index].id,count: count}).then(text=>{//console.log(text)},error=>{window.alert(error)})},

同理,对于清空购物车的操作,也遵循上书方式

//清空购物车
handleOrder(){//由于dispatch buy action 返回一个promise对象,用then()调用this.$store.dispatch('buy').then(text => {window.alert(text);})},

主页中,还有颜色,品牌的筛选,有销量和价格的排序,我认为自已的实现方法是较为笨重的,设置了filterBrand,filterColor作为筛选品牌和颜色的数组,order作为排序选项,然后通过两层 forEach 进行筛选,最终得到筛选,排序后的商品数组。希望大家有更好的实现方案能够私聊我或者直接在评论区分享~~
list.vue 定义:

export default {//使用组件productcomponents: { Product },computed: {list() {//从Vuex获取商品列表信息return this.$store.state.productList;},brands() {//从getters中获取所有商品的品牌return this.$store.getters.brands;},colors() {return this.$store.getters.colors;},filteredAndOrderedList() {//拷贝原数组let list = [...this.list];//品牌过滤,分别forEach遍历productlist与品牌筛选队列,匹配的product暂存pushif (this.filterBrand.length) {let list1 = [];list.forEach((item) => {this.filterBrand.forEach((brand) => {if (item.brand == brand) {list1.push(item);}});});list = list1;}//颜色过滤,分别forEach遍历productlist与颜色筛选队列,匹配的product暂存pushif (this.filterColor.length) {let list1 = [];list.forEach((item) => {this.filterColor.forEach((color) => {if (item.color == color) {list1.push(item);}});});list = list1;}//排序if (this.order !== "") {if (this.order === "sales") {list = list.sort((a, b) => b.sales - a.sales);} else if (this.order === "cost-desc") {list = list.sort((a, b) => b.cost - a.cost);} else if (this.order === "cost-asc") {list = list.sort((a, b) => a.cost - b.cost);}}return list;},},data() {return {//品牌过滤选中的品牌filterBrand: [],//颜色过滤选中的颜色filterColor: [],//排序过滤选中的方式://cost-desc价格降序//cost-asc价格升序//sales销量order: "",};},methods: {//品牌筛选handleFilterBrand(brand) {//点击品牌过滤,再次点击取消let index = this.filterBrand.findIndex((item) => item == brand);if (index > -1) {//若已经筛选的品牌包含此品牌,则删除this.filterBrand.splice(index, 1);} else {//将此品牌加入到品牌筛选队列中this.filterBrand.push(brand);}},//颜色筛选handleFilterColor(color) {//点击颜色过滤,再次点击取消let index = this.filterColor.findIndex((item) => item == color);if (index > -1) {//若已经筛选的颜色包含此颜色,则删除this.filterColor.splice(index, 1);} else {//将此颜色加入到颜色筛选队列中this.filterColor.push(color);}},handleOrderDefault() {this.order = "";},handleOrderSales() {this.order = "sales";},handleOrderCost() {//当点击升序时将箭头更新↓,降序设置为↑if (this.order === "cost-desc") {this.order = "cost-asc";} else {this.order = "cost-desc";}},},mounted() {//初始化时通过Vuex actions获取商品列表信息this.$store.dispatch("getProductList");},
};

购物车

关于购物车中,有总价的计算,商品数量的改变等,实现较为简单,此处不作赘述,看代码即可:

export default {name: "cart",data() {return {promotion: 0, //优惠金额promotionCode: "",productList: product_data,};},computed: {//购物车数据cartList() {return this.$store.state.cartList;},//设置字典对象,方便模板使用productDictList() {const dict = {};this.productList.forEach((item) => {dict[item.id] = item;});return dict;},//购物车商品数量总数countAll() {let count = 0;this.cartList.forEach((item) => {count += item.count;});return count;},//购物车商品总价costAll() {let cost = 0;this.cartList.forEach((item) => {cost += this.productDictList[item.id].cost * item.count;});return cost;},},methods: {//通知Vuex,完成下单handleOrder() {//由于dispatch buy action 返回一个promise对象,用then()调用this.$store.dispatch("buy").then((text) => {window.alert(text);});},//验证优惠码,使用ccylovehs代替优惠券字符串handleCheckCode() {if (this.promotionCode === "") {window.alert("请输入优惠码");return;}if (this.promotionCode !== "ccylovehs") {window.alert("优惠码输入错误");return;}this.promotion = 500;},//修改购物车数量,返回一个PromisehandleCount(index, count) {//判断是否只剩一件if (count < 0 && this.cartList[index].count === 1) return;this.$store.dispatch("editCart", {id: this.cartList[index].id,count: count,}).then((text) => {//console.log(text)},(error) => {window.alert(error);});},//根据index查找商品id进行删除handleDelete(index) {this.$store.commit("deleteCart", this.cartList[index].id);},},
};

商品详情页

商品详情页较之以上操作,多了评论的操作,如下:

export default {data() {return {//获取路由中的参数idid: parseInt(this.$route.params.id),product: null,comments: [],user_comment: "",};},methods: {getProduct() {setTimeout(() => {//模拟ajax获取具体商品数据this.product = this.$store.state.productList.find((item) => item.id === this.id);this.comments = this.product.comments;}, 300);},handleAddCart() {//增加1件此商品至购物车,返回一个Promisethis.$store.dispatch("add", this.id).then((text) => {//console.log(text)},(error) => {window.alert(error);});},comment() {const content = this.user_comment;const id = this.id;this.$store.commit("addComment", {product_id: id,content: content,});this.user_comment = "";},},mounted() {//初始化数据this.getProduct();},
};

结语

总体讲述了此商城项目,感谢阅读

感兴趣地可以到 github 上 clone 此项目:https://github.com/li-car-fei/vue_shopping

Vue 简易商城开发相关推荐

  1. vue+uni-app商城实战 | 第一篇:从0到1快捷开发一个商城微信小程序,无缝接入OAuth2实现一键授权登录

    一. 前言 本篇通过实战来讲述如何使用uni-app快速进行商城微信小程序的开发以及小程序如何接入后台Spring Cloud微服务. 有来商城 youlai-mall 项目是一套全栈商城系统,技术栈 ...

  2. Vue PC商城项目开发笔记与问题汇总

    Vue PC商城项目开发笔记与问题汇总 负责PC端商城项目,这也是人生第一个真正的项目.刚做了一天,就遇到不少问题,在这里列出自己的问题与解决办法,与大家交流,提升自己,希望以后不会掉进同一个坑里. ...

  3. python写微信小程序商城_Python(Django 2.x)+Vue+Uniapp微信小程序商城开发视频教程

    重要的事儿说在前面: 这并非是一个基础课程,请没有相关技术基础知识的同学先学一下基础知识. 本次分享虽然使用Uni-app这个"开发一次,多端覆盖"的框架,但只会给大家分享演示&q ...

  4. [转帖]2019 简易Web开发指南

    2019 简易Web开发指南 2019年即将到来,各位同学2018年辛苦了. 不管大家2018年过的怎么样,2019年还是要继续加油的! 在此我整理了个人认为在2019仍是或者将成为主流的技术与大家分 ...

  5. 谷粒商城开发踩坑及部分知识点大总结

    谷粒商城开发BUG踩坑及部分知识点大总结 基本上bug的出现位置和时间线都能够匹配 如果对你有帮助的话就点个赞哈 2022.6.28 github设置ssh免密登陆,以下代码在git bash上面输入 ...

  6. vue简易微前端项目搭建(一):项目背景及简介

    github传送门: 1.h5主项目 2.项目脚手架 3.子项目模板 系列文章传送门: vue简易微前端项目搭建(一):项目背景及简介 vue简易微前端项目搭建(二):子项目模板及项目脚手架搭建 vu ...

  7. vue简易微前端项目搭建(二):子项目模板及项目脚手架搭建

    github传送门: 1.h5主项目 2.项目脚手架 3.子项目模板 系列文章传送门: vue简易微前端项目搭建(一):项目背景及简介 vue简易微前端项目搭建(二):子项目模板及项目脚手架搭建 vu ...

  8. vue实现商城列表渲染

    需求:         利用vue实现商城列表渲染 所需数据如下: mockData: [{"id": 1,"img": "https://img13 ...

  9. Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)十六(商品排序,Thymeleaf快速入门,商品详情页的展示)

    Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)十六(商品详情页的展示) 一.商品排序 1.完善页面信息 这是用来做排序的,默认按照综合排序 ...

最新文章

  1. 二维小波变换_Wavelet Pooling小波池化的思考
  2. SCCM2012R2七:msi软件分发和卸载
  3. 腾讯2013暑期实习笔试面试总结
  4. 体感(Kinect)技术开发和应用简介
  5. java基础之多态的详细解释_JAVA基础之多态
  6. linux 挂iscisc存储,基于arm的嵌入式linux操作系统的移植研究-通信与信息系统专业论文.docx...
  7. ofo 辟谣“月盈利百万”;苹果回应向腾讯传输数据;Python 3.8.0 发布 | 极客头条...
  8. JAVA WEB 对返回数据进行按中文名称首字母A~Z升序排序
  9. Linux环境下虚拟化之KVM常用命令
  10. 苹果计算机音乐谱大全,macOS乐谱制作软件大全推荐~
  11. excel熵值法计算权重_SPSS主成分分析 | 权重计算amp;极差法标准化超详细教程!(下)...
  12. 利用onekey软件制作win10.gho系统文件的小方法
  13. mui框架手机端APP开发
  14. 【红队APT】钓鱼篇Office-CVE漏洞RLO隐藏压缩包释放免杀打包捆绑
  15. ANI漏洞微软官方补丁KB925902 下载
  16. python图片裁剪软件_python 实现图片裁剪小工具
  17. 5.19 英语单词小计
  18. 视频剪辑工具,批量处理视频中的声音,教你添加新音乐
  19. 原生JS实现轮播图插件
  20. 面试热点话题:聊聊MySQL索引“B+Tree”的前世今生,

热门文章

  1. android rxjava作用,Android-RxJava
  2. linux-优化 PS1
  3. UIPATH 常用包
  4. 获取a标签的src属性
  5. paypal国际支付——SpringBoot
  6. 喷雾干燥机设计计算机软件,喷雾干燥机的干燥塔原理与设计架构图详细介绍
  7. IO:字节流截取音乐
  8. 《中国芯片往事》的破局与诞生
  9. 涿州计算机二级培训,涿州职教中心计机专业高考班人才培养方案.doc
  10. 《算法之美》全解读 序言