# 核心概念
Vue 的官方调试工具 Devtools (opens new window) (opens new window)已经集成了 Vuex,提供了诸如零配置的 Time-travel 调试、Revert 回退、Commit 提交(会影响 Base State)、状态快照、导入导出等高级调试功能(一定要亲自 ✍ 体验)。如图所示:
我们可以通过下面的这个小案例,快速入手 vuex 的使用:
完整案例 ✍
代码示例
- stores / shop.ts
import { defineStore } from 'pinia'
interface InterGoods {
name: string
price: number
num?: number
select?: boolean
}
const useShopStore = defineStore('shop', {
state() {
return {
goods: [
{ name: 'Apple', price: 2.5 },
{ name: 'Banana', price: 1.5 },
{ name: 'Orange', price: 3.0 },
{ name: 'Grapes', price: 4.5 },
{ name: 'Mango', price: 5.0 }
] as InterGoods[],
isAllSelect: false
}
},
getters: {
total(): number {
return this.goods
.filter((item) => item.select)
.reduce(function (prev: number, item) {
return (prev += item.price * (item.num || 1))
}, 0)
}
},
actions: {
updataNum(index: number, num: number) {
this.goods[index].num = this.goods[index].num || 1
this.goods[index].num! += num
},
allSelectChange() {
this.goods.forEach((item) => {
item.select = this.isAllSelect
})
},
sigleChange() {
this.isAllSelect = this.goods.every((item) => {
return item.select === true
})
// console.log(reusult)
}
}
})
export default useShopStore
- App.vue
<script setup lang="ts">
import useShopStore from './stores/shop'
const shopStore = useShopStore()
function updataNum(index: number, num: number) {
shopStore.updataNum(index, num)
}
function allSelectChange() {
shopStore.allSelectChange()
}
function sigleChange() {
shopStore.sigleChange()
}
</script>
<template>
<div>简约购物车案例</div>
<div>
全选:<input type="checkbox" v-model="shopStore.isAllSelect" @change="allSelectChange" />
</div>
<div v-for="(item, index) in shopStore.goods">
<input @change="sigleChange()" type="checkbox" v-model="item.select" />
商品名:{{ item.name }} --- 商品价格:{{ item.price }} ---
<!-- num为0是,直接赋予它为 1 -->
<button @click="updataNum(index, 1)">+</button>
{{ item.num || 1 }}
<button @click="updataNum(index, -1)">-</button> --- 小计:{{ item.price * (item.num || 1) }}
</div>
<div>总计:{{ shopStore.total }}</div>
</template>
# 1、State
在使用 state 时,我们还可以通过 store 提供的方法对其进行 🤔 操作
State 与 Ts
interface UserInfo {
name: string
age: number
}
const useStoreStore = defineStore('storeId', {
state: () => {
return {
// 用于初始化空列表
userList: [] as UserInfo[],
// 用于尚未加载的数据
user: null as UserInfo | null
}
}
})
export default useStoreStore
<template>
<div>你好,我是pinia ---{{ count }}</div>
<buttom @click="userStore.$reset()">重置 state</buttom>
<button @click="count++">直接修改 state</button>
<buttom @click="changeState()">变更 state</buttom>
</template>
<script setup>
import useUserStore from './stores/counter'
import { storeToRefs } from 'pinia'
const userStore = useUserStore()
const { count, name, age } = storeToRefs(userStore)
function changeState() {
// 方式1:直接修改
// userStore.name = 'zhangsan'
// userStore.age = 99
// 方式2:使用 $patch
userStore.$patch({
name: 'zhangsan',
age: 99
})
}
</script>
# 2、Getter
在 defineStore 中,我们可以通过 this 访问到整个 store 实例:
这样当我们在 getter 中使用另一个 getter 时,就不用像 vuex 一样需要第二个参数 getter,而是直接使用 this
注意
在 Pinia 中,state 和 getter 一样,在使用时并不需要和 vuex 一样加上.state
、.getter
用来区分
const useCarterStore = defineStore('carter', {
state() {
return {
listArr: [
{ id: 1, name: '华为', product: '手机', hasStock: false },
{ id: 2, name: '小米', product: '家居', hasStock: true },
{ id: 3, name: '格力', product: '空调', hasStock: true }
]
}
},
getters: {
titleMsg(state) {
return state.listArr.map((item) => item.name + '--' item.product)
}
// 使用this
hasStock(state) {
return state.listArr.filter((item) => item.hasStock)
},
stockProd() {
return this.hasStock.map(item => item.product)
}
// 返回回调函数
getProdDetails(state) {
return function(id) {
return state.listArr.find((item) => item.id === id)
}
}
// {{ carterStore.getProdDetails(2) }} 使用示例
}
})
export default useCarterStore
拓展:跨 store ✍ 的 getter 操作
import { defineStore } from 'pinia'
import useUserStore from './useUser'
const useCarterStore = defineStore('carter', {
state() {
return {
listArr: [
{ id: 1, name: '华为', product: '手机', hasStock: false },
{ id: 2, name: '小米', product: '家居', hasStock: true },
{ id: 3, name: '格力', product: '空调', hasStock: true }
]
}
},
getters: {
showMsg(state) {
const userStore = useUserStore()
const listItem = state.listArr.find((item) => item.id === userStore.prodId)
return userStore.name + 'has' + listItem.product
}
}
})
export default useCarterStore
# 3、Action
在 Pinia 中,Action 基本就不用说了,想干啥就干啥(一家独大,又没有 mutation)
前面描述的使用 this、跨 store 操作等在 Action 中同样可以
const dely = () => {
return new promise((resolve,reject) => {
setTimeout(()=>{
resolve()
},1000)
})
}
const useCounterStore = defineStore('main', {
state() {
return {
count: 0,
}
},
actions: {
increment() {
this.count++,
},
randomizeCounter() {
this.count = Math.round(100 * Math.random())
},
// 使用函数传参
async saveCount(payload) {
await dely()
this.count = payload
}
}
})
export default useCounterStore
# 4、插件
Pinia 持久化插件
- pinia-plugin-persistedstate (opens new window):可以自定义存储方式、兼容 Vue2 和 3、Nuxt(SSR)等
下面,我们直接写一下看看:
Pinia 插件是一个函数,可以选择性地返回要添加到 store 的属性。它接收一个可选参数,即 context。
export function myPiniaPlugin(context) {
context.pinia // 用 `createPinia()` 创建的 pinia。
context.app // 用 `createApp()` 创建的当前应用(仅 Vue 3)。
context.store // 该插件想扩展的 store
context.options // 定义传给 `defineStore()` 的 store 的可选对象。
// ...
}
以官网的 Pinia 插件案例为例:
← Pinia基础 【axios请求封装✨】 →