- 本文来源于 Home | Pinia 中文文档 (web3doc.top)
1、概述
- Pinia 是 Vue 的存储库,它容许您跨组件 / 页面共享状态。
1)比照 Vuex
- 更小,只有 1kb;
- 更简略,actions 能够同时解决同步工作和异步工作;
- 不再有命名空间模块,然而各个 store 之间互相独立并且能够相互援用。
- 组合式 API 格调和良好的 TS 反对。
2)装置
-
Vue3 创立一个 pinia(根存储)并将其传递给应用程序
import {createPinia} from 'pinia' app.use(createPinia())
- Vue 2,您还须要装置一个插件并将创立的
pinia
注入应用程序的根目录 - 须要装置组合 API:
@vue/composition-api
import {createPinia, PiniaVuePlugin} from 'pinia'
Vue.use(PiniaVuePlugin)
const pinia = createPinia()
new Vue({
el: '#app',
// ...
pinia,
})
2、store
1)定义
-
Store 是应用
defineStore()
定义的,并且它须要一个 惟一 名称,作为第一个参数传递- 这个 name,也称为 id,是必要的,Pinia 应用它来将 store 连贯到 devtools。
import {defineStore} from 'pinia' // useStore 能够是 useUser、useCart 之类的任何货色 // 第一个参数是应用程序中 store 的惟一 id export const useStore = defineStore('main', {// other options...})
2)模块化
- 能够依据须要定义任意数量的 store,并且 您应该在不同的文件中定义每个 store 以充分利用 pinia。
3、state
-
在 Pinia 中,状态被定义为返回初始状态的函数。
import {defineStore} from 'pinia' const useStore = defineStore('storeId', { // 举荐应用 残缺类型推断的箭头函数 state: () => { return { // 所有这些属性都将主动推断其类型 counter: 0, name: 'Eduardo', isAdmin: true, } }, })
1)拜访“state”
- 默认状况下,您能够通过
store
实例拜访状态来间接读取和写入状态
const store = useStore()
store.counter++
2)扭转状态
-
除了间接用
store.counter++
批改 store,你还能够调用$patch
办法。它容许您应用局部“state”对象同时利用多个更改。store.$patch({ counter: store.counter + 1, name: 'Abalam', })
-
$patch
办法也承受一个函数来批量批改汇合内局部对象的状况:cartStore.$patch((state) => {state.items.push({ name: 'shoes', quantity: 1}) state.hasChanged = true })
3)重置状态
- 能够通过调用 store 上的
$reset()
办法将状态 重置 到其初始值
const store = useStore()
store.$reset()
4)替换state
-
能够通过将其
$state
属性设置为新对象来替换 Store 的整个状态:store.$state = {counter: 666, name: 'Paimon'}
5)订阅状态
- 能够通过 store 的
$subscribe()
办法查看状态及其变动。 - 与惯例的
watch()
相比,应用$subscribe()
的长处是 subscriptions 只会在 patches 之后触发一次。 -
当组件被卸载时,它们将被主动删除。如果要在卸载组件后保留它们,请将
{detached: true}
作为第二个参数传递给 detach 以后组件的 state subscription:export default {setup() {const someStore = useSomeStore() // 此订阅将在组件卸载后保留 someStore.$subscribe(callback, { detached: true}) // ... }, }
4、getters
-
Getter 齐全等同于 Store 状态 的 computed
- 箭头函数接管 state 作为第一个参数。
- 惯例函数通过 this 拜访到整个 store 实例,然而须要定义返回类型。
export const useStore = defineStore('main', {state: () => ({counter: 0,}), getters: { // 主动将返回类型推断为数字 doubleCount(state) {return state.counter * 2}, // 返回类型必须明确设置 doublePlusOne(): number {return this.counter * 2 + 1}, }, })
1)拜访其余 getter
- 与计算属性一样,您能够组合多个 getter。通过
this
拜访任何其余 getter。
export const useStore = defineStore('main', {state: () => ({counter: 0,}),
getters: {
// 类型是主动推断的,因为咱们没有应用 `this`
doubleCount: (state) => state.counter * 2,
doubleCountPlusOne() {return this.doubleCount + 1},
},
})
2)将参数传递给 getter
-
Getters 只是幕后的 computed 属性,因而无奈向它们传递任何参数。然而,您能够从 getter 返回一个函数以承受任何参数:
export const useStore = defineStore('main', { getters: {getUserById: (state) => {return (userId) => state.users.find((user) => user.id === userId) }, }, }) // App.vue <template> <p>User 2: {{getUserById(2) }}</p> </template>
-
在执行此操作时,getter 不再缓存,它们只是您调用的函数。然而,您能够在 getter 自身外部利用闭包缓存一些后果。
export const useStore = defineStore('main', { getters: {getActiveUserById(state) {const activeUsers = state.users.filter((user) => user.active) return (userId) => activeUsers.find((user) => user.id === userId) }, }, })
3)拜访其余 Store 的 getter
- 要应用其余存储 getter,您能够间接在 getter 外部应用它
import {useOtherStore} from './other-store'
export const useStore = defineStore('main', {state: () => ({// ...}),
getters: {otherGetter(state) {const otherStore = useOtherStore()
return state.localData + otherStore.data
},
},
})
5、actions
- Actions 相当于组件中的 methods,能够通过 this 拜访到整个 store 实例。
export const useStore = defineStore('main', {state: () => ({counter: 0,}),
actions: {increment() {this.counter++},
randomizeCounter() {this.counter = Math.round(100 * Math.random())
},
},
})
actions
能够是异步的,您能够在其中await
任何 API 调用甚至其余操作!
export const useUsers = defineStore('users', {state: () => ({
userData: null,
// ...
}),
actions: {async registerUser(login, password) {
try {this.userData = await api.post({ login, password})
showTooltip(`Welcome back ${this.userData.name}!`)
} catch (error) {showTooltip(error)
// 让表单组件显示谬误
return error
}
},
},
})
1)拜访其余 store 操作
- 要应用另一个 store,您能够间接在 action 外部应用它:
import {useAuthStore} from './auth-store'
export const useSettingsStore = defineStore('settings', {state: () => ({// ...}),
actions: {async fetchUserPreferences(preferences) {const auth = useAuthStore()
if (auth.isAuthenticated) {this.preferences = await fetchPreferences()
} else {throw new Error('User must be authenticated')
}
},
},
})
2)订阅 Actions
- 能够应用
store.$onAction()
订阅 action 及其后果。传递给它的回调在 action 之前执行。 after
解决 Promise 并容许您在 action 实现后执行函数。onError
容许您在解决中抛出谬误。这些对于在运行时跟踪谬误很有用。
const unsubscribe = someStore.$onAction(
({
name, // action 的名字
store, // store 实例
args, // 调用这个 action 的参数
after, // 在这个 action 执行结束之后,执行这个函数
onError, // 在这个 action 抛出异样的时候,执行这个函数
}) => {
// 记录开始的工夫变量
const startTime = Date.now()
// 这将在 `store` 上的操作执行之前触发
console.log(`Start "${name}" with params [${args.join(',')}].`)
// 如果 action 胜利并且齐全运行后,after 将触发。// 它将期待任何返回的 promise
after((result) => {
console.log(`Finished "${name}" after ${Date.now() - startTime
}ms.\nResult: ${result}.`
)
})
// 如果 action 抛出或返回 Promise.reject,onError 将触发
onError((error) => {
console.warn(`Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`
)
})
}
)
// 手动移除订阅
unsubscribe()
-
当组件被卸载时,它们将被主动删除。如果要在卸载组件后保留它们,请将
true
作为第二个参数传递给以后组件的 detach action subscription:export default {setup() {const someStore = useSomeStore() // 此订阅将在组件卸载后保留 someStore.$onAction(callback, true) // ... }, }
6、plugins
- 应用
pinia.use()
将插件增加到 pinia 实例中。 - 装置此插件后创立的每个 store 增加一个名为
secret
的属性
import {createPinia} from 'pinia'
// 这可能在不同的文件中
function SecretPiniaPlugin() {return { secret: 'the cake is a lie'}
}
const pinia = createPinia()
// 将插件提供给 pinia
pinia.use(SecretPiniaPlugin)
// 在另一个文件中
const store = useStore()
store.secret // 'the cake is a lie'
- Pinia 插件是一个函数,能够抉择返回要增加到 store 的属性。它须要一个可选参数,一个 context:
export function myPiniaPlugin(context) {context.pinia // 应用 `createPinia()` 创立的 pinia
context.app // 应用 `createApp()` 创立的以后应用程序(仅限 Vue 3)context.store // 插件正在裁减的 store
context.options // 定义存储的选项对象传递给 `defineStore()`
// ...
}
1)增加新的内部属性
-
当增加内部属性、来自其余库的类实例或仅仅是非响应式的货色时,您应该在将对象传递给 pinia 之前应用
markRaw()
包装对象。这是一个将路由增加到每个 store 的示例:import {markRaw} from 'vue' // 依据您的路由所在的地位进行调整 import {router} from './router' pinia.use(({store}) => {store.router = markRaw(router) })
2)在插件中调用 $subscribe
-
您也能够在插件中应用 store.subscribe和store.onAction
pinia.use(({store}) => {store.$subscribe(() => {// 在存储变动的时候执行}) store.$onAction(() => {// 在 action 的时候执行}) })
3)引入新的 store 属性
-
向 store 增加新属性时,您还应该扩大
PiniaCustomProperties
接口。import 'pinia' declare module 'pinia' { export interface PiniaCustomProperties { // 通过应用 setter,咱们能够同时容许字符串和援用 set hello(value: string | Ref<string>) get hello(): string // 你也能够定义更简略的值 simpleNumber: number } }
-
而后能够平安地写入和读取它:
pinia.use(({store}) => { store.hello = 'Hola' store.hello = ref('Hola') store.number = Math.random() // @ts-expect-error: we haven't typed this correctly store.number = ref(Math.random()) })