Vue3 官推的状态管理 Pinia

一、Pinia是什么?

在Vue2中一般采用Vuex进行状态管理,在Vue3中推荐使用Pinia,这Vue3中推荐使用Pinia,这是最新一代轻量级状态管理插件。Vuex将不再接受新的功能。推荐使用Pinia。

二、Pinia的特点

  1. 支持Vue2和Vue3,两者都可以使用Pinia
  2. 语法简洁,支持Vue3中的setup的写法,不必像Vue2中那样定义state、mutations、actions、getters等,可以按照setup Composition API的方式返回状态和改变状态的方法实现代码的扁平化;
  3. 支持Vuex中state、actions、getters形式的写法,丢弃了mutations开发时候不用根据同步异步来决定mutation或者actions,Pinia中只有actions;
  4. 对TyppeScript支持非常友好

三、Pinia的使用

1.npm install pinia -s

2.创建pinia实例

// src/store/index.ts
import { createPinia } from 'pinia';

const pinia = createPinia();
export default pinia ;

3.注册到App实例上

// src/main.ts
import App from './App.vue';
import { createApp } from 'vue';
import pinia from '@/store';

const app = createApp(App);
app.use(pinia);

4.模块化管理

defineStore()方法的第一个参数:为仓库起一个名字呢,不可重复,唯一的;
defineStore()方法的第二个参数:有setup和option两种写法

// src/store/modules/demo.ts(setup写法)
import { defineStore } from 'pinia';
import { store } from '/@/store';

const useDemoStore = defineStore('demo',()=>{
 	const counter = ref(0);
 	const increment = ()=>{
 		counter.value++;
 	}
 	return {counter,increment}
});
export default useDemoStore;
// src/store/modules/demo.ts(option写法)
import { defineStore } from 'pinia';
import { store } from '/@/store';

export const useDemoStore = defineStore('demo',{
 	state:()=>{
 		return {
 			name:'张三',
 			count:0
 		}
 	},
 	getters:{
 		getName:(state)=>{
 			// return this.name+'getName';
 			return state.name+'getName';
 			return (id)=>state.list.filter(v=>v.id===id);//getters传参
 		}
 	},
 	actions:{
 		updateName:(val:string)=>{
 			this.name=val;
 		}
 	},
 	persist:true
});

state:用来存储全局的状态,这边定义的,可以全局访问;
getters:和Vuex一样,来监视或者计算状态变化;具有缓存功能,组件中调用多次,实际在store中只执行了一次;
actions:适合处理修改逻辑复杂的数据,可以在actions中定义好函数,然后在组件中调用,是state数据相关的业务逻辑,需求不同,逻辑不同。
注:getters和actions里可以直接使用this。

5.组件中使用

// src/views/Demo.vue
<template>
  <h1>counter:{{counter}}</h1>
  <el-button @click="add"> 自增 </el-button>
</template>
<script setup lang="ts">
  import { storeToRefs} from 'pinia';
  import useDemoStore from '/@/store/modules/demo.ts';

  const demoStore = useDemoStore();
  const { counter } = storeToRefs(demoStore);
  const add = ()=>{
  	demoStore.increment();
  }
  console.log(demoStore.getName());
</script>

注:无论是pinia还是vuex,通过解构的方式获取状态,会导致状态失去响应性。如:
const { counter } = demoStore;
此时counter丢失了响应性,当其值发生改变时,其他组件不会监听到。所以pinia提供了storeToRefs函数,使其解构出来的状态仍然具有相应性。
const { counter } = storeToRefs(demoStore);

6.路由中使用

演示在全局路由守卫中获取状态值。创建一个路由守卫,在路由守卫中使用nprogress显示页面加载进度。

1.创建全局路由守卫
  1. 安装nprogress

npm install nprogress
npm install @types/nprogress -D

  1. 创建全局路由守卫
// src\router\guard\index.ts
import nProgress from 'nprogress';
import 'nprogress/nprogress.css';
nProgress.configure({
	showspinner:false,
})
// 全局前置守卫
router.beforeEach((to,from)=>{
	nProgress.start();
    return true;
})
// 全局后置守卫
router.afterEach((to,from)=>{
	nProgress.done(true);
})

3.在main.ts中引入全局守卫路由

import '@/router/guard/index'
2.全局守卫中使用全局状态

实际开发中,路由切换时,可能需要从全局状态中获取token等信息,判断是否能进入下一页面。

// src\router\guard\index.ts
...
import useDemoStore from '@/store/modules/demo'
import {storeToRefs} from 'pinia'
...
const demoStore = useDemoStore();
const {counter} = storeToRefs(demoStore);
// 全局前置守卫
router.beforeEach((to,from)=>{
	nProgress.start();
	console.log(counter);
    return true;
})

在钩子函数外,pinia还没有挂载,所以会报错,应该修改后如下:

