Pinia状态管理库-数据状态同步

pinia状态管理库

在 Vue.js 中,Pinia 是一种状态管理库,它使用了响应式的数据流。当使用 Pinia 管理的数据发生变化时,与 Vue 2.x 中的 Vuex 类似,Pinia 也会触发组件的重新渲染,以确保 UI 反映最新的数据状态。

当你在 Pinia 存储中使用 refreactive 定义的数据发生变化时,相关的组件会在下一个事件循环周期内更新,这是 Vue.js 的响应式系统的一部分。这种自动的响应式更新机制使得你不需要手动调度组件的重新渲染,Vue.js 会负责处理这些变化。

cartStore.js的封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//封装购物车模块
import { defineStore } from "pinia";
export const useCartStore = defineStore({
id: 'cart',
state: () => ({
cartList: []
}),
actions: {
addCart(goods) {
//已经添加过,count++
const item = this.cartList.find((item) => goods.skuId === item.skuId)
if (item) {
item.count = item.count + goods.count
} else {
//没有添加过则新加一个
this.cartList.push(goods)
}
},
//删除操作
delCart(skuId) {
const idx = this.cartList.findIndex((item) => skuId === item.skuId)
this.cartList.splice(idx, 1)
}
},
//计算属性
getters: {
//总数量:所有项的count之和
allcount: (state) => {
return state.cartList.reduce((a, c) => a + c.count, 0)
},
//总价格
allprice: (state) => {
return state.cartList.reduce((a, c) => a + c.price * c.count, 0)
}
}
})

addCart 方法修改 cartList 数据时,相关的组件将在下一个事件循环周期内进行更新,以反映 cartList 的最新状态。

总的来说,Pinia 提供了一种方便而强大的方式来管理 Vue.js 应用程序的状态,并通过其响应式系统确保数据的一致性和更新。

购物车复选框状态同步到pinia中store对应的状态

v-model双向绑定指令不方便进行命令式的操作(因为后续还需要调用接口),所以把v-model回退到一般模式,也就是:model-value和@click的配合实现。

单选框绑定一个回调函数(@change事件),并将当前的数据项数据i和单选框选中状态传递给这个回调函数。

1
2
3
4
5
6
7
8
9
10
11
<!-- 全选框 -->
<el-checkbox :model-value="useStore.isAll" @change="allCheck" />

<tr v-for="i in cartList" :key="i.id">
<td>
<!-- 单选框 -->
<el-checkbox
:model-value="i.selected"
@change="(selected) => singleCheck(i, selected)"
/>
</td>

回调函数处理逻辑:

1、导入Pinia 状态管理库定义的购物车状态管理模块(这个模块包含了购物车的状态属性、操作方法和计算属性,允许我们在当前文件中访问和操作购物车的状态信息);

2、调用 useCartStore 函数,创建了一个名为 useStore 的 Pinia store 实例。这个实例用于在当前组件中访问购物车的状态和操作;

3、从 useStore 实例中获取购物车列表数据,将其赋值给名为 cartList 的变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script setup>
import { useCartStore } from "@/stores/cartStore";
const useStore = useCartStore();
const cartList = useStore.cartList;

//单选回调
const singleCheck = (i, selected) => {
//单选框切换时修改pinia的store对应的状态
useStore.singleCheck(i.skuId, selected);
};
//全选回调
const allCheck = (selected) => {
//Vue 会默认将事件对象作为参数传递给回调函数
console.log("点击了", selected);
useStore.allcheck(selected);
};
</script>

使用pinia状态管理库提供的 defineStore 函数定义了一个名为 useCartStore 的状态管理模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//封装购物车模块
import { defineStore } from "pinia";
export const useCartStore = defineStore({
id: 'cart',
state: () => ({
cartList: []
}),
actions: {
addCart(goods) {
//已经添加过,count++
const item = this.cartList.find((item) => goods.skuId === item.skuId)
if (item) {
item.count = item.count + goods.count
} else {
//没有添加过则新加一个
this.cartList.push(goods)
}
},
//删除操作
delCart(skuId) {
const idx = this.cartList.findIndex((item) => skuId === item.skuId)
this.cartList.splice(idx, 1)
},
//单选功能
singleCheck(skuId, selected) {
//通过skuid找到要修改的那一项,把这一项的selected值修改为传过来的selected的值
const item = this.cartList.find((item) => item.skuId === skuId)
//item是对象
item.selected = selected
},
//全选功能
//当是空数组时forEach不会遍历数组
allcheck(selected) {
this.cartList.forEach((item) => item.selected = selected);
}
},
//计算属性
getters: {
//总数量:所有项的count之和
allcount: (state) => {
return state.cartList.reduce((a, c) => a + c.count, 0)
},
//总价格
allprice: (state) => {
return state.cartList.reduce((a, c) => a + c.price * c.count, 0)
},
//当cartList是空数组时返回true,every方法认为所有的元素都满足条件,因为没有元素违反了条件。
isAll: (state) => {
return state.cartList.every((item) => {
return item.selected
})
}
}
})

补充:已选中数量和已选中总价格如何显示?

增加两个计算属性:

1
2
3
4
5
6
7
8
9
10
11
//已选中数量
selectedCount: (state) => {
const selectedItems = state.cartList.filter((item) => item.selected)
return selectedItems.reduce((a, c) => a + c.count, 0)
},
//已选中总价格
selectedPrice: (state) => {
const selectedItems = state.cartList.filter((item) => item.selected)
const allprice = selectedItems.reduce((a, c) => a + c.price * c.count, 0)
return allprice.toFixed(2)
}

接口购物车

加入购物车

