初识 Pinia.js
前言
什么是 Pinia.js ? Pinia 是一个用于 Vue 的状态管理库,类似 Vuex, 是 Vue 的另一种状态管理方案。记得之前在面试某公司的时候,面试官问我一个问题,你会在每个 Vue 项目中都引入 Vuex 嘛,当时的我以为能用好用就可以了,没有考虑代码繁琐冗余等问题,如果在有同样的问题,我可能会回答,体积不是很庞大的项目,我会更倾向采用轻量级的 Pinia.js
。
选择 Pinia 的理由
- 未来趋势
Pinia
本质上是由 Vuex 核心成员在 Vue 3.x/4.x 的基础上进行改进开发。据官网描述, Pinia 已实现了在 Vuex5.x 中想要的大部分内容。 - 体积方面
Pinia
在重约 1kb前提下,实现了 vuex 中的 state,getters,actions,这里 Pinia,把 mutations 去掉,可通过$patch
来批量修改 state 中的属性也可以直接字面量赋值修改 - 开发工具方面
Pinia
与 Vue devtools 挂钩,为您提供增强的 Vue 2 和 Vue 3 开发体验。 - 使用方面,可直接引入即用,方便简单快捷,没有模块概念。某个页面/组件需要这个库可直接引入使用,库与库之间也可以引入使用。
- 完美支持 TS ,与 Vuex 相比,Pinia 提供了一个更简单的 API,具有更少的仪式,提供了 Composition-API 风格的 API,最重要的是,在与
TypeScript
一起使用时具有可靠的类型推断支持。
对比 Vue 3.x/4.x
mutations
不再存在。他们经常被认为是非常冗长的。他们最初带来了 devtools 集成,但这不再是问题。- 无需创建自定义复杂包装器来支持 TypeScript,所有内容都是类型化的,并且 API 的设计方式尽可能利用 TS 类型推断。
- 不再需要注入字符串、导入函数、调用它们,享受自动完成功能!
- 无需动态添加
store
,默认情况下它们都是动态的,您甚至都不会注意到。请注意,您仍然可以随时手动使用仓库进行注册,但因为它是自动的,您无需担心。 - 不再有模块的嵌套结构。您仍然可以通过在另一个仓库中导入和使用商店来隐式嵌套商店,但 Pinia 通过设计提供平面结构,同时仍然支持仓库之间的交叉组合方式。你甚至可以有 store 的循环依赖。
- 没有
namespaced
命名空间的模块。鉴于商店的扁平架构,“命名空间” store 是其定义方式所固有的,您可以说所有store
都是有命名空间的。
安装
-
yarn add pinia # or with npm npm install pinia
挂载全局实例
-
main.js 中挂载, 我这里使用的是 vue3.2 ,vue 2的写法也是 app.use 即可。
-
import { createPinia } from 'pinia' import { createApp } from 'vue' import App from './App.vue' createApp(App).use(createPinia()).mount('#app')
示例
- 这边我将用 Pinia 完成简单的一个登录登出需求,具体实现为, vue 父子组件。
- 父组件展示一个用户当前登录状态字段。 'isLogin' ,该字段存储将从我们的 userStore 当中获取。
- 子组件展示一个 userName && passWord 输入框来进行数据录入,存储在我们的 userStore 当中。
- store 方面我们使用 store 中的 action 进行逻辑处理,与 state 操作。
1. 创建一个 userStore
- 创建 userStore 完成用户登录登出逻辑的攥写。
user.js
-
这里给到 store actions 中两个方法 login / logout, 并简单写一个用户校验方法。
-
这里可以发现
Pinia
的actions
是支持同步 && 异步的。 -
import { defineStore } from 'pinia' /** * Simulate a login * @param {string} a * @param {string} p */ // 简单校验方法 function apiLogin(a, p) { if (a === 'ed' && p === 'ed') return Promise.resolve({ isAdmin: true }) if (p === 'ed') return Promise.resolve({ isAdmin: false }) return Promise.resolve({ isAdmin: false }) } // 导出 user 库 export const useUserStore = defineStore({ id: 'user', state: () => ({ name: 'Eduardo', isAdmin: false, }), actions: { // 退出 logout() { this.$patch({ name: '', isAdmin: false, }) }, /** * @param {string} username * @param {string} password */ async login(username, password) { const userData = await apiLogin(username, password) this.$patch({ name: username, ...userData, }) return userData }, }, })
2. 父组件 vue 代码
- 这里只展示一个字段,并且附上监听 store 字段变化的方式,可用用作其他逻辑处理
user.vue
-
<template> <div> <h1>父组件</h1> <h2>当前用户登录状态 {{ userStore.isLogin ? "在线" : "离线" }}</h2> <Demo> </Demo> </div> </template> <script> import { watch, } from "vue" import Demo from './components/Demo' import { useUserStore } from './stores/user' export default { name: 'User', setup() { // 引入 const userStore = useUserStore() watch(() => userStore.isLogin, (newValue, oldValue) => { console.log(newValue, oldValue) console.log("当前用户登录状态") }) return { userStore } }, components: { Demo, }, } </script>
3. 子组件 vue 代码
- 这里展示两个输入框,用户名和密码的输入。
- 登录登出两个按钮事件。
- 完成通过操作 userStore 对父组件之间的通信。
login.vue
-
<template> <div> <h1>子组件</h1> <label>用户名:</label> <input type="text" v-model="userName" /> <label>密码:</label> <input type="text" v-model="passWord" /> <button style="margin-left: 30px" @click="pinaiEvent">登录</button> <div style="margin-top: 30px"> <button @click="userStoreReSetEvent">退出登录</button> </div> <h2>msg:{{ logMsg }}</h2> </div> </template> <script> import { useUserStore } from '../stores/user' import { reactive, toRefs, } from 'vue' export default { name: 'Login', setup() { const state = reactive({ modalOpen: false, userName: '', passWord: '', logMsg: '', }) const userStore = useUserStore() const pinaiEvent = async () => { let temp = await userStore.login(state.userName, state.passWord) console.log(temp) if (temp.isLogin) { state.logMsg = '登录成功' return } state.logMsg = '登录失败' } // user仓库制空 const userStoreReSetEvent = () => { // userStore.$reset() userStore.logout() state.logMsg = '退出登录' } return { ...toRefs(state), pinaiEvent, userStore, userStoreReSetEvent } }, } </script>
4. 效果展示
5. 功能简述
Pinia
也是有很多实用的 API 比如可以在 vue 组件中直接操作 store 清空store.$reset()
- 也可以直接在 vue 组件中操作 store 中的 state实现双向绑定,但是这边我个人使用还是倾向于通过 store 内部 actions 去操作 state, 不然项目迭代过久,会变的难以维护。
- 操作 state 可以在 action 中去字面量赋值 ,也可以用
$patch
来修改多个 state 中属性。 - 在 Pinia 当中只是移除了大家印象里的
mutionse
,getters
还是存在的,大家可以根据按需使用。
总结
-
至此完成了一个 Pinia 简单的小功能的实现,大家可以根据自己项目按需使用,感觉 Pinia 这种轻量化仓库会是近段时间的一个趋势。
-
大家有什么疑惑和更实用的 Pinia 小技巧欢迎在评论区沟通,共同探讨。
-
以下为官方文档,方便同学们进一步学习Pinia。