pinia初体验

在这里插入图片描述

1.介绍

Pinia最初是一个实验,目的是在2019年11月左右重新设计Vue状态管理 Composition API上的样子,也就是下一代Vuex

  • 之前的Vuex主要服务于Vue2,选项式API
  • 如果想要在Vue3中使用Vuex,需要使用Vuex4.0版本
  • 所以在Vue3伴随着组合式API诞生之后,也设计了全新的Vuex:Pinia,也就是Vuex 5.0
  • pinia中去除了mutations配置项只保留了actions

2.安装

yarn add pinia
# 或者使用 npm
npm install pinia

3.基本使用

1.创建pinia示例并挂载

import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
// 创建pinia实例
const pinia = createPinia()
// 挂载到根实例上
createApp(App).use(pinia).mount('#app')

2.创建一个pinia

import { defineStore } from "pinia"

// 1.定义容器
/**
 * 参数1:容器的ID,必须唯一,pinia会把所有的容器挂载到根容器上
 * 参数2:配置项
 */
export const useMainStore = defineStore('main', {
   /**
    * 
    * state:类似vue2中的data函数
    * 1. 必须是函数,这样是为了在服务端渲染的时候避免状态污染
    * 2. 必须是箭头函数
    */
   state: () => {
      return {
         sum: 0
      }
   },
   /**
    * 类似计算属性跟vuex中的getters一样
    */
   getters: {

   },
   /**
    * 业务逻辑处理,修改state中的数据
    */
   actions: {

   }
})

3.App.vue中测试一下

<template>
   <div>
      <h2> mainStore.sum:{{  mainStore.sum }}</h2>
      <h2>sum:{{ sum }}</h2>
      <button @click="handleClick">修改数据</button>
   </div>
</template>

<script setup lang="ts">
import { useMainStore } from '../src/store/index'
import { storeToRefs } from 'pinia';

const mainStore = useMainStore()
/**
 *  const { sum } = mainStore
 * 直接解构会丢失数据的响应式
 */

//使用storeToRefs进行解构,这样的数据是响应式的
const { sum } = storeToRefs(mainStore) 
const handleClick = () => {
   mainStore.sum++ //可以成功修改
}

</script>

4. 修改state中的数据

//页面中的配置
const handleClick = () => {
   //方式1:最简单的方式
   mainStore.sum++ //可以成功修改

   //方式2:使用$patch批量修改
   mainStore.$patch({
      sum: mainStore.sum + 1
   })

   //方式3:$patch传入一个函数,接受state为参数,批量更新
   mainStore.$patch(state => {
      state.sum++
   })

   //方式4:使用action修改,直接调用action中定义的函数
   mainStore.changeState(1)
}

// actions 中的配置
actions:{
   changeState(val:number) {
      this.sum+=val
   }
}

5. getters

getters: {
      sumBig(state) {
         /*
            state和this是相同的
            getters使用方式就跟vue2中的计算属性一样 
         */
         console.log('state === this :>> ', state === this); //true
         return state.sum * 10
      }
   },


// App.vue内
import { useMainStore } from '../src/store/index'
import { storeToRefs } from 'pinia';

const mainStore = useMainStore()
const { sum ,sumBig } = storeToRefs(mainStore)

6. 完整代码

index.ts中

import { defineStore } from "pinia"

// 1.定义容器
/**
 * 参数1:容器的ID,必须唯一,pinia会把所有的容器挂载到根容器上
 * 参数2:配置项
 */
export const useMainStore = defineStore('main', {
   /**
    * 
    * state:类似vue2中的data函数
    * 1. 必须是函数,这样是为了在服务端渲染的时候避免状态污染
    * 2. 必须是箭头函数
    */
   state: () => {
      // 2.使用容器中的state
      return {
         sum: 0
      }
   },
   /**
    * 类似计算属性跟vuex中的getters一样
    */
   getters: {
      sumBig(state) {
         console.log('state === this :>> ', state === this); //true
         return state.sum * 10
      }
   },
   /**
    * 业务逻辑处理,修改state中的数据
    */
   actions: {
      // 3.修改state
      changeState(val: number) {
         this.sum += val
      }
   }
})

App.vue中

<template>
   <div>
      <h2>App组件</h2>
      <h2> mainStore.sum:{{ mainStore.sum }}</h2>
      <h2>sum:{{ sum }}</h2>
      <h2>sumBig :{{ sumBig }}</h2>
      <button @click="handleClick">修改数据</button>
      <hr>
      <Demo></Demo>
   </div>
</template>

<script setup lang="ts">
import Demo from './components/Demo.vue'
import { useMainStore } from '../src/store/index'
import { storeToRefs } from 'pinia';

const mainStore = useMainStore()
/**
 *  const { sum } = mainStore
 * 直接解构会丢失数据的响应式
 */
//使用storeToRefs进行解构
const { sum ,sumBig } = storeToRefs(mainStore)
const handleClick = () => {
   //方式1:最简单的方式
   // mainStore.sum++ //可以成功修改

   //方式2:使用$patch批量修改
   // mainStore.$patch({
   //    sum: mainStore.sum + 1
   // })

   //方式3:$patch传入一个函数,接受state为参数,批量更新
   // mainStore.$patch(state => {
   //    state.sum++
   // })

   //方式4:使用action修改,直接调用action中定义的函数
   mainStore.changeState(1)
}
</script>

