一、简介
Pinia 是 Vue 的存储库,它允许你跨组件/页面共享状态。如果你熟悉 Composition API,你可能已经使用过简单的
export const state = reactive({}),
这对于单页应用程序来说是正确的,但如果它是服务器端呈现的,则会将你的应用程序暴露给安全漏洞。
即使在小型单页应用程序中,你也可以从使用 Pinia 中获得很多好处:
- 开发工具支持 - 跟踪动作、突变的时间表- store只出现在使用它们的组件中- 时间旅行和更容易的调试
- 热模块更新 - 在不重新加载页面的情况下修改你的store- 在开发时保持任何现有状态
- 插件:使用插件扩展 Pinia 功能
- 为 JS 用户提供适当的 TypeScript 支持或自动完成功能
- 服务器端渲染(SSR)支持
Pinia 有以下特点:
- 完整的 TypeScript 的支持;
- 足够轻量,压缩后的体积约2kb;
- 去除 mutations,只有 state,getters,actions;
- actions 支持同步和异步;
- 没有模块嵌套,只有 store 的概念,store 之间可以自由使用,更好的代码分割;
- 无需手动添加 store,store 一旦创建便会自动添加;
二、入门
1、安装
yarn add pinia
// or with npm
npm install pinia
注意:
如果你的应用使用 Vue2,你还需要安装composition api:
@vue/composition-api;
如果你使用 Nuxt,则应遵循这些说明;如果你使用的是 Vue CLI,你可以试试这个非官方的插件。
2、何为 Store ?
一个 Store(如 Pinia)是一个实体,它持有未绑定到你的组件树的状态和业务逻辑。换句话说,它托管全局状态。它有点像一个始终存在并且每个人都可以读取和写入的组件。它包含三个概念,state、**getters **和 actions,并且可以假设这些概念等同与 data、computed 和 methods 在组件中。
3、何时使用 Store ?
Store 应该包含可以在整个应用程序中访问的数据。这包括在许多地方使用的数据,例如在导航栏中显示的用户信息,以及需要通过页面保存的数据,如非常复杂的多步骤表单。
另一方面,你应该避免在 Store 中包含可能托管在组件中的本地数据,例如页面本地元素的可见性。并非所有应用程序都需要访问全局状态,但如果你需要,Pania 将使你能更轻松地完成。
三、基本使用
1、Store
在 main.js 中创建并使用
import { createApp } from 'vue';
import App from './App.vue';
import { createPinia } from 'pinia';
const app = createApp(App);
const pinia = createPinia();
app.use(pinia).mount('#app');
2、State
定义 state
在 src/pinia 下面创建一个 count.js
import { defineStore } from 'pinia'
export const useStore = defineStore({
id: 'count', // id必填,且需要唯一
state: () => {
return {
count: 0
}
}
})
// 另外一种方式
export const useStore = defineStore('count', { // 将id作为第一个参数
state: () => {
return {
count: 0
}
}
})
获取 state
<template>
<div>
<p>{{ countStore.count }}</p>
</div>
</template>
<script>
import { useStore } from "../pinia/count.js";
export default {
setup() {
let countStore = useStore();
return {
countStore
};
}
}
</script>
也可以结合 computed 获取
let count = computed(() => countStore.count)
state 也可以使用解构,但使用解构会使其失去响应式,这时候可以用 pinia 的 storeToRefs
import { storeToRefs } from 'pinia';
let { count } = storeToRefs(countStore);
重置 state
可以通过调用 store 上的方法将 state 重置为其初始值:
$reset()
let countStore = useStore();
countStore.$reset();
更换 state
可以通过将 store 中的
$state
属性设置为新对象来替换 store 的整个 state
let countStore = useStore();
countStore.$state = { count: 0 };
修改 state
可以如下直接修改 state
let countStore = useStore();
countStore.count++
但一般不建议这么做,而是通过 actions 去修改 state,actions 里可以直接通过 this 访问
// count.js
export const useStore = defineStore({
id: 'count',
state: () => {
return {
count: 0
}
},
actions: { // 建议通过action修改state,更符合业务逻辑
increment() {
this.count++
}
}
})
<script>
import { useStore } from "../pinia/count.js";
export default {
setup() {
let countStore = useStore();
countStore.increment(); // 通过actions修改state,更符合业务逻辑
return {
countStore
};
}
}
</script>
订阅(监听)state
可以通过
$subscribe()
观察 state 及其变化,类似于 Vuex 的subscribe 方法
let countStore = useStore();
countStore.$subscribe((mutation, state) => {
console.log(mutation);
console.log("监听count变化触发的回调,count的值为:", state.count);
count = state.count;
});
// 可以在监听到state变化后执行某些操作
当 state 变化时,控制台打印
3、Getters
创建 getters
export const useStore = defineStore({
id: 'count',
state: () => {
return {
count: 0
}
},
getters: {
doubleCount(state) {
return state.count * 2
}
}
})
使用其他 getters
大多数时候,getters 只会依赖状态,但是有时候可能需要使用其他 getters
export const useStore = defineStore({
id: 'count',
state: () => {
return {
count: 0
}
},
getters: {
doubleCount(state) {
return state.count * 2;
},
doublePlusOne() {
return this.doubleCount + 1;
}
}
})
可以直接在 countStore 实例上访问 getters
<template>
<div>
<p>{{ countStore.doubleCount }}</p>
<p>{{ countStore.doublePlusOne }}</p>
</div>
</template>
访问其他 store 中的 getters
import { useOtherStore } from './other-store'
export const useStore = defineStore({
id: 'count',
state: () => ({
// ...
}),
getters: {
otherGetter(state) {
let otherStore = useOtherStore();
return state.count + otherStore.data;
},
},
})
4、Actions
创建 actions
export const useStore = defineStore({
id: 'count',
state: () => {
return {
count: 0
}
},
actions: {
increment() {
this.count++;
},
randomCount() {
this.count = Math.round(100 * Math.random());
}
}
})
像 getters 一样,actions 通过完全输入(和自动完成)支持访问整个 store 实例。与它们不同的是,它可以是异步的。
异步 actions
actions 可以像写一个简单的函数一样支持 async/await 的语法,让你愉快地应付异步处理的场景
export const useStore = defineStore({
id: 'login',
actions: {
async login(account, pwd) {
let { data } = await api.login(account, pwd);
return data;
}
}
})
访问其他 store 中的 actions
import { useAuthStore } from './auth-store'
export const useSettingsStore = defineStore({
id: 'settings',
state: () => ({
// ...
}),
actions: {
async fetchUserPreferences(preferences) {
let authStore = useAuthStore();
if (authStore.isAuthenticated) {
this.preferences = await fetchPreferences();
} else {
throw new Error('User must be authenticated');
}
}
}
})
订阅 actions
可以使用
store.$onAction()
观察 actions 及其结果。传递给它的回调在操作本身之前执行。
after
处理承诺并允许你更改操作的返回值。
onError
允许你阻止错误传播。这些对于在运行时跟踪错误很有用,类似于Vue 文档中的这个技巧。
这是一个在运行操作之前和它们解决/拒绝之后记录的示例
// 订阅(监听)actions
store.$onAction(
({
name, // name of the action
store, // store instance, same as `someStore`
args, // array of parameters passed to the action
after, // hook after the action returns or resolves
onError, // hook if the action throws or rejects
}) => {
console.log("name:",name);
console.log("store:",store);
console.log("before store.count:",store.count);
console.log("args",args);
// this will trigger if the action succeeds and after it has fully run.
// it waits for any returned promised
after(() => {
console.log("after store.count:",store.count);
});
// this will trigger if the action throws or returns a promise that rejects
onError((error) => {
console.log(error);
});
}
);
当触发 actions 时,控制台打印
默认情况下,操作订阅绑定到添加它们的组件(如果 store 位于组件的 内部
setup()
)。意思是,当组件被卸载时,它们将被自动删除。如果要在卸载组件后保留它们,请将
true
作为第二个参数传递来把操作订阅与当前组件分离
export default {
setup() {
let someStore = useSomeStore();
// this subscription will be kept after the component is unmounted
someStore.$onAction(callback, true)
// ...
},
}
四、与 Vuex 的比较
1、优缺点
Vuex 的优点
- 支持调试功能,如时间旅行和编辑
- 适用于大型、高复杂度的Vue.js项目
Vuex 的缺点
- 从 Vue 3 开始,getter 的结果不会像计算属性那样缓存
- Vuex 4有一些与类型安全相关的问题
Pinia 的优点
- 完整的 TypeScript 支持:与在 Vuex 中添加 TypeScript 相比,添加 TypeScript 更容易
- 极其轻巧(体积约 1KB)
- store 的 action 被调度为常规的函数调用,而不是使用
dispatch
方法或MapAction
辅助函数,这在 Vuex 中很常见 - 支持多个Store
- 支持 Vue devtools、SSR 和 webpack 代码拆分
Pinia 的缺点
- 不支持时间旅行和编辑等调试功能
2、使用场景
由于Pinea是轻量级的,体积很小,它比较适合中小型应用。它也适用于低复杂度的Vue.js项目,因为调试功一些能,如时间旅行和编辑仍然不被支持。
将 Vuex 用于中小型 Vue.js 项目是过度的,因为它重量级的,对性能降低有很大影响。因此,Vuex 适用于大规模、高复杂度的 Vue.js 项目。
据Vue.js 核心团队成员并积极参与 Vuex 设计的 Pinia 的创建者(Eduardo San Martin Morote)所说,Pania 和 Vuex 的相似之处多于不同之处:
Pinia 试图尽可能地接近 Vuex 的理念。它的设计是为了测试 Vuex 的下一次迭代的建议,它是成功的,因为我们目前有一个开放的 RFC,用于 Vuex5,其API与 Pinea 使用的非常相似。我对这个项目的个人意图是重新设计使用全局 store 的体验,同时保持 Vue 的平易近人的理念。我保持 Pinea 的API与 Vuex 一样接近,因为它不断向前发展,使人们很容易迁移到Vuex,甚至在未来融合两个项目(在Vuex下)。
五、总结
以上就是关于 Pinia 的一些基本介绍,Pinia 的内容远远不止这些,对于我们使用者而言,首先掌握最基本用法,然后再去深入更多的用法,如在 Pinia 中使用插件来扩展功能等。
更多内容请前往官网了解
版权归原作者 前端不释卷leo 所有, 如有侵权,请联系我们删除。