更新记录
更新记录
2024年2月28日 初始化。
Pinia 是什么
状态管理包,允许跨组件/页面共享状态。
Pinia 和 Vuex 对比
最大最大的区别,Pinia没有mutations概念,这个东西真的很恶心。
从ExtJS转过来,看Pinia不要太爽,和ExtJS的Store的概念简直一模一样。
安装包
npm install pinia
注册 Pinia 插件
进入 src/main.js
import './assets/main.css'
import { createApp } from 'vue'
//引入Pinia
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
const app = createApp(App)
//使用插件
app.use(createPinia())
app.use(router)
app.mount('#app')
创建初始Stores文件夹
用来存放具体的Store。如果是用的官方脚手架生成的基架,这一切都是自动的。
创建 具体 Store(选项式 API )
defineStore 函数
通过 defineStore 定义一个名为 xxx 的Store。
官方推荐使用 use 开头命名 Store 进行导出。
三个概念,state、getter 和 action,类似组件中的 data、 computed 和 methods。
创建 scr/stores/user.js文件
import { defineStore } from 'pinia'
export const useUserStore = defineStore("user", {
});
state
import { defineStore } from 'pinia'
export const useUserStore = defineStore("user", {
state: () => {
return {
name: "ll",
age: 18,
count: 100,
};
},
});
action
Actions 支持同步和异步方法。
同步:
state: () => ({
count: 0,
}),
actions: {
//同步
increment() {
this.count++;
},
//异步
async fetchHomeBanner() {
const res = await fetch("http://panda666/home/waDataAsync");
const data = await res.json();
this.banners = data.data.banner.list;
},
},
getters
export const useUserStore = defineStore("user", {
state: () => ({
name: "ll",
age: 18,
count: 100,
}),
getters: {
doubleCount(state) {
return state.count * 2;
},
getUserById(state) {
return (id: number) => state.userList.find((item) => item.id === id);
},
},
});
创建 具体 Store(组合式 API )
和 选项式API 类似
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
//state
const count = ref(0);
//getter
const doubleCount = computed(() => count.value * 2);
//actions
function increment() {
count.value++
}
return { count, doubleCount, increment }
})
使用 Store
在 App.vue 中直接引入 useCounterStore 进行调用,然后创建 具体Store的实例对象。
<script setup>
import { useCounterStore } from "@/stores/counter";
// 注意:这里导入的函数,不能被解构,那么会失去响应式。
// 例如不能这样: const { count, increment, decrement, doubleCount } = useCounterStore();
const counterStore = useCounterStore();
</script>
<template>
<div class="app-wrapper">
<div>count:{{ counterStore.count }}</div>
<div>doubleCount:{{ counterStore.doubleCount }}</div>
<div>
<button @click="counterStore.increment()">增加</button>
<button @click="counterStore.decrement()">减少</button>
</div>
</div>
</template>
<style scoped></style>
操作 State
读取 state
<template>
<h1>我是child组件</h1>
<p>姓名:{{ name }}</p>
<p>年龄:{{ age }}</p>
<p>性别:{{ sex }}</p>
</template>
<script setup>
import { useUsersStore } from "../src/stores/user";
const store = useUsersStore();
const { name, age, sex } = store; //纯读取
</script>
修改 state
import { storeToRefs } from 'pinia'; //重要
const store = useUsersStore();
//用于修改需要转为ref
const { name, age, sex } = storeToRefs(store);
//修改
age++;
store.age++;
批量修改 state
const counterStore = useCounterStore();
const add = () => {
// 调用 $patch 同时应用多个修改
counterStore.$patch({
count: 10,
// ...可同时修改多个属性
});
};
或者
userStore.$patch( state => {
state.count++
state.arr.push(1)
})
重置 state
const counterStore = useCounterStore();
const resetStore = () => {
// 调用 $reset 函数进行数据重置为初始值
counterStore.$reset();
};
注意:在 Setup Stores 中,您需要创建自己的 $reset() 方法
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
function $reset() {
count.value = 0
}
return { count, $reset }
})
替换 state
const counterStore = useCounterStore();
const replaceState = () => {
// 通过 $state 直接赋值替换整个对象
counterStore.$state = {
count: 100,
};
};
订阅state
当state发生变化,就会触发。
通过 store 的 $subscribe() 方法侦听 state 及其变化。
cartStore.$subscribe((mutation, state) => {
// import { MutationType } from 'pinia'
mutation.type // 'direct' | 'patch object' | 'patch function'
// 和 cartStore.$id 一样
mutation.storeId // 'cart'
// 只有 mutation.type === 'patch object'的情况下才可用
mutation.payload // 传递给 cartStore.$patch() 的补丁对象。
// 每当状态发生变化时,将整个 state 持久化到本地存储。
localStorage.setItem('cart', JSON.stringify(state))
});
默认情况下,state subscription 会被绑定到添加它们的组件上 (如果 store 在组件的 setup() 里面)。这意味着,当该组件被卸载时,它们将被自动删除。如果你想在组件卸载后依旧保留它们,请将 { detached: true } 作为第二个参数,以将 state subscription 从当前组件中分离:
<script setup>
const someStore = useSomeStore()
// 此订阅器即便在组件卸载之后仍会被保留
someStore.$subscribe(callback, { detached: true })
</script>
使用 watch() 函数侦听整个 state
watch(
pinia.state,
(state) => {
// 每当状态发生变化时,将整个 state 持久化到本地存储。
localStorage.setItem('piniaState', JSON.stringify(state))
},
{ deep: true }
)
订阅action
通过 store.$onAction() 来监听 action 和结果。
const unsubscribe = someStore.$onAction(
({
name, // action 名称
store, // store 实例,类似 `someStore`
args, // 传递给 action 的参数数组
after, // 在 action 返回或解决后的钩子
one rror, // action 抛出或拒绝的钩子
}) => {
// 为这个特定的 action 调用提供一个共享变量
const startTime = Date.now()
// 这将在执行 "store "的 action 之前触发。
console.log(`Start "${name}" with params [${args.join(', ')}].`)
// 这将在 action 成功并完全运行后触发。
// 它等待着任何返回的 promise
after((result) => {
console.log(
`Finished "${name}" after ${
Date.now() - startTime
}ms.\nResult: ${result}.`
)
})
// 如果 action 抛出或返回一个拒绝的 promise,这将触发
one rror((error) => {
console.warn(
`Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`
)
})
}
)
// 手动删除监听器
unsubscribe()
默认情况下,action 订阅器会被绑定到添加它们的组件上(如果 store 在组件的 setup() 内)。这意味着,当该组件被卸载时,它们将被自动删除。如果你想在组件卸载后依旧保留它们,请将 true 作为第二个参数传递给 action 订阅器,以便将其从当前组件中分离:
<script setup>
const someStore = useSomeStore()
// 此订阅器即便在组件卸载之后仍会被保留
someStore.$onAction(callback, true)
</script>
状态持久化-pinia-plugin-persistedstate
避免刷新浏览器的时候出现数据丢失,将需要持久化存储的数据添加到localStorage或sessionStore中。
使用:pinia-plugin-persistedstate 插件。
安装
npm i pinia-plugin-persistedstate
全局配置
在 src/stores/index.js 中引入插件
import { createPinia } from "pinia"
// 引入持久化插件
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const store = createPinia()
// 将插件提供给store实例
store.use(piniaPluginPersistedstate)
export default store
使用
在需要持久化的store中引入即可。因为我们定义store有两种方式,因此持久化也有对应的书写方式
Store(state的方式):
import { defineStore } from 'pinia'
export const useUserStore = defineStore("user", {
state: () => ({
name: "ll",
age: 18,
count: 100,
}),
//....
// 表示这个store里的数据都将持久化存储
persist: true
});
Store(函数式方式)
import { defineStore } from 'pinia'
import { reactive, toRefs } from "vue"
export const useUserStore = defineStore("user", ()=>{
return {
...toRefs(state),
fullName,
updateLastName
}
}, {
// 注意defineStore的第三个参数可以传入插件配置
persist: true
});
只持久化部分数据
设置配置文件
在 src/utils 目录下新建 persist.js 文件,修改存储配置:
import { PersistedStateOptions } from 'pinia-plugin-persistedstate'
/**
* @description pinia持久化参数配置
* @param {String} key 存储到持久化的 name
* @param {Array} paths 需要持久化的 state name
* @return persist
* */
const piniaPersistConfig = (key, paths) => {
const persist = {
key,
storage: window.localStorage,
// storage: window.sessionStorage,
paths
}
return persist
}
export default piniaPersistConfig
注册插件
import { createPinia } from 'pinia'
import piniaPluginPersist from 'pinia-plugin-persist'
const pinia = createPinia()
pinia.use(piniaPluginPersist)
在 Store 中使用
修改store中的持久化配置
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 1 }),
// 开启数据缓存
persist: {
enabled: true
}
})
//或者
const useUserStore = defineStore('user', () => {
return {
...toRefs(state),
fullName,
updateLastName
}
}, {
// 注意defineStore的第三个参数可以传入插件配置,根据配置只持久化lastName
persist: piniaPersistConfig('user', ['lastName'])
})
参考链接
https://blog.logrocket.com/complex-vue-3-state-management-pinia/
https://www.zhihu.com/question/521159639
https://pinia.vuejs.org/
https://zhuanlan.zhihu.com/p/605989666
https://juejin.cn/post/7204735957777203260
https://zhuanlan.zhihu.com/p/533233367
https://pinia.web3doc.top/