当前位置:   article > 正文

学习pinia并搭配json-server,axios,typescript实现一个购物车_pinia axios

pinia axios

搭建环境

  1. npm init vite@latest 搭建vue-ts环境

    vite中文文档

  2. npm i pinia

  3. 修改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');
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

基本使用

定义仓库

在这里插入图片描述
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: {},
});

  • 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

在组件中获取仓库中的数据

Test.vue

<template>
  <div>{{ testStore.count }}</div>
</template>

<script setup lang="ts">
import { useTestStore } from "../store";

const testStore = useTestStore();
</script>

<style scoped>
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在这里插入图片描述

获取仓库中数据的多种方式

直接获取与解构获取

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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在这里插入图片描述

注意不能直接进行解构获取

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>
  • 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

在这里插入图片描述

更改仓库中的数据

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);
    },
  },
});
  • 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

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>
  • 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

在这里插入图片描述

注意不能使用箭头函数定义action

store

...
actions: {
    ...
    test: () => {
      console.log(this);
    },
  },
...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Test.vue

// 测试action中定义了箭头函数
testStore.test();
  • 1
  • 2

在这里插入图片描述
store

import { defineStore } from "pinia";
console.log(this);
  • 1
  • 2

在这里插入图片描述

为什么呢?

箭头函数的this指向取决于它的父级this。见下方两端代码示例
  • 1
const fn = function(obj) {
    obj.clg();
};

// obj定义在全局中,因此this指向是window
const obj = {
    clg: () => {
        console.log(this);
    }
}

const fn2 = function() {
    fn(obj);
};

fn2();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在这里插入图片描述

const fn = function(obj) {
    obj.clg();
};

function main() {
    return {
        clg: () => {
            console.log(this);
        }
    }

};

const fn2 = function() {
    // 手动绑定this指向到一个对象
    fn(main.call({ name: "xxx" }));
};

fn2();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

getters的使用

store

...
getters: {
    DoubleCount(state) {
      // 拥有缓存功能,不会多次调用值,函数执行多次
      console.log("DoubleCount被调用了");
      return state.count * 2;
    },
    // 如果在getter中使用this而不使用state,那么需要手动给返回值定义类型
    CountAddTen(): number {
      return this.count + 10;
    },
},
...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

Test.vue

// 调用多次
console.log(testStore.DoubleCount);
console.log(testStore.DoubleCount);
console.log(testStore.DoubleCount);
console.log(testStore.DoubleCount);

console.log(testStore.CountAddTen);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在这里插入图片描述

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);
        });
    },
  },
});

  • 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
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152

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;
  • 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

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}`,
  });
};
  • 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
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

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>
  • 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
  • 55
  • 56
  • 57

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
    }
  ]
}
  • 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

启动项目

启动后端接口json-server

在这里插入图片描述

启动项目

在这里插入图片描述

运行效果

在这里插入图片描述

代码仓库

git clone https://gitee.com/ctbaobao/csdn-wang-yuanrou.git -b vue3-vite-ts-pinia
  • 1

仓库

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/225643
推荐阅读
相关标签
  

闽ICP备14008679号