pinia是一个vue的状态存储库,你可以使用它来存储、共享一些跨组件或者页面的数据,使用起来和vuex非常类似。pina相对Vuex来说,更好的ts支持和代码自动补全功能。本篇随笔介绍pinia的基础用法以及持久化存储的一些用法,供参考学习。
Pinia 是 Vuex4 的升级版,也就是 Vuex5; Pinia 极大的简化了Vuex的使用,是 Vue3的新的状态管理工具;Pinia 对 ts的支持更好,性能更优, 体积更小,无 mutations,可用于 Vue2 和 Vue3;Pinia支持Vue Devtools、 模块热更新和服务端渲染。
安装pinia(https://pinia.vuejs.org/)
npm install pinia
在main.j或者main.ts中引入使用
import { createPinia } from 'pinia' app.use(createPinia())
下面就是使用pinia的一个例子。这样你就创建了一个状态存储。
// stores/counter.js import { defineStore } from 'pinia' export const useCounterStore= defineStore('counter', { state: ()=> {return { count: 0 } },// 也可以这样定义状态// state: () => ({ count: 0 }) actions: { increment() {this.count++ }, }, })
在组件中使用:
import { useCounterStore } from '@/stores/counter' exportdefault { setup() { const counter= useCounterStore() counter.count++// 编辑器会有代码提示 counter.$patch({ count: counter.count + 1 })// 也可以使用action来代替 counter.increment() }, }
const useCounterStore = defineStore('counter', { state: ()=> ({ count: 0 }), getters: {double: (state) => state.count * 2, }, actions: { increment() {this.count++ } } }) const useUserStore= defineStore('user', {// ...}) exportdefault { computed: {// 其他计算属性// ...// 可以使用 this.counterStore 和 this.userStore获取 ...mapStores(useCounterStore, useUserStore)// 可以使用 this.count 和this.double获取 ...mapState(useCounterStore, ['count', 'double']), }, methods: {// 可以使用 this.increment()调用 ...mapActions(useCounterStore, ['increment']), }, }
与vue4之前的版本相比,pinia的API是有很多不同的,即:
import { defineStore } from 'pinia' export const useStore= defineStore('main', {// other options... })
上面只是定义了store,在setup函数中调用了useStore()时,才会创建store:
import { useStore } from '@/stores/counter' exportdefault { setup() { const store= useStore()return {// 你可以返回store这个对象,然后就可以在template中使用了 store, } }, }
在store实例化以后,你就可以调用到store中定义的state、getters和actions了。为了让解构的值还保持响应式,你需要用到storeToRefs()方法。它会给响应式的数据创建ref。
import { storeToRefs } from 'pinia' exportdefault defineComponent({ setup() { const store= useStore()// `name` 和 `doubleCount` 是响应式的// 插件增加的属性也会创建ref// 但是会自动跳过action或者不是响应性的属性 const { name, doubleCount } = storeToRefs(store)return { name, doubleCount } }, })
默认情况下,你可以在store实例上直接获取或者修改state:
const store = useStore() store.counter++
也可以调用$reset()方法来把state恢复为初始值:
const store = useStore() store.$reset()
除了直接修改store里的值store.counter++,你也可以是用$patch方法。你可以同时修改多个值:
store.$patch({ counter: store.counter+ 1, name:'Abalam', })
或者$patch接收一个函数作为参数,来简化改变数组的写法:
store.$patch((state) => { state.items.push({ name:'shoes', quantity: 1 }) state.hasChanged=true })
localStorage和sessionStorage差别
localStorage和sessionStorage一样都是用来存储客户端临时信息的对象。
他们均只能存储字符串类型的对象(虽然规范中可以存储其他原生类型的对象,但是目前为止没有浏览器对其进行实现)。
localStorage生命周期是永久,这意味着除非用户显示在浏览器提供的UI上清除localStorage信息,否则这些信息将永远存在。
sessionStorage生命周期为当前窗口或标签页,一旦窗口或标签页被永久关闭了,那么所有通过sessionStorage存储的数据也就被清空了。
不同浏览器无法共享localStorage或sessionStorage中的信息。相同浏览器的不同页面间可以共享相同的 localStorage(页面属于相同域名和端口),但是不同页面或标签页间无法共享sessionStorage的信息。这里需要注意的是,页面及标 签页仅指顶级窗口,如果一个标签页包含多个iframe标签且他们属于同源页面,那么他们之间是可以共享sessionStorage的。
JSON对象提供的parse和stringify将其他数据类型转化成字符串,再存储到storage中就可以了,操作的方式:
存:
var obj = {"name":"xiaoming","age":"16"}
localStorage.setItem("userInfo",JSON.stringify(obj));
取:
var user = JSON.parse(localStorage.getItem("userInfo"))
删除:
localStorage.remove("userInfo);
清空:
localStorage.clear();
pnia 使用订阅机制subscribe来实现数据的持久化存储的代码如下所示。
const instance = useMainStore();// 订阅数据变化,变化时存储 instance.$id 这是storeId instance.$subscribe((mutation, state) => { localStorage.setItem(instance.$id, JSON.stringify(state)); });//init 初始的时候获取 const val = localStorage.getItem(instance.$id);if (val) { instance.$state= JSON.parse(val); }
也可以通过watch实现
watch( pinia.state, (state)=> {// persist the whole state to the local storage whenever it changes localStorage.setItem('piniaState', JSON.stringify(state)) }, { deep:true } )
但是需要注意,这种方式持久化会提示pinia未安装挂载,所以需要在pinia挂载后再调用,这里可以将它封装成方法导出,在挂载后调用
xport const initStore = () => { const instance= useMainStore();// 订阅数据变化,变化时存储 instance.$id 这是storeId instance.$subscribe((mutation, state) => { localStorage.setItem(instance.$id, JSON.stringify(state)); });//init 初始的时候获取 const val = localStorage.getItem(instance.$id);if (val) { instance.$state= JSON.parse(val); } }
exportdefault { setup() { const someStore= useSomeStore()// 组件卸载后,侦听也会有 someStore.$subscribe(callback,true)// ... }, }
或者watch状态的变化
watch( pinia.state, (state)=> {// 在state改变时,保存在localStorage中 localStorage.setItem('piniaState', JSON.stringify(state)) }, { deep:true } )
pinia plugin persist官方网站:pinia-plugin-persist
持久化存储也可以通过安装插件的方式,安装 pinia-plugin-persist 来实现。
npm ipinia-plugin-persist --save
使用main.js
import { createPinia } from 'pinia' import piniaPluginPersist from'pinia-plugin-persist' const store= createPinia() store.use(piniaPluginPersist) createApp(App).use(store).mount('#app')
在对应的store中开启,数据默认存在 sessionStorage 里,并且会以 storeId 作为 key
import { defineStore } from 'pinia'// 'main' 是storeId export const useMainStore = defineStore('main', { state: ()=> ({ counter:2, name:'Eduardo', isAdmin:true }),// ……// 开启数据缓存 persist: { enabled:true } })
如果需要自定义key和存储位置,则修改参数即可。
persist: { enabled:true, strategies: [//使用插件自定义存储 { key:'settings',// key可以自己定义,不填的话默认就是这个store的ID storage: localStorage, } ] },
一般项目开发,实际上存储的内容会比较多,可能根据不同的键值模块进行区分,因此把它们放在一个store/modules里面,方便的使用引用它来存取设置数据即可。
我们这里简单以一个settings的配置信息进行介绍,其中index.ts是一个统一的创建pinia的对象并挂接到全局App上的。
其中index.ts的代码如下所示。
import type { App } from "vue"; import { createPinia } from"pinia"; import piniaPluginPersist from'pinia-plugin-persist';//使用插件持久化 const store= createPinia(); store.use(piniaPluginPersist)//使用插件持久化 exportfunction setupStore(app: App) { app.use(store); } export { store };
因此在main.js里面引入并挂接pinia即可。
import { createApp } from 'vue' import ElementPlus from'element-plus' import'element-plus/dist/index.css' import'normalize.css'// css初始化 import App from'./App.vue' import { setupStore } from "/@/store"; const app= createApp(App)setupStore(app) app.use(ElementPlus) app.mount('#app')
这样我们就可以再次定义一个模块化的配置信息,以便于管理存储各种不同类型的内容。
如下面我们定义一个程序配置信息setttings.ts
import { defineStore } from "pinia"; import { store } from"/@/store"; export typesettingType= { title: string; fixedHeader:boolean; hiddenSideBar:boolean; }; export const useSettingStore= defineStore({ id:"settings", state: ():settingType=> ({ title:"Vue3 + TypeScript + Element", fixedHeader:false, hiddenSideBar:false }),persist: { enabled:true, strategies: [//使用插件自定义存储 { key:'settings',// key可以自己定义,不填的话默认就是这个store的ID storage: localStorage, } ] }, getters: { getTitle() {returnthis.title; }, getFixedHeader() {returnthis.fixedHeader; }, getHiddenSideBar() {returnthis.HiddenSideBar; } }, actions: { CHANGE_SETTING({ key, value }) {// eslint-disable-next-line no-prototype-builtinsif (this.hasOwnProperty(key)) {this[key] = value; } }, changeSetting(data) {this.CHANGE_SETTING(data); } } }); exportfunction useSettingStoreHook() {return useSettingStore(store); }
然后在组件视图vue或者app.vue中使用即可
<scriptlang="ts"> import { defineComponent } from"vue"; import {useSettingStoreHook } from"/@/store/modules/settings"; import {storeToRefs } from"pinia"; exportdefault defineComponent({ name:"app", components: { }, setup() { const store= useSettingStoreHook(); const { fixedHeader, title }= storeToRefs(store);return {fixedHeader,title, }; }, methods: { setTitle() {this.title="Vue3 + TypeScript + Element + Edit"; console.log(this.title); }, }, });script>
查看数据修改后,存储在本地存储空间中的内容,如下所示。