文章目录
1、Pinia 的介绍
1.1、pinia 版本问题
Pinia是轻便化的全局状态管理器
中文网:pinia.web3doc.top/introductio…
源码:github.com/vuejs/pinia
- Vuex
Vuex当前最新版本是4.x,
Vuex4用于Vue3;
Vuex3用于Vue2
- Pinia
Pinia的最新版本是2.x,被认为是Vuex5。
对Vue2和Vue3做到很好的支持,也就是老项目也可以使用Pinia。
1.2、优点
- 支持Vue专用开发工具dev-tools
跟踪动作、突变的时间线
Store 出现在使用它们的组件中
time travel 和 更容易的调试
- 热模块更换
在不重新加载页面的情况下修改您的 Store
在开发时保持任何现有状态
- 支持扩展插件:使用插件扩展 Pinia 功能
如: 持久化工具 pinia-plugin-persist,可以非常方便地进行本地存储
- 更好的支持 TypeScript
- 支持服务器端渲染
1.3、pinia与 Vuex 的比较
与 Vuex 相比,Pinia 提供了一个更简单的 API,抛弃了Mutations的操作,具有更少的规范,提供了 Composition-API 风格的 API,与 TypeScript 一起使用时具备类型推断。
- Vuex有(state、mutations、actions、getters),
Action 提交的是 mutation,而不是直接变更状态。
const store = createStore({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
//在vue页面中
this.$store.dispatch("increment ") //actions正常访问方式,需要用dispatch去触发actions对应的方法
- pinia有(state、actions、getters)
Action 可以包含同步与任意异步的操作。
2、Pinia的使用
2.1、安装与引入
安装
npm install pinia //"pinia": "^2.0.13"
项目中引入 Pinia 并创建容器挂载到根实例上
法一:
//main.js导入
import {createPinia} from 'pinia'
createApp(App).use(createPinia()).mount('#app') //createPinia()得加上括号
法二:
//main.ts中引入store根容器
import store from '@/store'
createApp(App).use(store).mount('#app')
//对应的store/index.ts中
import { createPinia } from 'pinia'
const store = createPinia()
export default store
2.2、Store的核心配置
作用:
Pinia和之前的Vuex一样,是 Vue 的存储库,允许跨组件/页面共享状态。
Store是保存状态与业务逻辑的实体,有三个核心概念
- state:存储全局状态
- getters:类似于组件的computed,根据已有state封装派生数据,也具有缓存的特性
- actions:类似于组件的methods,用来封装业务逻辑,支持同步和异步
2.3、重点函数介绍:
- createPina >> 见 2.1:安装与引用
- defineStore >> 声明一个Store
- storeToRefs
- $patch
- Action
- Getters
1)、defineStore定义容器
参数1:是对仓库的命名,名称必须具备唯一性;
参数2:是对容器仓库的配置,以对象的形式进行配置,
state 属性: 用来存储全局的状态的,这里边定义的,就可以是为SPA里全局的状态。
getters属性: 用来监视或者说是计算状态的变化的,有缓存的功能。
actions属性: 处理state里数据变化的业务逻辑。就是修改state全局状态数据。
import {defineStore} from "pinia";
export const useMainStore = defineStore('main',{
//必须是函数,为了避免在服务端交叉请求导致的状态数据污染,而且必须是箭头函数,为了更好的TS类型推导
state:()=>{
return{}
},
//用来封装计算属性 有缓存功能 类似于computed
getters:{},
//编写业务逻辑
actions:{}
})
2)、storeToRefs()函数读取state
作用:
storeToRefs() 函数可将普通数据变成响应式数据,
且使用storeToRefs()将state中的状态解构出来,方便在视图中使用,
//相应的vue页面的script
import {storeToRefs} from 'pinia' //先用import引入
const {count,name,arr,count10} = storeToRefs(mainStore) //将mainStore容器的state值结构,并成为响应式数据
3)、$patch 函数修改多条 数据
修改state中多个数据时用
p
a
t
c
h
,
patch,
patch,patch函数会批量更新,此时需要传入state参数;
这时候的state就是store仓库里的state,所以可以直接在函数里改变任何状态数据的值。
复制代码
//$patch加函数的形式修改状态数据
mainStore.$patch(state=>{
state.count++
state.name += '~~'
state.arr.push(5)
})
Pinia的官方网站,已经明确表示 p a t c h 的方式是经过优化的,会加快修改速度,对程序的性能有很大的好处。如果你是多条数据同时更新状态数据,推荐使用 patch的方式是经过优化的,会加快修改速度, 对程序的性能有很大的好处。如果你是多条数据同时更新状态数据,推荐使用 patch的方式是经过优化的,会加快修改速度,对程序的性能有很大的好处。如果你是多条数据同时更新状态数据,推荐使用patch方式更新。
4)、Action通过函数更改state
在store/index.js中添加changeState方法,在组件中用store调用
注:定义actions时不要使用箭头函数,因为箭头函数绑定外部this。使用容器中的state 时,action通过this操作;此外,还可以通过$patch修改state的数据
actions:{
changeState(num,str){
this.count += num //action通过this操作state的数据
this.name += str
this.arr.push(5)
this.
p
a
t
c
h
(
)
t
h
i
s
.
patch({}) this.
patch()this.patch(state=>{})
}
}
复制代码
5)、Getters
在store/index.js的Getters函数中添加count10()方法,在组件中用store调用,getters函数接收state参数
注:若组件中使用ts,getters使用this时,必须指定类型,否则会导致推导错误
getters:{
count10(state){
return state.count + 10
}
countOther():number{
return this.count += 12
}
},
2.4、总体代码演示
store/index.js文件
import {defineStore} from "pinia";
//参数1:定义一个仓库的唯一id名,Pinia会将所有的容器挂载到根容器;
//参数2:选项对象
export const useMainStore = defineStore('main',{
//state必须是函数,一是避免在服务端渲染时的交叉请求导致的状态数据污染;二是为了更好的TS类型推导
state:()=>{
return{
count:10,
name:'wl',
arr:[1,2,3],
}
},
getters:{
//函数接受一个可选参数:state
count10(state){
return state.count + 10
}
//在TS文件下,getters使用了this,则必须指定类型,否则会导致推导错误,(在定义常规函数时通过 this 访问到 整个 store 的实例)
count10():number{
return this.count += 12
}
},
//不要使用箭头函数定义action,因为箭头函数绑定外部this
actions:{
changeState(num,str){
this.count += num
this.name += str
this.arr.push(5)
}
}
})
Vue组件中的使用
<template>
<h2>数量:{{mainStore.count}}---{{count}}</h2>
<h2>姓名:{{mainStore.name}}---{{name}}</h2>
<h2>arr:{{mainStore.arr}}---{{arr}}</h2>
<h2>getter的使用:{{mainStore.count10}}---{{count10}}</h2>
<button @click="changeNum">修改数量</button>
</template>
<script >
import {useMainStore} from "./store/index.js"
import {storeToRefs} from 'pinia'
import {toRefs} from "vue";
export default {
name: 'App',
setup(){
const mainStore = useMainStore()
const {count,name,arr,count10} = storeToRefs(mainStore)//使用storeToRefs函数将state里的数据解构出来实现响应式
function changeNum() {
mainStore.count++ //法1
mainStore.$patch({ //法2:修改多个数据,用$patch
count:mainStore.count + 1,
name:mainStore.name + '!',
arr:[...mainStore.arr,4]
})
mainStore.$patch(state=>{ //法3:$patch 加 函数的形式 批量更新
state.count++
state.name += '~~'
state.arr.push(5)
})
mainStore.changeState(10,'hello') //法4:逻辑比较多时使用actions
}
return{count,name,arr,count10,mainStore,changeNum}
}
}
</script>
注意,state 和 $patch() 的直接更改都出现在 devtools 中
3、持久化工具 pinia-plugin-persist
Pinia与Vuex一样,刷新页面后,数据就会重置,有时候我们需要将数据进行持久化存储,我们可以使用pinia-plugin-persist这个插件
1). 安装:
npm i pinia-plugin-persist -D
pnpm install pinia-plugin-persist -D
2). main.ts引入store
//main.ts
import store from '@/store'
createApp(App).use(store ).mount('#app')
3). 在 store/index.ts 引入pinia-plugin-persist
import { createPinia } from 'pinia'
import piniaPluginPersist from 'pinia-plugin-persist';
const store = createPinia()
store.use(piniaPluginPersist); //将pinia-plugin-persist添加到 pinia 实例中
export default store
4). 在对应的modules中 写 需要持久化的 store 模块( config.ts)
添加 persist
import { defineStore } from 'pinia'
export const useConfigStore = defineStore({
id: 'curTheme',
state: () => {
return {
curTheme: 'light',
name:'wl
}
},
persist: {
enabled: true, // 开启缓存 默认会存储在本地localstorage
strategies: [
{
key: 'key1', // 修改存在缓存中的key值
storage: localStorage, /// 修改存储方式为localStorage,默认sessionStorage
paths: ['curTheme'] // 只持久化'curTheme',此时刷新页面curTheme数据会被保留,其他state将会重置
},
{
key:'key2',
storage: localStorage,
paths: ['curTheme','name']
}
]
},
getter:{
},
actions: {
setCurTheme(data) {
this.curTheme = data
}
}
})
注意:
pinia-plugin-persist要触发actions里的方法才会生效
一个store容器存储多条key,可在strategies里,设置多个对象,以key的不同名称区分,如代码中的key1,key2
如果一条key存储多个state的数据,可在paths数组里添加需要存储的名称,以,分割,如代码中key2的paths
5). 在vue页面中
//templete中
<el-icon class="each_menu" :size="20" @click="changeCurTheme('dark')" v-if="curTheme === 'light'" >
<Sunny />
</el-icon>
<el-icon class="each_menu" :size="20" @click="changeCurTheme('light')" v-else>
<Moon />
</el-icon>
//script中
import { ref, onMounted} from 'vue'
import { useConfigStore } from '@/store/modules/config.ts'
const configStore = useConfigStore()
const curTheme = ref(configStore.curTheme)
onMounted(() => {
changeCurTheme(configStore.curTheme)
});
function changeCurTheme(themeName) {
configStore.setCurTheme(themeName) //使用action更改useConfigStore 中的curTheme值
curTheme.value = themeName
window.document.documentElement.setAttribute( "data-theme", themeName );
}
注:在浏览器的存储结果展示
4、总结
Pinia就是Vuex的替代产品,相比于Vuex,Pinia更好地兼容了Vue本身,代码更加简洁,开发起来也更加便捷。