1、pinia简介
全局状态管理工具
Pinia.js 有如下特点:
- 完整的 ts 的支持;
- 足够轻量,压缩后的体积只有1kb左右;
- 去除 mutations,只有 state,getters,actions;
- actions 支持同步和异步;
- 代码扁平化没有模块嵌套,只有 store 的概念,store 之间可以自由使用,每一个store都是独立的
- 无需手动添加 store,store 一旦创建便会自动添加;
- 支持Vue3 和 Vue2
官方文档Pinia
git 地址 https://github.com/vuejs/pinia
2、安装及注册
2.1、安装
yarn add pinia
或
npm install pinia
2.2、注册
// main.ts注册
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
const app = createApp(App)
const store = createPinia()
app.use(store)
app.mount('#app')
3、初始化Store仓库
1、src下新建store文件夹
2、新建一个[name].ts文件
3、在新建的[name].ts文件下定义仓库
4、新建一个store-namespace/index.ts文件用于名称管理
// store-namespace/index.ts
export const enum Names {
User= 'USER'
}
// User.ts
import { defineStore } from 'pinia'
import { Names } from './store-namespace'
// defineStore需要一个唯一的名称,相当于id,作为第一个参数传递
// defineStore将返回的函数一般命名为use...
export const useUserInfo = defineStore(Names.User, {
// state在vuex中是一个对象
// 在pinia中为一个箭头函数返回一个对象,在对象里面定义数据
state: () => ({
name: '张三1111',
age: 20
}),
// 类似于computed
getters: {},
// pinia中actions既可以操作异步也能同步操作state值
actions: {
setData() {
this.name = '杨七'
this.age++
}
}
})
4、pinia中state值修改的五种方式
<template>
<div>
<h1>
pinia-----{{User.name}}------{{User.age}}
</h1>
<button @click="change">change</button>
</div>
</template>
<script setup lang="ts">
import { useUserInfo } from '@/store/User.ts'
const User = useUserInfo()
// 方式一:可以直接修改值,不同于vuex
// const change = () => {
// User.name = '哈哈'
// User.age++
// }
// 方式二:$patch方法对象形式修改
// const change = () => {
// User.$patch({
// name: '李四',
// age: 100
// })
// }
// 方式三:$patch方法函数形式修改,内部可自定义逻辑修改
// const change = () => {
// User.$patch((state) => {
// state.name = '王五'
// state.age++
// })
// }
// 方式四:$state方法设置新对象替换整个store,必须修改整个state对象的所有属性
// (但本人发现修改单个属性也可以,并无其他影响)
// const change = () => {
// User.$state = {
// name: '赵六',
// age: 999,
// }
// }
// 方式五:通过actions修改state值
const change = () => {
User.setData()
}
</script>
5、解构pinia中state值
<template>
<div>
<h4>
原始值-----{{User.name}}------{{User.age}}
</h4>
<h4>
解构值-----{{name}} ----- {{age}}
</h4>
<button @click="change">change</button>
</div>
</template>
<script setup lang="ts">
import { useUserInfo } from '../store'
import { storeToRefs } from 'pinia'
const User = useUserInfo()
// 1、pinia值直接解构,失去响应式
// const { name, age } = User
// 2、使用storeToRefs方法解构数据为响应式
const { name, age } = storeToRefs(User)
const change = () => {
User.age++ // 或 age.value++
}
</script>
// storeToRefs源码
// storeToRefs同toRefs一样,给内部数据包裹一层toRef
function storeToRefs(store) {
// See https://github.com/vuejs/pinia/issues/852
// It's easier to just use toRefs() even if it includes more stuff
if (isVue2) {
// @ts-expect-error: toRefs include methods and others
return toRefs(store);
}
else {
// 首先将store响应式对象转化为普通对象,防止下面的toRef重复代理
store = toRaw(store);
// 最终return 这个 refs
const refs = {};
for (const key in store) {
const value = store[key];
// 判断属性值是否是ref或reactive响应式对象,如果是拷贝到refs中,将其原始对象包裹toRef变为响应式对象
if (isRef(value) || isReactive(value)) {
// @ts-expect-error: the key is state or getter
refs[key] =
// ---
toRef(store, key);
}
}
return refs;
}
}
store转化为普通对象后的属性展示
6、actions、getters使用
actions既支持异步也支持同步方法
import { defineStore } from 'pinia'
import { Names } from './store-namespace'
type Result = {
name: string,
age: number
}
const Login = ():Promise<Result> => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
name: '王翠花',
age: 19
})
}, 3000)
})
}
export const useUserInfo = defineStore(Names.User, {
state: () => ({
num: 100,
user: <Result>{}
}),
actions: {
// 异步方法
async getLogin() {
let res = await Login()
this.user = res
console.log(res)
// actions中相互调用
this.setNum(res.age)
},
// 同步方法
setNum(val: number) {
this.num = val
},
}
})
getters使用
import { defineStore } from 'pinia'
import { Names } from './store-namespace'
export const useUserInfo = defineStore(Names.User, {
state: () => ({
num: 100,
}),
getters: {
// 写法1 箭头函数不能使用this,this指向已经变成undefined,修改请用state
doubleNum: (state) => state.num * 2,
// 写法2 普通函数形式中可使用this,但ts无法进行正确的类型推导,需定义返回值类型
tripleNum(): number {
return this.num * 3
},
// getters中相互调用
numStr1(): string {
return this.doubleNum + 'str'
},
},
})
7、pinia API
$reset、$subscribe、$onAction
<template>
<div>
<h4>
actions-----{{User.info}} ---- {{User.num}}
</h4>
<button @click="change">change</button>
<button @click="reset">reset</button>
<button @click="actionChange">actionChange</button>
</div>
</template>
<script setup lang="ts">
import { useUserInfo } from '../store'
const User = useUserInfo()
const change = () => {
User.info.name = '张三'
User.info.age = 999
User.num++
}
const reset = () => {
// $reset重置store到初始状态,将state所有值重置回初始状态
User.$reset()
}
const actionChange = () => {
User.setNum(789)
}
// 监听state值的改变,只要state值改变都会走这个函数
User.$subscribe((args, state) => {
console.log(args, state, 'state数据改变----')
}, {
// 组件销毁后仍可监听
detached: true
})
// 当actions中函数被调用,触发监听
User.$onAction((args) => {
console.log(args, 'actions调用--------------')
})
</script>
8、pinia 数据持久化插件
8.1、下载插件
npm i pinia-plugin-persist --save
或
yarn add pinia-plugin-persist
8.2、在main.ts中引入
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
// 持久化插件
import piniaPersist from 'pinia-plugin-persist'
const store = createPinia()
store.use(piniaPersist)
const app = createApp(App)
app.use(store)
app.mount('#app')
8.3、模块中使用
import { defineStore } from 'pinia'
import { Names } from './store-namespace'
type Result = {
name: string,
age: number
}
export const useUserInfo = defineStore(Names.User, {
state: () => ({
info: <Result>{},
num: 100,
}),
persist: {
enabled: true, // 开启存储
// strategies 设置存储位置及存储变量,不写默认为sessionStorage中存储所有数据
strategies: [
// storage 设置存储到localStorage或sessionStorage中
// paths 不写则默认存储所有数据,写了只存储指定变量数据(如下只对num做了数据持久化)
{storage: localStorage, paths: ['num']}
]
},
getters: {},
actions: {},
})
原文链接:
https://xiaoman.blog.csdn.net/article/details/123338137