如果你不是很喜欢setup函数和组合API,pinia也有类似vuex的map的功能。你可以用上面的方式定义你的store,但是使用时用mapStores(), mapState(),或者 mapActions():
constuseCounterStore=defineStore(counter,{state:()=>({count:0}),getters:{double:(state)=>state.count*2,},actions:{increment(){this.count++}}})constuseUserStore=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是有很多不同的,即:
去掉了mutation。因为好多人认为mutation是多余的。以前它方便devtools集成,现在这不是个问题了。不用在写复杂的ts类型包装,所有的都是有类型的,API设计的都是尽量符合ts的类型推断不再使用一个莫名其妙的字符串了,只需要导入一个函数,调用他们就行了,同时还有代码自动补全不需要动态添加store了,因为它们现在本来就是动态。如果你想,你随时可以手动去写一个store。没有复杂的嵌套模块了。你仍然可以在一个store中导入其他的store来实现嵌套模块,但是pinia还是推荐使用一个扁平的结构。但是即使你使用循环依赖也没关系。不再需要命名空间了。因为现在store本来就是扁平结构了。你也可以理解为所有的store本来就有命名空间了。
你的应用中的全局数据需要保存在store中。在很多地方你都要使用这些数据,比如说,用户信息需要在导航栏中显示,也需要在个人中心显示。还有些数据,需要暂存起来,比如一个需要分好几页填写的表单。
在pinia中,store是通过defineStore()方法定义的,它的第一个参数就是一个唯一的名字:
import{defineStore}frompiniaexportconstuseStore=defineStore(main,{//other options...})
上面只是定义了store,在setup函数中调用了useStore()时,才会创建store:
import{useStore}from@/stores/counterexportdefault{setup(){conststore=useStore()return{//你可以返回store这个对象,然后就可以在template中使用了store,}},}
在store实例化以后,你就可以调用到store中定义的state、getters和actions了。为了让解构的值还保持响应式,你需要用到storeToRefs()方法。它会给响应式的数据创建ref。
import{storeToRefs}frompiniaexportdefaultdefineComponent({setup(){conststore=useStore()//`name` 和 `doubleCount` 是响应式的//插件增加的属性也会创建ref//但是会自动跳过action或者不是响应性的属性const{name,doubleCount}=storeToRefs(store)return{name,doubleCount}},})
默认情况下,你可以在store实例上直接获取或者修改state:
conststore=useStore()store.counter++
也可以调用$reset()方法来把state恢复为初始值:
conststore=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})
2、pinia的持久化存储处理
你可以用$subscribe()来侦听state的改变,持久化一般存储在localStorage和sessionStorage。
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来实现数据的持久化存储的代码如下所示。
constinstance=useMainStore();//订阅数据变化,变化时存储 instance.$id 这是storeIdinstance.$subscribe((mutation,state)=>{localStorage.setItem(instance.$id,JSON.stringify(state));});//init 初始的时候获取constval=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 changeslocalStorage.setItem(piniaState,JSON.stringify(state))},{deep:true})
但是需要注意,这种方式持久化会提示pinia未安装挂载,所以需要在pinia挂载后再调用,这里可以将它封装成方法导出,在挂载后调用
xportconstinitStore=()=>{constinstance=useMainStore();//订阅数据变化,变化时存储 instance.$id 这是storeIdinstance.$subscribe((mutation,state)=>{localStorage.setItem(instance.$id,JSON.stringify(state));});//init 初始的时候获取constval=localStorage.getItem(instance.$id);if(val){instance.$state=JSON.parse(val);}}
默认情况下,state侦听会和组件绑定在一起(如果store是在组件的setup中)。这意味着,当组件卸载时,侦听会自动被移除。如果你需要在组件被卸载时,侦听仍然保持,需要给$subscribe()方法传递第二个参数true:
exportdefault{setup(){constsomeStore=useSomeStore()//组件卸载后,侦听也会有someStore.$subscribe(callback,true)//...},}
或者watch状态的变化
watch(pinia.state,(state)=>{//在state改变时,保存在localStorage中localStorage.setItem(piniaState,JSON.stringify(state))},{deep:true})
3、使用pinia插件持久化存储
pinia plugin persist官方网站:pinia-plugin-persist
持久化存储也可以通过安装插件的方式,安装 pinia-plugin-persist 来实现。
npm ipinia-plugin-persist--save
使用main.js
import{createPinia}frompiniaimportpiniaPluginPersistfrompinia-plugin-persistconststore=createPinia()store.use(piniaPluginPersist)createApp(App).use(store).mount(#app)
在对应的store中开启,数据默认存在 sessionStorage 里,并且会以 storeId 作为 key
import{defineStore}frompinia//main 是storeIdexportconstuseMainStore=defineStore(main,{state:()=>({counter:2,name:Eduardo,isAdmin:true}),//……//开启数据缓存persist:{enabled:true}})
如果需要自定义key和存储位置,则修改参数即可。
persist:{enabled:true,strategies:[//使用插件自定义存储{key:settings,//key可以自己定义,不填的话默认就是这个store的IDstorage:localStorage,}]},
4、在实际项目中使用pinia
一般项目开发,实际上存储的内容会比较多,可能根据不同的键值模块进行区分,因此把它们放在一个store/modules里面,方便的使用引用它来存取设置数据即可。
我们这里简单以一个settings的配置信息进行介绍,其中index.ts是一个统一的创建pinia的对象并挂接到全局App上的。
其中index.ts的代码如下所示。
importtype{App}from"vue";import{createPinia}from"pinia";importpiniaPluginPersistfrompinia-plugin-persist;//使用插件持久化conststore=createPinia();store.use(piniaPluginPersist)//使用插件持久化exportfunctionsetupStore(app:App<Element>){app.use(store);}export{store};
因此在main.js里面引入并挂接pinia即可。
import{createApp}fromvueimportElementPlusfromelement-plusimportelement-plus/dist/index.cssimportnormalize.css//css初始化importAppfrom./App.vueimport{setupStore}from"/@/store";constapp=createApp(App)setupStore(app)app.use(ElementPlus)app.mount(#app)
这样我们就可以再次定义一个模块化的配置信息,以便于管理存储各种不同类型的内容。
如下面我们定义一个程序配置信息setttings.ts
import{defineStore}from"pinia";import{store}from"/@/store";exporttypesettingType={title:string;fixedHeader:boolean;hiddenSideBar:boolean;};exportconstuseSettingStore=defineStore({id:"settings",state:():settingType=>({title:"Vue3 + TypeScript + Element",fixedHeader:false,hiddenSideBar:false}),persist:{enabled:true,strategies:[//使用插件自定义存储{key:settings,//key可以自己定义,不填的话默认就是这个store的IDstorage: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);}}});exportfunctionuseSettingStoreHook(){returnuseSettingStore(store);}
然后在组件视图vue或者app.vue中使用即可
<scriptlang="ts">import{defineComponent}from"vue";import{useSettingStoreHook}from"/@/store/modules/settings";import{storeToRefs}from"pinia";exportdefaultdefineComponent({name:"app",components:{},setup(){conststore=useSettingStoreHook();const{fixedHeader,title}=storeToRefs(store);return{fixedHeader,title,};},methods:{setTitle(){this.title="Vue3 + TypeScript + Element + Edit";console.log(this.title);},},});