分析得到:因为购物车,在几个页面都是需要进行数据互通,主页面,商品页面,详情页面,三个不同的组件,要实现一个页面数据变化,另外页面数据跟着变化,那必须要使用vue中vuex,因为是vue3也可以使用pinia
vuex官网:https://vuex.vuejs.org/zh/
pinia官网:https://pinia.web3doc.top/
这次购物车就选用vuex:
在vite.config.ts中配置
注意:建立好模块之后,index.ts需要引入
vuex的特点是: 全局变量,
他有一个缺点: 如果页面刷新,那我们做的全局变量会初始化
怎么解决这个问题:
用的底层原理就是将 vuex 的全局变量放到 localstorage
localstorage,生命周期,您不删除的话,他不会删除
sessionstorage 生命周期,浏览器关闭了,就会自动删除
官方: 持久化插件
vuex刷新之后他的数据初始化的问题.
用的是持久化插件
下载vuex数据持久化的插件
npm i [email protected] --save
或者 :
cnpm i [email protected] --save
完整代码段:
整个代码
import { createStore } from 'vuex'
import cart from "./modules/cart"
import createPersistedState from "vuex-persistedstate";
export default createStore({
modules: {
cart
},
plugins: [createPersistedState({
//指定数据被存储在哪里,当值为window.localStorage则表示存储在localStorage中,
// 当值为window.sessionStorage则表示存储在sessionStorage中
storage: window.localStorage,
key: 'sellcard',
paths: ["cart"]
})],
})
最后:项目准备工作已经完成了,所有的配置已经完成,接下来就是写代码,实现购物车的业务逻辑
首先要确定什么类型,是数组,对象,还是字符串,因为是购物车,有很多商品,商品包括名字,价格,数量等.肯定是数组比较合适
state: {
// 放全局变量的
// 定义一个数组存入 你往购物车添加的所有商品
cartList: []
},
添加购物车,需要进行传参,把整个数据传过去
// vue2中store 使用this.$store 因为他是全局挂载
// vue3中store 是按需导入的 不能使用this.$store
// vue3中 let store = new useStore()
// vue3语法糖中 new 不要
// let store = useStore()
let store = useStore();
//第一个方法是把商品添加到购物车
let addToCart = (food: any) => {
//这个方法要操作 全局变量
console.log(food);
store.commit("setData", food);
};
设置这个方法主要是修改改变state中的数据
mutations: {
//同步的修改 state的值的方法
setData(state: any, foodobj: any) {
// foodobj
// 没有点餐的数量 要呀
// 你在点击添加商品的时候.相当于把商品添加进来,然后
// 数量默认为1
state.cartList.push({ count: 1, ...foodobj })
}
},
小bug出现:点击购物车,跳转到详情页面去了,主要是事件冒泡,需要阻止事件冒泡
主要是解决:有数量的时候显示数据,如果数量为0,显示"添加购物车"
难点:关键是怎么判断,到底什么时候v-if,什么时候v-else呢
问题:
//他不是点击触发出的点击事件
//详情页面也有一个添加购物车
//我点击详情页面的添加购物车,
//商品的按钮就会消失
//因为我已经添加了购物车
解决思路:vue中可以用监听的方式来实时掌握数据源的变化,当数据源发生了变化,就触发这个v-if或者v-else;而且它们返回的true或者false
1.可以用computed实时监听数据源的变化,但是需要传参过去
注意:但是我们需要进行传参,然而计算属性不能传值,用的办法就是返回一个函数,因为函数是可以进行接受传参的,可以解决这个不能传参的问题
let isHaveFood = computed(() => {
let fn = (id: any) => {
//因为v-if最终的结果为true /false
// 我这里的方法会返回一个true /false
// 根据数据源的变化而变化
// 这个数据源在 全局变量中
// 实时的监听全局变量中的cartList的变化
//store.getters
return store.getters.isFood(id);
};
return fn;
});
2.在 getters:中定义,因为getters可以计算数据源的变化情况,但是也是需要返回一个函数,因为计算属性不能传参,返回函数来接收computed传的参数id
// 全局变量中的计算属性
// 计算属性不能传值,只能返回一个函数才可以传值
// 主要是判断变量中的id和传过来的id是否相同
isFood(state: any) {
return function (id: number) {
for (let i = 0; i < state.cartList.length; i++) {
if (state.cartList[i].id == id) {
return true;
}
}
return false;
};
},
3.在getters中实时监听计算state中的数据源,就可以判断是否被添加到购物车了,主要是利用循环,判断每一项,数据源的商品id是否等于computed传过来的id,如果相同就说明添加了购物车,此时就显示数量,否则显示"添加购物车"
1.在getters定义方法,获取加入购物车的数量
//得到当前加减数量
getCount(state: any) {
return function (id: number) {
for (let i = 0; i < state.cartList.length; i++) {
if (state.cartList[i].id == id) {
return state.cartList[i].count; //返回数量
}
}
return 1;
};
},
2.主要还是判断computed计算属性的参数和数据源的id是都相等
//获取数量
let getCount = computed(() => {
return function (id: any) {
return store.getters.getCount(id);
};
});
1.给加减号添加点击事件,定义一个方法
// 给 加减号添加点击事件
+
{{ getCount(ele.id) }}
-
2.编写代码逻辑,但是要注意的是,mutations里面只能接受两个参数,但是我们要传递三个参数,此时用对象
// 在goods.vue中编写方法
// fh 符号 id就是商品的id
let updCount = (fh,id)=>{
let obj:Object = {
fh,
id
}
store.commit("updCount",obj)
}
3.在ts中编写代码
主要是利用循环遍历,state中每一项,判断我们传过去的id和实际数据源中的id是否一样;如果是加号就直接进行数量上相加,主要是当减法的时候,需要再做判断
// 购物车加减
updCount(state: any, obj: { fh: string; id: number }) {
for (let i = 0; i < state.cartList.length; i++) {
if (state.cartList[i].id == obj.id) {
if (obj.fh == "+") {
state.cartList[i].count++;
} else {
// 需要判断
if (state.cartList[i].count > 1) {
state.cartList[i].count--;
} else {
// 如果减到0 将这个商品全局变量中删除 不能在我们购物车中显示了
state.cartList.splice(i, 1);
// 如果splice有2个参数的话,表示从i开始,一共删除几个元素
}
}
}
}
},
注意:如果数量此时已经不大于1了, 我们就要将这个商品在全局变量中删除,不能在购物车里显示,数组的方法,我们可以用splice,两个参数,第一个代表从i开始,第二个代表删除几个元素.
1.在对应vant组件里面找模板,添加购物车,是在我们layout页面进行布局,因为商品组件,也要显示
js中
定义一个变量
// false 代表隐藏 true 代表的是显示
let show = ref(false)
2.我们需要定义一个方法,控制,点击按钮是否显示与隐藏
问题:怎么去获取state中的值,这里面就是添加购物的商品
1.还是需要用getters来获取
// 这个是获取购物车中的list中数据
getCartList(state: any) {
return state.cartList;
},
2.用computed计算属性实时监听数据的变化,而且它还有缓存功能
let getCartList = computed(() => {
return store.getters.getCartList;
});
3.页面上显示但是会报错 getCount updCount 它们在goods.vue写过一次又要用, 复制粘贴过来
页面上使用
+
{{ getCount(ele.id) }}
-
1.触发点也是数据源的改变而触发 ,cartList数据改变,他就要触发
// 总价格和总数量
getTotal(state: any) {
let totalNumber: number = 0;
let totalPrice: number = 0;
// 循环把数量加起来 数量乘以价格
for (let i = 0; i < state.cartList.length; i++) {
totalNumber += state.cartList[i].count;
totalPrice += state.cartList[i].price * state.cartList[i].count;
}
return {
totalNumber,
totalPrice: totalPrice * 100, //还需要乘以100方便数据显示
};
},
},
// 获取总数量和总价格
let getTotal = computed(() => {
return store.getters.getTotal;
});
2.页面上直接渲染即可
第一步:
src中新建mixin的目录
第二步:
在mixin的目录中新建一个文件 cartMixin.ts
把公共的方法提出来
第三步:完整代码块
export default function () {
let store = useStore();
let getCartList = computed(() => {
return store.getters.getCartList;
});
// 获取总数量和总价格
let getTotal = computed(() => {
return store.getters.getTotal;
});
//第一个方法是把商品添加到购物车
let addToCart = (food: any) => {
//这个方法要操作 全局变量
// console.log(food);
store.commit("setData", food);
};
//他不是点击触发出的点击事件
//详情页面有一个添加购物车
//我点击详情页面的添加购物车,
//商品的按钮就会消失
//因为我已经添加了购物车
// 用什么方法去触发这个方法了
// 计算属性 具有监听的效果
// 当数据源改变的时候就会触发
// 计算属性能传值吗?
// 计算属性在vue中当基本属性使用
let isHaveFood = computed(() => {
let fn = (id: any) => {
//因为v-if最终的结果为true /false
// 我这里的方法会返回一个true /false
// 根据数据源的变化而变化
// 这个数据源在 全局变量中
// 实时的监听全局变量中的cartList的变化
//store.getters
return store.getters.isFood(id);
};
return fn;
});
//获取数量
let getCount = computed(() => {
return function (id: any) {
return store.getters.getCount(id);
};
});
//加或者减
//fh符号
let updCount = (fh: string, id: any) => {
// 因为加减是点击触发
// store中mutations去写方法
//mutations中的方法只有两个参数
// 第一个参数为state 第二个参数就是你传过去的
//我们有两个,怎么才能变成一个
let obj: Object = {
fh,
id,
};
store.commit("updCount", obj);
};
// 把方法暴露出去
return {
updCount,
getCount,
isHaveFood,
addToCart,
getTotal,
getCartList,
};
}
第四步:怎么在页面上用,按需引入即可使用
import cartMixin from "@/mixin/cartMixin.ts"
let { updCount, getCount, isHaveFood,addToCart,getTotal ,getCartList } = cartMixin()
cartMixin是一个方法 用的时候需要添加括号 cartMixin()
cartMixin() 代表的是调用这个方法 这个方法他又返回一个对象
````js
{{ item.name }}
{{ i.name }}
月售{{ ele.sellCount }}
+
{{ getCount(ele.id) }}
-
添加购物车
export default {
state: {
// 全局变量
cartList: [], //定义一个数组,存你往购物车添加的所有商品
},
getters: {
// 全局变量中的计算属性
// 计算属性不能传值,只能返回一个函数才可以传值
// 主要是判断变量中的id和传过来的id是否相同
isFood(state: any) {
return function (id: number) {
for (let i = 0; i < state.cartList.length; i++) {
if (state.cartList[i].id == id) {
return true;
}
}
return false;
};
},
//得到当前加减数量
getCount(state: any) {
return function (id: number) {
for (let i = 0; i < state.cartList.length; i++) {
if (state.cartList[i].id == id) {
return state.cartList[i].count; //返回数量
}
}
return 1;
};
},
// 这个是获取购物车中的list中数据
getCartList(state: any) {
return state.cartList;
},
// 总价格和总数量
getTotal(state: any) {
let totalNumber: number = 0;
let totalPrice: number = 0;
// 循环把数量加起来 数量乘以价格
for (let i = 0; i < state.cartList.length; i++) {
totalNumber += state.cartList[i].count;
totalPrice += state.cartList[i].price * state.cartList[i].count;
}
return {
totalNumber,
totalPrice: totalPrice * 100, //还需要乘以100方便数据显示
};
},
},
mutations: {
// 同步的修改state的值的方法
setData(state: any, foodObj: any) {
// 要不加的数据追加到变量中
state.cartList.push({ count: 1, ...foodObj });
},
// 购物车加减
updCount(state: any, obj: { fh: string; id: number }) {
for (let i = 0; i < state.cartList.length; i++) {
if (state.cartList[i].id == obj.id) {
if (obj.fh == "+") {
state.cartList[i].count++;
} else {
// 需要判断
if (state.cartList[i].count > 1) {
state.cartList[i].count--;
} else {
// 如果减到0 将这个商品全局变量中删除 不能在我们购物车中显示了
state.cartList.splice(i, 1);
// 如果splice有2个参数的话,表示从i开始,一共删除几个元素
}
}
}
}
},
},
actions: {
// 异步方法 可以修改state
},
};