判断用户是否登录(看是否存在token)

如果已经登录,就调用加入购物车的接口;

然后调用获取购物车列表接口,紧接着接口购物车列表覆盖本地购物车列表。

修改相应store的actions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//添加购物车
async addCart(goods) {
const { skuId, count } = goods
if (this.isLogin) {
//登录后的加入购物车逻辑
//调用加入购物车的接口
await insertCartAPI({ skuId, count })
//调用获取购物车列表接口
const res = await findNewCartListAPI()
console.log('购物车列表:', res);
//接口购物车列表覆盖本地购物车列表
this.cartList = res.result
} else {
//已经添加过,count++
const item = this.cartList.find((item) => goods.skuId === item.skuId)
if (item) {
item.count = item.count + goods.count
} else {
//没有添加过则新加一个
this.cartList.push(goods)
}
}
},

api封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 加入购物车
export const insertCartAPI = ({ skuId, count }) => {
return request({
url: '/member/cart',
method: 'POST',
data: {
skuId,
count
}
})
}
//获取最新的购物列表
export const findNewCartListAPI = () => {
return request({
url: '/member/cart'
})
}

删除购物车数据

逻辑和加入一样,只是接口调用不同

增加删除接口函数

1
2
3
4
5
6
7
8
9
10
// 删除购物车
export const delCartAPI = (ids) => {
return request({
url: '/member/cart',
method: 'DELETE',
data: {
ids
}
})
}

修改对应pinia的store的actions中的删除方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//删除操作
async delCart(skuId) {
if (this.isLogin) {
//接口调用删除购物车商品操作
await delCartAPI([skuId])
//调用获取购物车列表接口
const res = await findNewCartListAPI()
console.log('购物车列表:', res);
//接口购物车列表覆盖本地购物车列表
this.cartList = res.result
} else {
//本地购物车操作
const idx = this.cartList.findIndex((item) => skuId === item.skuId)
this.cartList.splice(idx, 1)
}

},

退出登录-清空购物车数据

在购物车store中增加清空购物车的action,

组件调用store中的清空action,执行清除业务;

找到退出登录的业务逻辑,调用清除函数:

1
2
3
4
5
//stores/userStore.js
//退出时清除用户的数据
clearUserInfo() {
this.userInfo = {}
}

增加清除购物车业务:

1
2
3
4
5
6
7
8
//退出时清除用户的数据
clearUserInfo() {
this.userInfo = {}
//执行清除购物车的action
const userStore = useCartStore()
userStore.clearCart()
}
},

登录-更新购物车列表数据

封装一下更新购物车数据的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//获取购物车列表
async updateCart() {
//调用获取购物车列表接口
const res = await findNewCartListAPI()
console.log('购物车列表:', res);
//接口购物车列表覆盖本地购物车列表
this.cartList = res.result
},
//添加购物车
async addCart(goods) {
const { skuId, count } = goods
if (this.isLogin) {
//登录后的加入购物车逻辑
//调用加入购物车的接口
await insertCartAPI({ skuId, count })
//获取购物车列表
this.updateCart()
} else {
//已经添加过,count++
const item = this.cartList.find((item) => goods.skuId === item.skuId)
if (item) {
item.count = item.count + goods.count
} else {
//没有添加过则新加一个
this.cartList.push(goods)
}
}

},

登录业务调用函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const doLogin = () => {
const { account, password } = form.value;
formRef.value.validate(async (vaild) => {
//vaild表示所有表单都通过校验才为true
console.log(vaild);
if (vaild) {
await userStore.getUserInfo({ account, password });
//1\提示用户
ElMessage({ type: "success", message: "登录成功" });
//2、跳转首页-替换当前位置
router.replace({ path: "/" });
//刷新购物车列表
const cartStore = useCartStore();
cartStore.updateCart();
}
});
};

本地购物车和服务端购物车数据合并

为了不丢失用户在没有登录状态下的本地购物车数据,在做登录操作的时候实现一个优化,将本地购物车数据和服务端购物车数据进行一个合并。

封装合并的接口函数:其中data是数组对象,需要参数为{skuId,selected,count}

1
2
3
4
5
6
7
8
//合并购物车
export const mergeCartAPI = (data) => {
return request({
url: '/member/cart/merge',
method: 'POST',
data
})
}

在用户登录操作增加购物车合并操作,并刷新购物车列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 获取用户登录数据
async getUserInfo({ account, password }) {
try {
const res = await loginAPI({ account, password })
console.log('用户数据:', res);
this.userInfo = res.result
//合并购物车的操作
const cartStore = useCartStore()
await mergeCartAPI(cartStore.cartList.map((item) => {
return {
skuId: item.skuId,
selected: item.selected,
count: item.count
}
}))
//更新购物车列表数据
cartStore.updateCart();
}
catch (e) {
console.log('获取用户数据出错', e);
}
},

生成订单

1、调用接口生成订单id,并且携带id跳转到支付页

2、调用更新购物车列表接口,更新购物车状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//创建订单
const createOrder = async () => {
const res = await createOrderAPI({
deliveryTimeType: 1,
payType: 1,
payChannel: 1,
buyerMessage: "",
goods: checkInfo.value.goods.map((item) => {
return {
skuId: item.skuId,
count: item.count,
};
}),
addressId: defaultAddress.value.id,
});
const orderId = res.result.id;
router.push({
path: "/pay",
query: {
id: orderId,
},
});
//更新购物车
cartStore.updateCart();
};

接口:

1
2
3
4
5
6
7
8
//创建订单
export const createOrderAPI = (data) => {
return request({
url: '/member/order',
method: 'POST',
data
})
}