对比 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()
// ------------使用变量------
// userStore 是一个用 reactive 包装的对象
let count = userStore.count
// 所以reactive对象的属性不具有响应式,所以不能解构直接使用
// 若想解构,先将它每一个属性转化为响应式,类似roRefs()
import { storeToRefs } from 'pinia'
const { name, doubleCount } = storeToRefs(userStore)
// ----------使用方法---------
userStore.increment()
//或者解构:actions 的 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)
/* 第一种修改方式:虽然可以直接修改,但是出于代码结构来说,
* 全局的状态管理还是不要直接在各个组件处随意修改状态,
* 应放于 action 中统一方法修改(piain没有mutation)
* 并不推荐!!!
* */
function changeData() {
store.baseUrl = 'https://www.taobao.com/'
store.ipList.push('192.168.10.111')
}
/*第二种修改方式:使用$patch改变数据, $patch 可以同时修改多个值
* $patch也有两种的调用方式
* 第一种写法的在修改数组时,假如我只想修改 ipList 的中第2项 , 也需要传入整个数组, 所以一般都推荐使用第二种传入一个函数的写法
*/
function changeDataByPatch() {
// 第一种 $patch方法
store.$patch({
baseUrl: 'https://www.jd.com/',
ipList: ['192.168.10.777', '192.168.10.222', '192.168.10.888']
})
// 第二种 $patch方法
store.$patch((state) => {
state.baseUrl = 'https://www.jd.com/'
state.ipList[1] = '192.168.10.222'
})
}
// 第三种修改方式:调用store中的action改变数据的方法changeState
function changeDataByAction() {
store.changeState('https://www.alibabagroup.com/cn/global/home') //可以直接给actions里面的方法传递参数
}
// 重置数据
function resetData() {
store.$reset()
}
</script>
订阅$subscribe:监听 state 里面的值是否发生了变化
cartStore.$subscribe((mutation, state) => {
console.log(mutation, state)
// mutation.type //发生改变的三种方式: 'direct' | 'patch object' | 'patch function'
// mutation.storeId // store名字
// mutation.payload // 传递给 cartStore.$patch() 的对象。只有 mutation.type === 'patch object'的情况下才可用
// 每当状态发生变化时,将整个 state 持久化到本地存储。
localStorage.setItem('cart', JSON.stringify(state))
})
//subscription 会被绑定到添加它们的组件上 ,当该组件被卸载时,它们将被自动删除。如果想保留,设置 { detached: true } ,以将 state subscription 从当前组件中分离
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, // action 名称
store, // store 实例,类似 `someStore`
args, // 传递给 action 的参数数组
after, // 在 action 返回或解决后的钩子
onError, // action 抛出或拒绝的钩子
}) => {
// 为这个特定的 action 调用提供一个共享变量
const startTime = Date.now()
// 这将在执行 "store "的 action 之前触发。
console.log(`Start "${name}" with params [${args.join(', ')}].`)
// 这将在 action 成功并完全运行后触发。
// 它等待着任何返回的 promise
after((result) => {
console.log(
`Finished "${name}" after ${
Date.now() - startTime
}ms.\nResult: ${result}.`
)
})
// 如果 action 抛出或返回一个拒绝的 promise,这将触发
onError((error) => {
console.warn(
`Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`
)
})
}
)
// 手动删除监听器
unsubscribe()
// 此订阅器即便在组件卸载之后仍会被保留
someStore.$onAction(callback, true)
本文含有隐藏内容,请 开通VIP 后查看