什么是pinia
Pinia 最初是在 2019 年 11 月左右重新设计使用Composition API 。从那时起,最初的原则仍然相同,但 Pinia 对 Vue 2 和 Vue 3 都有效,并且不需要您使用组合 API。 除了安装和 SSR 之外,两者的 API 都是相同的,并且这些文档针对 Vue 3,并在必要时提供有关 Vue 2 的注释,以便 Vue 2 和 Vue 3 用户可以阅读!
为什么要使用 Pinia?
- Vuex的官方强烈推荐新新项目用Pinia。
- Pinia 的 API 设计非常接近
Vuex 5
的提案。 - pinia相比vuex4,对于vue3的pinia的api更友好。
- pinia相比vuex4,具备完善的 类型推荐 ,对 TS 支持很友好。
- Vue开发者工具支持pinia。
Pinia 核心特性
-
Pinia 没有
Mutations
-
Actions
支持同步和异步 -
没有模块的嵌套结构
Pinia 通过设计提供扁平结构,就是说每个 store 都是互相独立的,谁也不属于谁,也就是扁平化了,更好的代码分割且没有命名空间。当然你也可以通过在一个模块中导入另一个模块来隐式嵌套 store,甚至可以拥有 store 的循环依赖关系
-
更好的
TypeScript
支持不需要再创建自定义的复杂包装器来支持 TypeScript 所有内容都类型化,并且 API 的设计方式也尽可能的使用 TS 类型推断
-
不需要注入、导入函数、调用它们,享受自动补全,让我们开发更加方便
-
无需手动添加 store,它的模块默认情况下创建就自动注册的
-
Vue2 和 Vue3 都支持
除了初始化安装和SSR配置之外,两者使用上的API都是相同的
-
支持
Vue DevTools
跟踪 actions, mutations 的时间线
在使用了模块的组件中就可以观察到模块本身
支持 time-travel 更容易调试
在 Vue2 中 Pinia 会使用 Vuex 的所有接口,所以它俩不能一起使用
但是针对 Vue3 的调试工具支持还不够完美,比如还没有 time-travel 功能 -
模块热更新
无需重新加载页面就可以修改模块 热更新的时候会保持任何现有状态
-
支持使用插件扩展 Pinia 功能
-
支持服务端渲染
Pinia 和 Vuex
Vuex: State
、Gettes
、Mutations
(同步)、Actions
(异步)
Pinia: State
、Gettes
、Actions
(同步异步都支持)
Vuex 当前最新版是 4.x
- Vuex4 用于 Vue3
- Vuex3 用于 Vue2
Pinia 当前最新版是 2.x
- 即支持 Vue2 也支持 Vue3
就目前而言 Pinia 比 Vuex 好太多了,解决了 Vuex 的很多问题,所以笔者也非常建议直接使用 Pinia,尤其是 TypeScript 的项目
pinnia的集成
- 安装依赖
yarn add pinia
ornpm i pinia
; - 在main.js中创建pinia
const pinia = createPinia()
; - 将创建好的pinia应用到app中;
- 定义store;
- 创建store, 页面或组件中导入定义好的业务store,并调用userStore()。
创建单个store的步骤
1. 创建store/user.js
2. const userStore = defineStore("storeId",{
state:()=>{return {}},
actions:{},
getters:{}
});
### 配置对象有state,getters, actions
3. 导出userStore
注意:
pinia中store可以创建多个;
定义defineStore并没有直接创建store,而是定义了一个创建store的函数。
(1)在main.js
import { createPinia } from 'pinia';
import { createApp } from 'vue';
import App from './App.vue'
const app = createApp(App);
const pinia = createPinia();
app.use(pinia);
app.mount('#app');
(2)store/userStore.js
import { defineStore } from "pinia";
const useUserStore = defineStore('userStore', {
state: ()=> {
return {
userName: 'heihei',
}
},
actions: ()=>{
},
getters: ()=>{
}
})
export default useUserStore;
(3) 在组件中使用
<script setup>
import { useRoute } from 'vue-router';
import useUserStore from '../store/uerStore';
const userStore = useUserStore();
</script>
<template>
Home
</template>
actions的使用
在userSotre的actions中提供方法并且修改数据
import { defineStore } from "pinia";
const useUserStore = defineStore('userStore', {
state: () => {
return {
userName: 'heihei',
age: 18
}
},
actions: {
increment() {
this.age++
},
incrementAsync() {
setTimeout(() => {
this.age++
}, 1000)
}
},
getters: {
}
})
export default useUserStore;
在Home组件中使用
<script setup>
import { useRoute } from 'vue-router';
import useUserStore from '../store/uerStore';
const userStore = useUserStore();
</script>
<template>
Home
{{userStore.age}}
<button @click="userStore.increment">+1</button>
<button @click="userStore.incrementAsync">异步+1</button>
</template>
getters的使用
作用
pinia中的getters和vuex中的基本是一样的,计算state的状态值。
步骤
- 在getters 中计算方法
- getters中的方法直接操作数据(this.数据名)
- 在template中使用 (store的变量名.方法名) 在getters中提供计算属性
import { defineStore } from 'pinia'
// 1. 创建store
// 参数1:store的唯一表示
// 参数2:对象,可以提供state actions getters
const useCounterStore = defineStore('counter', {
state: () => {
return {
count: 0,
}
},
getters: {
double() {
return this.count * 2
},
},
actions: {
increment() {
this.count++
},
incrementAsync() {
setTimeout(() => {
this.count++
}, 1000)
},
},
})
export default useCounterStore
在组件中使用
<h1>根组件---{{ counter.count }}</h1>
<h3>{{ counter.double }}</h3>
store间的取值
步骤
- 创建另一个store, useAccountStore ;
- 在useUserStore中导入useAccoutStore
- 在useUserStore中使用useAccoutStore 的数据和方法
useAccountSore
import { defineStore } from "pinia";
const useAccountStore = defineStore('account',{
state:()=>{
return{
accountNumber: '123456',
money: 1000000
}
},
actions:{
addMoney(){
this.money+=100;
}
}
})
export default useAccountStore;
useUserStore
import { defineStore } from "pinia";
import useAccountStore from "./account";
const useUserStore = defineStore('userStore', {
state: () => {
return {
userName: 'heihei',
age: 18
}
},
actions: {
increment(){
this.age +=1;
},
incrementAsync(){
setTimeout(()=>{
this.age -= 1
},1000)
},
addMoney(){
const account = useAccountStore();
account.addMoney();
console.log(account.money)
}
},
getters: {
descAge(){
return this.age - 10;
}
}
})
export default useUserStore;
pinia模块化
在复杂项目中,不可能把多个模块的数据都定义到一个store中,一般来说会一个模块对应一个store,最后通过一个根store进行整合
步骤
- 创建store/index文件;
- 在index.js中将给store导入;
- 添加统一导出useStore方法
(1)新建store/user.js文件
import { defineStore } from 'pinia'
const useUserStore = defineStore('user', {
state: () => {
return {
name: 'zs',
age: 100,
}
},
})
export default useUserStore
新建store/index.js
import useUserStore from './user'
import useCounterStore from './counter'
// 统一导出useStore方法
export default function useStore() {
return {
user: useUserStore(),
counter: useCounterStore(),
}
}
在组件中使用
<script setup>
import { storeToRefs } from 'pinia'
import useStore from './store'
const { counter } = useStore()
// 使用storeToRefs可以保证解构出来的数据也是响应式的
const { count, double } = storeToRefs(counter)
</script>