对比 Vuex优点:
- Vue2和Vue3都支持,可以定义为选项式和组合式API形式
- pinia中只有state、getter、action,没有mutations和models
- pinia中action支持同步和异步,Vuex不支持
- 良好的Typescript支持,毕竟我们Vue3都推荐使用TS来编写,这个时候使用pinia就非常合适了
- 无需再创建各个模块嵌套了,Vuex中如果数据过多,我们通常分模块来进行管理,稍显麻烦,而pinia中每个store都是独立的,互相不影响。
- 体积非常小,只有1KB左右。
- pinia支持插件来扩展自身功能。
- 支持服务端渲染
pinia 其中几个API
- $patch 可以批量修改 state 里面的数据
- $reset 可以重置 state 里面的数据
- $subscribe 监听 state 里面的值是否发生了变化:主要做数据持久化,存在本地
- $onAction:监听每一个action的执行,并在执行前、执行后、执行出错时执行一些特定的逻辑。
使用
yarn add pinia
npm install pinia
----------------------------------
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
定义store文件
- 在store文件夹下新建user.js
- defineStore定义一个store
- 第一个参数是你的应用中 Store 的唯一 ID
- 第二个参数可接受两类值:Setup 函数或 Option 对象
- 仓库最好使用 store 的名字,同时以
use
开头且以 Store
结尾。(比如 useUserStore
,useCartStore
,useProductStore
)
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
})
Option Store 类似选项式API
export const useUserStore = defineStore('User', {
state: () => {
return {
count: 0,
}
},
getters: {
doubleCount: (state) => state.count * 2,
doubleCountPlus() {
return this.doubleCount + 1
},
},
actions: {
increment() {
this.count++
},
},
})
Setup Store 类似组合式API 的 setup 函数
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function increment() {
count.value++
}
return { count, increment }
})
使用 Store
- store 是一个用 reactive 包装的对象,所以使用方式和reactive对象一样
<script setup>
import { useUserStore } from '@/store/user'
const userStore= useUserStore()
let count = userStore.count
import { storeToRefs } from 'pinia'
const { name, doubleCount } = storeToRefs(userStore)
userStore.increment()
const { increment } = userStore
increment()
</script>
修改state的三种方法
- 直接在页面组件中改变数据
- 使用$patch改变数据
- patch object:是通过我们熟悉的 $patch 的 对象方式 来变化
- patch function:也是通过 $patch,但是是通过 函数的方式 来变化
- direct:通过调用 action 方法(推荐)
- 重置数据
<template>
<div>baseUrl:{{ baseUrl }}</div>
<div>ipList:{{ ipList }}</div>
<button @click="changeData">直接在页面组件中改变数据</button>
<button @click="changeDataByPatch">使用$patch改变数据</button>
<button @click="changeDataByAction">使用action改变数据</button>
<button @click="resetData">重置数据</button>
</template>
<script setup>
import useAppStore from "@/store/modules/app"
import { storeToRefs } from "pinia"
const store = useAppStore()
let { baseUrl, ipList } = storeToRefs(store)
function changeData() {
store.baseUrl = 'https://www.taobao.com/'
store.ipList.push('192.168.10.111')
}
function changeDataByPatch() {
store.$patch({
baseUrl: 'https://www.jd.com/',
ipList: ['192.168.10.777', '192.168.10.222', '192.168.10.888']
})
store.$patch((state) => {
state.baseUrl = 'https://www.jd.com/'
state.ipList[1] = '192.168.10.222'
})
}
function changeDataByAction() {
store.changeState('https://www.alibabagroup.com/cn/global/home')
}
function resetData() {
store.$reset()
}
</script>
订阅$subscribe:监听 state 里面的值是否发生了变化
cartStore.$subscribe((mutation, state) => {
console.log(mutation, state)
localStorage.setItem('cart', JSON.stringify(state))
})
cartStore.$subscribe(callback, { detached: true })
当前 getter 访问其他 store 的 getter
import { useOtherStore } from './other-store'
export const useStore = defineStore('main', {
getters: {
otherGetter(state) {
const otherStore = useOtherStore()
return state.localData + otherStore.data
},
},
})
订阅 action $onAction:监听每一个action的执行,并在执行前、执行后、执行出错时执行一些特定的逻辑。
const unsubscribe = someStore.$onAction(
({
name,
store,
args,
after,
onError,
}) => {
const startTime = Date.now()
console.log(`Start "${name}" with params [${args.join(', ')}].`)
after((result) => {
console.log(
`Finished "${name}" after ${
Date.now() - startTime
}ms.\nResult: ${result}.`
)
})
onError((error) => {
console.warn(
`Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`
)
})
}
)
unsubscribe()
someStore.$onAction(callback, true)