Vue3 之 Pinia 核心概念(八)
核心概念
State:这是你的应用程序的状态,是一个响应式的对象。
Getters:类似于 Vuex 中的 getters,它们是基于 state 的计算属性。
Actions:类似于 Vuex 中的 mutations 和 actions,它们用于改变 state。但与 Vuex 不同的是,在 Pinia 中,mutations 和 actions 被合并为一个概念,即 actions。
其他特性:Pinia 还支持插件、热重载、服务器端渲染等。
安装
npm install pinia
# 或者使用 yarn
yarn add pinia
main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
app.mount('#app')
store/counter.js
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', {
// state
state: () => ({
count: 0,
}),
// getters
getters: {
doubleCount: (state) => state.count * 2,
},
// actions
actions: {
increment() {
this.count++
},
decrement() {
this.count--
},
// 你也可以在 actions 中使用其他 actions 或 getters
incrementBy(amount) {
this.count += amount
},
},
})
App.vue
<template>
<div>
<p>Count: {{ counterStore.count }}</p>
<p>Double Count: {{ counterStore.doubleCount }}</p>
<button @click="counterStore.increment">Increment</button>
<button @click="counterStore.decrement">Decrement</button>
<button @click="counterStore.incrementBy(5)">Increment by 5</button>
</div>
</template>
<script>
import { useCounterStore } from './store/counter'
export default {
setup() {
const counterStore = useCounterStore()
return { counterStore }
},
}
</script>
扩展 Store
由于有了底层 API 的支持,Pinia store 现在完全支持扩展。以下是你可以扩展的内容:
为 store 添加新的属性
定义 store 时增加新的选项
为 store 增加新的方法
包装现有的方法
改变甚至取消 action
实现副作用,如本地存储
仅应用插件于特定 store
1. 插件 (Plugins)
插件是通过 pinia.use() 添加到 pinia 实例的。最简单的例子是通过返回一个对象将一个静态属性添加到所有 store。
import { createPinia } from 'pinia'
// 创建的每个 store 中都会添加一个名为 `secret` 的属性。
// 在安装此插件后,插件可以保存在不同的文件中
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'
2. 持久化状态 (Persistence)
代码示例(使用第三方插件 pinia-plugin-persistedstate):
npm install pinia-plugin-persistedstate
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import createPersistedState from 'pinia-plugin-persistedstate';
const pinia = createPinia();
// 使用插件
pinia.use(createPersistedState({
storage: window.localStorage,
}));
const app = createApp(/* ... */);
app.use(pinia);
// ...
3. 订阅状态变化 (Subscribing to State Changes)
Pinia 允许你订阅 Store 的状态变化,以便在状态更新时执行某些操作。这可以通过 pinia.state.subscribe 方法实现。
代码示例(在插件中使用):
// 插件中的代码
app.pinia.state.subscribe((mutation, state) => {
// mutation 是一个对象,包含 type('direct' 或 'patch')和 key
// state 是 Store 的当前状态
console.log('State mutated:', mutation, state);
});
4. 使用 TypeScript 进行类型安全
如果你在使用 TypeScript,Pinia 提供了强大的类型支持,确保你的代码是类型安全的。你可以为 state、getters 和 actions 提供类型定义。
代码示例(使用 TypeScript):
import { defineStore } from 'pinia';
interface CounterState {
count: number;
}
export const useCounterStore = defineStore('counter', {
state: (): CounterState => ({
count: 0,
}),
// ...
});
5. 访问其他 Store
在 Pinia 中,一个 Store 可以很容易地访问另一个 Store 的状态和方法。你可以直接在 Store 的 actions 中使用 useStore 函数来访问其他 Store。
代码示例:
import { defineStore } from 'pinia';
import { useOtherStore } from './otherStore';
export const useCounterStore = defineStore('counter', {
// ...
actions: {
incrementAndDoSomethingElse() {
this.count++;
const otherStore = useOtherStore();
otherStore.doSomething();
},
},
});
在组件外部使用 Store
1. 创建 Pinia 实例和 Store
首先,你需要在你的主入口文件(如 main.js 或 main.ts)中创建 Pinia 实例,并使用它创建你的 Store。
// main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
const pinia = createPinia()
// 使用 Pinia
app.use(pinia)
// 挂载应用
app.mount('#app')
// 假设你有一个名为 useCounterStore 的 Store
// import { useCounterStore } from './stores/counter'
// 但在这里我们不会直接在 main.js 中使用它
2. 在组件外部使用 Store
示例:在路由守卫中使用 Store
假设你有一个名为 useUserStore 的 Store,用于管理用户状态,并且你想在路由守卫中检查用户是否已登录。
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import { useUserStore } from './stores/user' // 假设你的 Store 在这里
import pinia from '../main' // 引入 Pinia 实例(你可能需要根据你的项目结构来调整这个路径)
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes: [
// ...你的路由配置
],
})
// 在路由守卫中使用 Store
router.beforeEach((to, from, next) => {
// 使用 pinia.useStore() 获取 Store 实例
const userStore = pinia.useUserStore()
// 假设有一个 isLoggedIn 的 getter 或 state 属性
if (to.meta.requiresAuth && !userStore.isLoggedIn) {
// 重定向到登录页面或执行其他操作
next('/login')
} else {
next()
}
})
export default router