学习pinia并搭配json-server,axios,typescript实现一个购物车
介绍pinia的语法,购物车案例练习使用pinia
·
文章目录
搭建环境
-
npm init vite@latest 搭建vue-ts环境
-
npm i pinia
-
修改
main.ts
文件
import { createApp } from 'vue'
import App from './App.vue'
// 1、引入
import {createPinia} from 'pinia';
// 2、创建
const pinia = createPinia();
// 3、挂载
createApp(App).use(pinia).mount('#app');
基本使用
定义仓库
Test.vue
import { defineStore } from "pinia";
// 1.定义并导出容器
export const useTestStore = defineStore("test", {
/*
类似于组件中的data,用来存储全局状态的
1、尽量使用函数,为了在服务器端渲染的时候避免交叉请求导致的数据状态污染。
2、尽量使用箭头函数,为了更好的ts类型推导
*/
state: () => {
return {
count: 10,
};
},
/*
类似于组件的computed,用来封装计算属性,有缓存的功能
*/
getters: {},
/*
类似于methods,封装业务逻辑,修改state
*/
actions: {},
});
在组件中获取仓库中的数据
Test.vue
<template>
<div>{{ testStore.count }}</div>
</template>
<script setup lang="ts">
import { useTestStore } from "../store";
const testStore = useTestStore();
</script>
<style scoped>
</style>
获取仓库中数据的多种方式
直接获取与解构获取
Test.vue
<template>
<div>
<div>直接获取:{{ testStore.count }}</div>
<div>解构获取:{{ count }}</div>
</div>
</template>
<script setup lang="ts">
import { storeToRefs } from "pinia";
import { useTestStore } from "../store";
const testStore = useTestStore();
const { count } = storeToRefs(testStore);
console.log(count.value);
</script>
<style scoped>
</style>
注意不能直接进行解构获取
Test.vue
<template>
<div>
<div>直接获取:{{ testStore.count }}</div>
<div>解构获取:{{ count }}</div>
<button @click="btn">count++</button>
</div>
</template>
<script setup lang="ts">
import { useTestStore } from "../store";
const testStore = useTestStore();
/*
pinia内部将数据进行了响应式处理,如果我们直接通过解构赋值的方式来
获取值,那么获取到的值将不是响应式的
*/
const { count } = testStore;
// 修改pinia仓库中的值
const btn = function () {
testStore.count++;
};
</script>
<style scoped>
</style>
更改仓库中的数据
store
import { defineStore } from "pinia";
// 1.定义并导出容器
export const useTestStore = defineStore("test", {
/*
类似于组件中的data,用来存储全局状态的
1、尽量使用函数,为了在服务器端渲染的时候避免交叉请求导致的数据状态污染。
2、尽量使用箭头函数,为了更好的ts类型推导
*/
state: () => {
return {
count: 10,
arr: [1, 2, 3],
};
},
/*
类似于组件的computed,用来封装计算属性,有缓存的功能
*/
getters: {},
/*
类似于methods,封装业务逻辑,修改state
*/
actions: {
changeState(num: number) {
this.count = this.count + num;
this.arr.push(this.arr.length + 1);
},
},
});
Test.vue
<template>
<div>
<div>count:{{ testStore.count }}</div>
<div>arr:{{ testStore.arr }}</div>
<button @click="btn">按钮</button>
</div>
</template>
<script setup lang="ts">
import { useTestStore } from "../store";
const testStore = useTestStore();
const btn = function () {
// 修改数据方式1
testStore.count++;
// 修改数据方式2 批量修改多个数据 $patch(对象)
testStore.$patch({
count: testStore.count + 1,
arr: [...testStore.arr, testStore.arr.length + 1],
});
// 修改数据方式3 $patch(函数)
testStore.$patch((state) => {
state.count++;
state.arr.push(state.arr.length + 1);
});
// 修改数据方式4 调用action,允许传入参数 推荐这种方式,方便集中管理数据操作的行为
testStore.changeState(2);
};
</script>
<style scoped>
</style>
注意不能使用箭头函数定义action
store
...
actions: {
...
test: () => {
console.log(this);
},
},
...
Test.vue
// 测试action中定义了箭头函数
testStore.test();
store
import { defineStore } from "pinia";
console.log(this);
为什么呢?
箭头函数的this指向取决于它的父级this。见下方两端代码示例
const fn = function(obj) {
obj.clg();
};
// obj定义在全局中,因此this指向是window
const obj = {
clg: () => {
console.log(this);
}
}
const fn2 = function() {
fn(obj);
};
fn2();
const fn = function(obj) {
obj.clg();
};
function main() {
return {
clg: () => {
console.log(this);
}
}
};
const fn2 = function() {
// 手动绑定this指向到一个对象
fn(main.call({ name: "xxx" }));
};
fn2();
getters的使用
store
...
getters: {
DoubleCount(state) {
// 拥有缓存功能,不会多次调用值,函数执行多次
console.log("DoubleCount被调用了");
return state.count * 2;
},
// 如果在getter中使用this而不使用state,那么需要手动给返回值定义类型
CountAddTen(): number {
return this.count + 10;
},
},
...
Test.vue
// 调用多次
console.log(testStore.DoubleCount);
console.log(testStore.DoubleCount);
console.log(testStore.DoubleCount);
console.log(testStore.DoubleCount);
console.log(testStore.CountAddTen);
pinia与Vue devtools
购物车案例
项目目录
项目文件
store
import { defineStore } from "pinia";
import {
queryGoods,
IGoods,
IShoppingCartItem,
queryShoppingCart,
addShoppingCart,
updateShoppingCart,
deleteShoppingCartItem,
} from "../api";
// 1.定义并导出容器
export const useStateStore = defineStore("main", {
/*
类似于组件中的data,用来存储全局状态的
1、尽量使用函数,为了在服务器端渲染的时候避免交叉请求导致的数据状态污染。
2、尽量使用箭头函数,为了更好的ts类型推导
*/
state: () => {
return {
count1: 10,
count2: 20,
arr: [1, 2, 3],
};
},
/*
类似于组件的computed,用来封装计算属性,有缓存的功能
*/
getters: {
count1Double(state) {
// 拥有缓存功能,不会多次调用值,函数执行多次
console.log("count1Double被调用了", state.count1);
return state.count1 * 2;
},
// 如果在getter中使用this而不使用state,那么需要手动给返回值定义类型
count2Double(): number {
return this.count2 * 2;
},
},
/*
类似于methods,封装业务逻辑,修改state
*/
actions: {
changeState(num: number) {
this.count1 = this.count1 + num;
this.arr.push(this.arr.length + 1);
},
test: () => {
console.log(this);
},
},
});
// 商品仓库
export const useGoodsStore = defineStore("goods", {
state: () => {
return {
goods: [] as IGoods[],
};
},
getters: {},
actions: {
async queryGoods() {
const result = await queryGoods();
this.goods = result.data;
},
},
});
// 购物车仓库
export const useShoppingCartStore = defineStore("shoppingCart", {
state: () => {
return {
shoppingCart: [] as IShoppingCartItem[],
};
},
getters: {
priceSum(state): number {
let sum = 0;
state.shoppingCart.forEach((item: IShoppingCartItem) => {
sum += item.price * item.amount;
});
return sum;
},
},
actions: {
// 查询购物车中的内容
async queryShoppingCart() {
const result = await queryShoppingCart();
this.shoppingCart = result.data;
},
// 向购物车中添加商品
addShoppingCart: function (good: IGoods) {
const result = this.shoppingCart.map((item: IShoppingCartItem) => {
if (item.id === good.id) {
return item;
}
return false;
});
const isExist = result.find(
(item: boolean | IShoppingCartItem) => item !== false
);
if (!isExist) {
// 发送新增接口
addShoppingCart({ ...good, amount: 1 }).then((response) => {
if (response.status === 201) {
this.shoppingCart.push({ ...good, amount: 1 });
}
});
} else {
// 发送修改接口
updateShoppingCart(good, isExist.amount + 1).then((response) => {
if (response.status === 200) {
this.shoppingCart = this.shoppingCart.map((item) => {
if (item.id === good.id) {
item.amount++;
}
return item;
});
}
});
}
},
// 修改购物车中商品的数量
updateShoppingCart: function (cartItem: IShoppingCartItem, amount: number) {
updateShoppingCart(cartItem, amount).then((response) => {
if (response.status === 200) {
this.shoppingCart.forEach((item) => {
if (item.id === cartItem.id) item.amount = amount;
});
}
});
},
// 删除购物车中的某商品
deleteShoppingCartItem: function (cartItem: IShoppingCartItem) {
deleteShoppingCartItem(cartItem)
.then((response) => {
if (response.status === 200) {
this.shoppingCart = this.shoppingCart.filter(
(item) => item.id !== cartItem.id
);
}
})
.catch((err) => {
console.log(err);
});
},
},
});
request.ts请求拦截器
import axios from "axios";
// import config from "../config/request.js";
// create an axios instance
const request = axios.create({
// baseURL: import.meta.env['VITE_APP_BASE_API'] as string, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 3000, // request timeout
baseURL: "http://localhost:3004",
});
// request interceptor
request.interceptors.request.use(
(c) => {
return c;
},
(error) => {
return Promise.reject(error);
}
);
// response interceptor
request.interceptors.response.use(
(response) => {
if (response.status === 200) {
console.log("请求成功", response.data);
} else {
console.log("接口失败");
}
return response;
},
(error) => {
return Promise.reject(error);
}
);
export default request;
api/index.ts
import request from "./request";
export interface IGoods {
id: number;
name: string;
price: number;
}
export interface IShoppingCartItem extends IGoods {
amount: number;
}
// 查询所有的商品
export const queryGoods = function () {
return request({
//请求类型
method: "GET",
//URL
url: "/goods",
});
};
// 查询购物车中的内容
export const queryShoppingCart = function () {
return request({
//请求类型
method: "GET",
//URL
url: "/shoppingCart",
});
};
// 向购物车中新增商品
export const addShoppingCart = function (shoppingCartitem: IShoppingCartItem) {
return request({
//请求类型
method: "POST",
//URL
url: "/shoppingCart",
data: shoppingCartitem,
});
};
// 修改购物车中某商品的数量
export const updateShoppingCart = function (
shoppingCartItem: IShoppingCartItem | IGoods,
amount: number
) {
return request({
//请求类型
method: "PUT",
//URL
url: `${"/shoppingCart/" + shoppingCartItem.id}`,
data: {
...shoppingCartItem,
amount,
},
});
};
// 删除购物车中的某商品
export const deleteShoppingCartItem = function (
shoppingCartItem: IShoppingCartItem
) {
return request({
//请求类型
method: "DELETE",
//URL
url: `${"/shoppingCart/" + shoppingCartItem.id}`,
});
};
ShoppingCart.vue
<template>
<div>
<div>商品货架</div>
<div v-for="good in goods" :key="good.id">
<div>商品名:{{ good.name }}</div>
<div>价格:{{ good.price }}¥</div>
<button @click="addToShoppingCart(good)">添加到购物车</button>
</div>
<br />
<div>
<div>我的购物车</div>
<div v-for="cartItem in shoppingCart" :key="cartItem.id">
<div>商品名:{{ cartItem.name }}</div>
<div>
数量:<button @click="reduceAmount(cartItem)">-</button
>{{ cartItem.amount }}<button @click="addAmount(cartItem)">+</button>
</div>
<div>价格:{{ cartItem.amount * cartItem.price }}¥</div>
<button @click="deleteCartItem(cartItem)">删除</button>
</div>
<div>总价:{{ priceSum }}</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useGoodsStore, useShoppingCartStore } from "../store/index";
import { storeToRefs } from "pinia";
import { IGoods, IShoppingCartItem } from "../api";
const goodsStore = useGoodsStore();
const shoppingCartStore = useShoppingCartStore();
// 初始化获取所有商品的数据
goodsStore.queryGoods();
shoppingCartStore.queryShoppingCart();
// 需要使用storeToRefs来实现
const { goods } = storeToRefs(goodsStore);
const { shoppingCart, priceSum } = storeToRefs(shoppingCartStore);
const addToShoppingCart = function (good: IGoods) {
console.log(good);
shoppingCartStore.addShoppingCart(good);
};
const reduceAmount = function (cartItem: IShoppingCartItem) {
shoppingCartStore.updateShoppingCart(cartItem, cartItem.amount - 1);
};
const addAmount = function (cartItem: IShoppingCartItem) {
shoppingCartStore.updateShoppingCart(cartItem, cartItem.amount + 1);
};
const deleteCartItem = function (cartItem: IShoppingCartItem) {
shoppingCartStore.deleteShoppingCartItem(cartItem);
};
</script>
<style scoped>
</style>
db.json
{
"goods": [
{
"id": 1,
"name": "苹果",
"price": 10
},
{
"id": 2,
"name": "橘子",
"price": 15
},
{
"id": 3,
"name": "西瓜",
"price": 8
}
],
"shoppingCart": [
{
"id": 1,
"name": "苹果",
"price": 10,
"amount": 3
},
{
"id": 2,
"name": "橘子",
"price": 15,
"amount": 4
},
{
"id": 3,
"name": "西瓜",
"price": 8,
"amount": 4
}
]
}
启动项目
启动后端接口json-server
启动项目
运行效果
代码仓库
git clone https://gitee.com/ctbaobao/csdn-wang-yuanrou.git -b vue3-vite-ts-pinia
更多推荐
已为社区贡献4条内容
所有评论(0)