目录
9.持久化插件pinia-plugin-persistedstate
1.为什么使用pinia而不再去使用vuex?
与 Vuex 相比,Pinia 提供了一个更简单的 API,具有更少的规范,提供了 Composition-API 风格的 API,最重要的是,在与 TypeScript 一起使用时具有可靠的类型推断支持。
2.安装pinia
(pnpm || cnpm || npm) install pinia || yarn add pinia都可以。
vue3引入
import { createPinia } from 'pinia' app.use(createPinia())
vue2引入
import { createPinia, PiniaVuePlugin } from 'pinia' Vue.use(PiniaVuePlugin) const pinia = createPinia() new Vue({ el: '#app', // 其他选项... // ... // 注意同一个 `pinia` 实例可以在多个 Vue 应用程序中使用 // 同一个页面 pinia, })
3.Store的使用
首先在src目录下创建一个store文件夹,在这里面放入我们的Store的.js文件。
import { defineStore } from "pinia";
export const useStore = defineStore("main", {});
defineStore定义一个默认的Store,第一个参数是Store的id,第二个参数用来存储Store里面的内容。在组件中使用这个store,首先引入这个js文件,引入它向外暴露的useStore方法,当然这里也可以将存储store的js文件改成默认暴露。
import useStore from "../stores/main";
import { storeToRefs } from "pinia";
export default {
name: "HelloWorld",
setup() {
const store = useStore();
const { count } = storeToRefs(store);
setInterval(() => {
console.log(count);
}, 5000);
return {
store,
count,
};
},
};
我们使用了storeToRefs这个方法来解构数据,将需要转换的store传递进去进行解构。不使用这个方法而去结构赋值,数据的响应式则会失效。
4.state的使用
1.基础使用
const store = useStore();//这个store上面现在有count这个属性
store.count++//我们可以直接访问与修改
store.$reset()会去直接给store中的所有state数据赋值为初始值(重置)
2.mapState方法
computed,methods访问Store,需要调用mapState方法
第一个参数是我们需要使用的store,第二个参数是一个对象,将方法放到其中,每个方法都会去接受传递的store作为参数(只能读取,不能写入), 调用mapState它会将第二个参数对象上面写的方法返回给我们,我们需要通过...解构将返回的方法,放到computed上面。
第二种方法第一个参数不变,第二个参数可以是对象,只不过对象的值不再是函数,而是对应state属性的名字,我们可以重写这个属性的名字,键是重写后的名字,键要重写的属性。
第三种第一个参数和上一个一样,第二个参数变为数组,数组里面是属性名。
import useStore from "../stores/main";
import { mapState } from "pinia";
export default {
name: "HelloWorld",
computed: {
...mapState(useStore, {
// myOwnName: "count",
double: (store) => {
return store.count * 2;
},
}),//第一种写法
...mapState(useStore, {
// myOwnName: "count",
double:'num'
}),//第二种写法
...mapState(useStore,['num']),//第三种写法
},
};
3.mapWritableState方法
刚才提到mapState只能读取,无法修改,mapWritableState就解决了这样的问题。
import useStore from "../stores/main";
import { mapWritableState } from "pinia";
export default {
name: "HelloWorld",
computed: {
...mapWritableState(useStore, ["count", "num"]),
},
};
我们可以直接在一个input上面去双向绑定数据。
4.改变状态patch
除了直接给store的属性进行赋值以外,我们还可以去调用store上面的$patch方法。
两种写法:
(1)传递一个对象给patch告诉它你要给谁,改谁就去在这个对面上面写上它的属性和值,当你传递的属性值在原来的state上面没有去访问到它会自动给state上面添加上这个属性。
import useStore from "../stores/main";
export default {
name: "HelloWorld",
setup() {
let store = useStore();
store.$patch({
count: store.count + 1,
name: "123",
});
console.log(store);
},
mounted: function () {},
computed: {},
};
(2)传递一个函数,参数有当前store的state,可以直接去上面进行添加和修改。
import useStore from "../stores/main";
export default {
name: "HelloWorld",
setup() {
let store = useStore();
store.$patch((state) => {
state.count = state.count++;
state.name = "123";
});
console.log(store);
},
mounted: function () {},
computed: {},
};
5.替换state
你甚至可以直接通过store.$state = {} 这样的方式来去修改当前store的state。
pinia.state.value = {}直接将所有的state都赋值为空
6.订阅状态$subscribe
store上面的$subscribe相当于是vue当中的watch,它可以去监听state数据的改变。
let store = useStore();
store.$subscribe((mutation, state) => {
console.log(mutation, state);
});
store.$patch((state) => {
state.count = state.count + 1;
state.name = "123";
});
打印的mutation如下:
events存储发生了什么事件,key代表其改变的属性名.newValue代表新的属性值,oldValue代表旧值,type什么类型,如果这个值是修改为set,给state添加一个新值是add。target代表改变以后得state是什么样子的。
storeId代表当前这个store的id。
type告诉你是通过什么方式改变的,direct直接修改state数据的类型, patch function使用patch修改数据并且传递的是个函数,patch Object传递的是个对象。
(需要注意的是组件销毁的时候监听state的监听也会失效,需要去传递第二个参数{ detached: true })
7.总结pinia的store对比其vuex修改state的数据不再那么麻烦,更加的轻巧便捷。
5.getters
1.基础使用
getter相当于store里面的计算属性。
import { defineStore } from "pinia";
export default defineStore("main", {
state: () => {
return {
count: 123,
num: 1,
};
},
getters: {
Bignum: (state) => state.num * 10000,
Smallcount() {
console.log(this);
return this.count - 122;
},
},
});
两种写法:
- 接收一个当前的state作为参数,将其中的数据返回出去。
- 不接收当前的state,直接使用this当前的this指向store。
2.访问其它的getters
Smallcount() {
return this.Bignum - 10000;
}//this写法
Smallcount(state) {
return state.Bignum - 10000;
},//接收参数写法
可以通过this去调用其它getters,也可以通过参数来调用。
3.访问其它的store
import { defineStore } from "pinia";
export default defineStore("price", {
state: () => {
return {
carPrice: "100000",
carNum: 30,
};
},
getters: {
carTotal(state) {
return state.carPrice * state.carNum + "¥";
},
},
});
import { defineStore } from "pinia";
import price from "./price";//引入刚才的文件
export default defineStore("main", {
state: () => {
return {
count: 123,
num: 1,
};
},
getters: {
Bignum: (state) => state.num * 10000,
Smallcount(state) {
let priceStore = price()
return priceStore.carTotal;
},
},
});
4.给getters传递参数(给getter传递参数会失去缓存这一特性,你想当于调用的是个方法)
Name() {
return (name) => "name:" + name;
},//store代码
console.log(store.Name("mike"));//vue文件调用代码
5.Composition Api setup使用
使用getters与state是一样的,直接store.getter名字即可获取。
6.options Api setup使用
在setup函数中接受store并返回。在computed里面进行接受。
7.vue2的写法
在vue2里面需要使用mapState这一工具函数前面我们在state里面介绍过它,这个函数不仅可以给state使用,getters也可以。
6.Actions
1.默认使用
actions: {
setNum() {
this.num++;
},
},//store内容
store.setNum();
action就相当于是store.method,用于修改state中的属性,this默认绑定为当前的store实例。当然在这里面我们是可以去进行异步操作的,当请求的结果需要去修改state中的数据,就将这个方法写到actions里面(推荐这么做,便于管理)。
2.访问其它的store
actions: {
setNum() {
let store = price();
store.carPrice = "1";
console.log(store.carPrice);
this.num++;
},
},
price是我引入的别的store文件,我们可以去访问这个store上面的所有属性和方法,我们也可以获取上面的一些属性进行一些判断,比如user存储着用户信息和token,我们需要判断当前有没有token,就会用到,我们还可以再引入的store上面调用patch方法。
3.Composition api setup
import useStore from "../stores/main";
export default {
name: "HelloWorld",
setup() {
let store = useStore();
return { store };
},
mounted: function () {
console.log(this.a());
},
methods: {
a() {
this.store.setNum();
},
},
};
4.不使用setup
import { mapActions } from "pinia";
import useStore from "../stores/main";
export default {
name: "HelloWorld",
mounted: function () {
console.log(this.setNum());
},
methods: {
...mapActions(useStore, ["setNum"]),
...mapActions(useStore,{a:'setNum'})
},
};
引入mapActions方法两种传递方式:
- 第一个参数都是需要解构的store,第二个参数可以是一个数组,数组的每一项是一个字符串,为对应store action方法的名字。
- 第二种是给第二个参数传递一个对象,可以修改action方法的名字,键为要修改的名字,值为store中的action方法名。
7.plugins
1.基本使用
plugin是一个函数,在main.js里面我们可以在引入createPinia方法后面使用use(plugin函数)来进行使用。
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import { createPinia } from "pinia";
function SecretPiniaPlugin(context) {
console.log(context);
return { secret: "the cake is a lie" };
}
const pinia = createPinia();
pinia.use(SecretPiniaPlugin);
createApp(App).use(router).use(pinia).mount("#app");
会自动将这个函数返回对象的值绑定到store上面(后面使用的所有store上面都有secret这个属性),上面的写法就和vue当中的混入差不多。
context参数是个对象有四个属性:
- app代表Vue实例。
- options代表传递给defineStore的配置对象。
- pinia代表当前通过createPinia穿件的pinia实例。
- store代表扩展后的store实例。
2.扩展store
你可以使用返回的形式来进行state数据的添加,也可以使用store.属性名的方式添加,不过最好使用返回值,这便于devtools更好的追踪,同时store使用了vue中的reactive这个方法,包裹自身,自动展开各种ref,computeds属性,这也是为什么可以双向绑定这些属性的原因。
3.添加新状态
给store添加初始属性,我们上面已经说过,但给state添加属性的时候,最好使用这种。
function SecretPiniaPlugin({ store }) {
let num1 = ref("123");
store.$state.num1 = num1;
store.num1 = num1;
}
默认使用返回值也只会去在store上面绑定属性,$state访问的还是undefined,在$state上面绑定属性的好处是你可以在vue开发者工具devtools中的pinia选项上面的state观察到,而使用return绑定属性的话会在pinia上面的custom properties上面,使用赋值语句观察不到这个属性。
注:使用了$state就不要使用return再次去返回这个数据,不然的话devtools就会在state部分和custom properties都出现这个属性。
4.添加外部框架属性
当添加外部属性、来自其他库的类实例或仅仅是非响应式的东西时,您应该在将对象传递给 pinia 之前使用
markRaw()
包装对象。 这是一个将路由添加到每个 store 的示例:
import { createApp, ref, toRef, markRaw } from "vue";
import App from "./App.vue";
import router from "./router";
import { createPinia } from "pinia";
function SecretPiniaPlugin({ store }) {
store.router = markRaw(router);
}
const pinia = createPinia();
pinia.use(SecretPiniaPlugin);
createApp(App).use(router).use(pinia).mount("#app");
控制台打印正常,但后面all.js抛出了一个错误,控制台还会提示 "storeid" store installed(原因未知)。
5.初始化监听
你也可以在初始化pinia的时候调用store.$subscribe(监听store)和 store.$onAction(监听action)这两个方法。
6.添加新选项
你给defineStore第二个参数传递的所有属性都会被记录到options中,你可以拿这个记录一些信息,你可以率先在初始化store的js里面引入存储用户信息的pinia。然后用到的地方加上一个属性就叫useUserData用true,不用就是没有,true的话在他的state上面加上这个用户信息。
import { defineStore } from "pinia";
export default defineStore("price", {
state: () => {
return {
userInfo:{
name: "nike",
age: 10,
sex: "man",
}
};
},
getters: {},
});
引入的用户信息如上,使用如下:
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import user from "./stores/user";
import { createPinia } from "pinia";
function SecretPiniaPlugin(context) {
let data = user().userInfo;
if (context.options.useUserData) {
context.store.$state.userData = data;
context.store.userData = data;
}
}
const pinia = createPinia();
pinia.use(SecretPiniaPlugin);
createApp(App).use(router).use(pinia).mount("#app");
9.持久化插件pinia-plugin-persistedstate
1.使用
使用npm i pinia-plugin-persistedstate进行下载,然后引入进来。
在main.js使用pinia的后面加上pinia.use( persistedstate)
createApp(App).use(router).use(createPinia().use(persistedstate)).mount("#app");
需要持久化的某个store使用添加配置,传递一个persist为true的属性。
2.配置
- persist配置时会变成对象包含下面的属性
- key:存储的名字,默认值为当前store的id,可传递一个字符串。
- storage:存储方式,默认值为localstorage,可选值localstorage和sessionstorage。
- paths:需要持久化的state数据,默认值为undefined,为null或者undefined持久化整个state,可传递字符串类型的数组,包含持久化的state属性。
- serializer:持久化时使用的序列型方法,默认读取时JSON.parse,存储时JSON.stringify。
- beforeRestore:在state数据恢复前执行,接收一个参数,这个参数可以可以访问恢复后的数据,可以进行一下用户提示类工作。
- afterRestore:在state数据恢复完毕后触发,接收参数同上。
- debug:控制恢复store过程发生错误时是否使用console.error打印错误信息,默认为false,也就是不使用。
10.总结
- pinia的灵活性远超于vuex,你可以去双向绑定数据,以及直接对store的数据进行赋值,而不是像vuex一样,一个简单的修改都要写一个Mutation,如果你是一个马虎的人,肯定会在调用的时候敲不对方法名字。
- 实现了监听state,action以及相当于vue混入的初始化数据,这都对我们的开发带来了便捷。
- 你还在犹豫什么,赶紧用起来。