<style scoped>

</style>

4.购物车小案例

页面模板

/src/App.vue

<template>
   <div id="app">
     <h1>Shopping Cart Example</h1>
     <hr>
     <h2>Products</h2>
     <ProductList/>
     <hr>
     <ShoppingCart/>
   </div>
 </template>
 
 <script lang="ts" setup>
 import ProductList from './components/ProductList.vue'
 import ShoppingCart from './components/ShoppingCart.vue'

 </script>

src\components\ProductList.vue


<template>
   <ul>
      <li v-for="product in all" :key="product.id">
         {{ product.title }} - {{ product.price | currency }}
         <button :disabled="!product.inventory" @click="addProductToCart(product)">
            加入购物车
         </button>
      </li>
   </ul>
</template>

<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { useProductStore } from '../store/products'
import { useCartStore } from '../store/cart'

const ProductStore = useProductStore()
const CartStore = useCartStore()
const { all } = storeToRefs(ProductStore)
const currency: number = 0

//从接口中获取所有的商品信息数据
ProductStore.loadAllProducts()

const addProductToCart = (product: any) => {
   CartStore.addProductToCart(product)
}
</script>

src\components\ShoppingCart.vue

<template>
   <div class="cart">
      <h2>Your Cart</h2>
      <p>
         <i>请添加一些商品到购物车</i>
      </p>
      <ul>
         <li v-for="cartItem in cartProducts" :key="cartItem.id">
            商品名称:{{ cartItem.title }} - 商品价格:{{ cartItem.price | 0 }} - 商品数量:{{ cartItem.quantity }}
         </li>
      </ul>
      <p>商品总价: {{ CartStore.totalPrice }}</p>
      <p><button @click="CartStore.checkOut">结算</button></p>
      <p v-show="CartStore.checkOutStatus">结算 【{{ CartStore.checkOutStatus }}】!!!</p>
   </div>
</template>

<script lang="ts" setup>
import { storeToRefs } from 'pinia';
import { useCartStore } from '../store/cart'

const CartStore = useCartStore()
const { cartProducts } = storeToRefs(CartStore)
</script>

模拟数据

src/api/shop.ts

//定义接口
export interface IProduct {
   id: number,
   title: string,
   price: number,
   inventory: number //库存
}
//泛型数组 约束数组中每个对象
const _products: IProduct[] = [
   { id: 1, title: 'iphone 14', price: 900, inventory: 5 },
   { id: 2, title: 'iphone 13', price: 1000, inventory: 5 },
   { id: 3, title: 'iphone 12', price: 2000, inventory: 5 },
]
//获取所有商品数据
export const getProducts = async () => {
   await wait(100)
   return _products
}
//模拟结算
export const buyProducts = async () => {
   await wait(100)
   return Math.random() > 0.5
}
//没啥用的延迟效果
async function wait(delay: number) {
   return new Promise((reslove) => setTimeout(reslove, delay))
}

定义store数据

src\store\products.ts

import { defineStore } from 'pinia';
import { getProducts, IProduct } from '../api/shop'
export const useProductStore = defineStore('products', {
   state: () => {
      return {
         all: [] as IProduct[], // 类型推导
      }
   },
   getters: {

   },
   actions: {
      async loadAllProducts() {
         const res: IProduct[] = await getProducts()
         this.all = res
      },
      decrementProduct(product: IProduct) {
         //如果商品已经在购物车内那么就数量-1
         const res = this.all.find(item => item.id === product.id)
         if (res) {
            res.inventory--
         }
      }
   }
})

src\store\cart.ts

import { IProduct, buyProducts } from './../api/shop';
import { defineStore } from 'pinia';
import { useProductStore } from '../store/products'
type CartProduct = {
   quantity: number,
} & Omit<IProduct, 'inventory'>
//Omit过滤 把IProduct接口中的inventory给去除掉

export const useCartStore = defineStore('cart', {
   state: () => {
      return {
         cartProducts: [] as CartProduct[],
         checkOutStatus: null as null | string
      }
   },
   getters: {
      //总价
      totalPrice(state) {
         return state.cartProducts.reduce((total, item) => {
            return total + (item.quantity * item.price)
         }, 0)
      }
   },
   actions: {
      //添加
      addProductToCart(product: IProduct): void {
         if (product.inventory < 1) {
            return
         }
         const cartItem = this.cartProducts.find((item) => item.id === product.id)
         if (cartItem) { //如果商品重复了 那么就数量+1
            cartItem.quantity++
         } else {
            this.cartProducts.push({
               id: product.id,
               title: product.title,
               price: product.price,
               quantity: 1 //初次加入购物车的商品数量为1
            })
         }
         const ProductStore = useProductStore()
         ProductStore.decrementProduct(product)
      },
      //结算
      async checkOut() {
         const res = await buyProducts()
         this.checkOutStatus = res ? '成功' : '失败'
      }
   }
})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值