# pinia 2.x [官网](https://pinia.vuejs.org/introduction.html) **优势**: 1. 适配vue3的composition api特性 2. 可脱离组件使用 3. 支持热更新 4. 体积小(1KB) 5. 支持 TypeScript 6. 支持ssr 7. 进入vue官方库, 接近 Vuex 5.x 的提案 **不足**: 1. devtool 部分功能不支持 **和 vuex3.x/4.x 的比较**: 1. 没有mutation 2. action既可以执行异步方法, 也可以执行同步方法 3. 没有命名空间(或者说每个store都是一个空间, 扁平状态) 4. store是动态的 **本文档只整理vue3 composition api 部份** ## 安装 ```bash yarn add pinia ``` ## 简单使用 > store 有三个概念, state, getters, actions, 类似vue2中的data, computed, methods ```ts // 创建 // @/store/index.ts import type { App } from 'vue' import {createPinia} from 'pinia' const store = createPinia() export function setupStore(app: App){ app.use(store) } export {store} ``` ````ts // 使用 // @/main.ts import { createApp } from 'vue' import {setupStore} from './store' import App from './App.vue' const app = createApp(App) setupStore(app) app.mount('#app') ```` ```ts // 定义容器 // @/store/modules/app.ts import { defineStore } from "pinia" import {store } from '../index' // 返回值是个函数 export const useAppStore = defineStore({ // 唯一ID,可以配合 Vue devtools 使用 id: 'app', // 必须是函数: 避免服务端渲染时交叉请求污染数据; // 箭头函数: 为了更好的类型推导 state: (): any => ({ count: 0, // 这些属性都会自动推断类型 }), // getters有缓存 // getters函数默认接收一个可选参数state, 带上state时能自动推断函数返回, 不带state, 就得声明返回类型 getters: { countIsEven(): boolean { return this.count % 2 === 0 // 像vue2使用data一样 } }, actions: { // action不能用箭头函数, this问题 countAddOne(): void { this.count ++ } } }) // export const useStore = defineStore('app', {}), 也可以这样传, 第一个参数是id, 是唯一的 // 在setup之外的地方引用 export function useAppStoreWithOut(){ return useAppStore(store) } ``` ```vue ``` ```ts // 在组件外使用: 在组件内使用时, pinia已经注册到app实例了, 但在组件外时, 不一定. 所以需要手动传 store import {useAppStoreWithOut} from '@/store/modules/app' const appStore = useAppStoreWithOut() ``` **其他写法: 像setup函数一样定义store** ```ts export const useCounterStore = defineStore('counter', () => { const count = ref(0) function increment() { count.value++ } return { count, increment } }) ``` ## storeToRefs() ```ts // store是一个reactive, 不能解构, 否则会失去响应性. 想保存响应性, 需要使用 storeToRefs()方法 import {useAppStore} from '@/store/modules/app' import { storeToRefs } from 'pinia' const appStore = useAppStore() const {count} = storeToRefs(appStore) // 会自动跳过action和不是响应性的属性 // 当然在computed里返回 appStore.count 也能实现响应性 const isEven2 = computed(()=>{ return appStore.isEven }) // toRef('count', appStore.count) 也能实现响应性 ``` ## $reset() ```ts // 用$reset()方法来把state恢复为初始值 const appStore = useAppStore() store.$reset() ``` ## $patch() ```ts store.$patch({ list: [1, 2, 3, 4, 5], name: 'Abalam', }) // 这样写有时太消耗性能, 比如修改数组时还得新建一个数组 // $patch()可以接收一个函数作为参数, 该函数的默认参数是state store.$patch(state => { state.list.push(6) state.name = 'Jan' }) ``` ## $state ````ts // 通过store的$state属性,整个替换state对象 store.$state = { counter: 666, name: 'Paimon' } ```` ## $subscribe() ```ts // 用$subscribe()来订阅state的改变 appStore.$subscribe((mutation, state) => { console.log('mutation', mutation) /* mutation: { type: 'direct' | 'patch object' | 'patch function', storeId: 'app', } */ console.log('state', state) // 应用举例: state变化时存到localStorage }) // 默认情况下,state侦听会和组件绑定在一起(如果store是在组件的setup中)。这意味着,当组件卸载时,侦听会自动被移除。如果你需要在组件被卸载时,侦听仍然保持,需要给$subscribe()方法传递第二个参数true appStore.$subscribe(callback, true) ``` ```ts // 当然, 也可以用watch监听整个state watch( pinia.state, (state) => { // ... }, { deep: true } ) ``` ## $onAction() ```ts // 用store.$onAction()方法侦听、订阅action的调用与结果。在action调用前就会调用这个回调。可以在after回调中改变action的返回结果。在onError回调中可以处理错误。 // 应用场景: 在运行时跟踪错误 const ubsubscribe = someStore.$onAction( ({ name, store, args, after, onError }) => { const startTime = Date.now() // after会在完成调用后执行 after(result => { console.log(`花费时间: ${Date.now() - startTime} ms`) }) onError(error => { console.warn(error) }) } ) // 可手动移除监听 ubsubscribe() // 默认情况下,actions的侦听是在组件初始化是加上的(如果store是在一个组件的setup中使用)。这意味着组件卸载时,侦听会自动被移除。如果你想要组件卸载时,不移除侦听,在组件中调用$onAction时加上第二个参数true someStore.$onAction(callback, true) ```