// src\router\guard\index.ts
import  router from '@/router';
import nProgress from 'nprogress';
import 'nprogress/nprogress.css';
import useDemoStore from '@/store/modules/demo'
import {storeToRefs} from 'pinia'
nProgress.configure({
	showspinner:false,
})
// 全局前置守卫
router.beforeEach((to,from)=>{
	nProgress.start();
	const demoStore = useDemoStore();
    const {counter} = storeToRefs(demoStore);
   	console.log(counter);
    return true;
})
// 全局后置守卫
router.afterEach((to,from)=>{
	nProgress.done(true);
})

# 四、Pinia状态持久化

 ## 1.为什么需要状态持久化?
因为状态存储到浏览器内存中,刷新浏览器后,重新加载页面会重新初始化vue.pinia,导致浏览器存储中的数据丢失。
实际项目中,浏览器刷新时,这有些数据希望保存下来。
解决方案:状态改变时将其同步到浏览器的存储中,如cookie,localStorage,sessionStorage,每次初始化状态时从存储中获取初始值即可。
## 2.插件pinia-plugin-persistedstate

 1. npm install pinia-plugin-persistedstate 
 2. src/store/index.ts 
```typescript
import { createPinia } from 'pinia';
import piniaPluginPersistedState from 'pinia-plugin-persistedstate';

const pinia = createPinia();
pinia.use(piniaPluginPersistedState);
export default pinia;
  1. 模块管理中的使用:
// src/store/modules/demo.ts
import { defineStore } from 'pinia';
import { store } from '/@/store';

const useDemoStore = defineStore('demo',()=>{
 	const counter = ref(0);
 	const increment = ()=>{
 		counter.value++;
 	}
 	return {counter,increment}
},{ persist:true });
export default useDemoStore;

persist : 值为true,状态存储在localStorage中;该localStorage中的key为模块名“demo”。
如何修改key?如何将状态存储到SessionStorage?

{
	persist:{
		key:"byyourself",
		storage:"sessionStorage",
	}
}

四、修改数据 $patch

const updataName = () =>{
 // 1.修改单个数据
 demoStore.name='李四';
 // 2.修改多个数据-$patch
 demoStore.$patch({
 	name:'李四',
 	count:18
 )
 // 3.修改复杂数据-$patch+函数
 demoStore.$patch((state)=>{
 	state.name='李四';
 })
 // 4.使用action处理数据
 demoStore.updateName('李四')
}

五、重置数据 $reset

const resetName = ()=>{
	demoStore.$reset();
}

pinia setup 方式构建$reset方法失效解决方案:

import { creatPinia } from 'pinia';
const pinia = creatPinia();
pinia.use((store)=>{
	const initialStata=JSON.parse(JSON.stringify(store.$state));
	store.$reset=()=>{
		store.$patch(initialStata)
	}
})

六、监听数据 $subscribe

     const subscribeName = demoStore.$subscribe((mutations,state)=>{
        // 监听store中的某个值变化,处理一些逻辑
        // $subscribe第一个参数mutations,包含了3个属性:
        //      1.events:改变的具体数据,newValue,oldValue;
        //      2.storeId:当前store的Id,这里是'demo';
        //      3.type:记录改变数据途径(direct:通过actions改变;patch object:
        //        提供$patch传递的方式改变的;patch function:通过$patch传递函数)

          // $subscribe第二参数,是options对象,是各种参数配置
          //        imemediate,deep和vue2中的watch里的参数一样;
          //        detached:默认是false,正常情况下,当订阅所在的组件被卸载时,订阅失败;
          //                  当值为true时,即便订阅的组件被卸载,订阅依然生效;

      },{
        imemediate:false,
        deep:false,
        detached:false
      })
      // 停止订阅监听
      subscribeName();

七、监听Actions $onAction()

      const subAction=demoStore.$onAction((name,store,args,after,onError)=>{
        // name:   actions的名字;
        // store:  store实例;
        // args:   调用这个actions用的参数;
        // after((result)=>{
        //   // 在action执行完毕后执行的逻辑
        // })
        // onError((result)=>{
        //   // 在action执行异常后执行的逻辑
        // })
      },true)// true组件卸载后,监听依旧保留
      // 停止监听Actions
      subAction();

Tip:可以在pinia实例上使用watch()函数侦听整个state。

      watch(pinia.state,(state)=>{
        // 每当状态发生变化时,将整个state持久化到本地存储
        localStorage.setItem(,JSON.stringify(state))
      },}{deep:true})

Tip:不使用setup时。

      import { mapState, mapActions } from 'pinia';
      import { mainStore } from '@/store/index.ts'

      export default {
        computed:{
          ...mapState(mainStore,['name','count','getName']),
          // 定义
          myOwnName:"name"
        },// 可以在组件中通过this.name访问
        methods:{
          ...mapActions(mainStore,['updateName']),d
           // 定义
          myOwnName:"updateName"
        },// 可以在组件中通过this.updateName()调用
        }
      }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值