vuex-pinia

59 阅读4分钟

状态管理

我们会的应用程序需要处理各种各样的数据,这些数据需 要保存在我们应用程序中的某一个位置,对于这些数据的管理我们就 称之为是 状态管理

在vue中都是组件式开发

  • 在组件里面定义的data/setup中返回的使用的数据 称之为state
  • 在template模版中使用 并且渲染成DOM 称之为view
  • 模版中产生行为事件 处理这些行为事件 有可能会修改state 称之为actions

WeChat2a3d16ed7322dc28aecfedd206ee4bc1.png

vuex和单纯的全局对象有什么区别

  1. vuex储存的状态是响应式的 当vue组件从store中读取状态的时候 若store中的state发生了改变 那么相对应的组件也会被更改
  2. 你不能直接改变store中的状态 改变store中的**状态唯一途径就是显示提交(commit)mutation **这样是我们可以方便的跟踪每一个状态的改变 从而让我们能够通过一些工具帮助我们更好的管理应用状态

单一状态树

vuex使用的是单一状态树 一个对象就包括了全部的应用层级的状态 单一状态树和模块化并不冲突

◼ 单一状态树的优势:

如果你的状态信息是保存到多个Store对象中的,那么之后的管理和维护等等都会变得特别困难;

所以Vuex也使用了单一状态树来管理应用层级的全部状态;

单一状态树能够让我们最直接的方式找到某个状态的片段;

而且在之后的维护和调试过程中,也可以非常方便的管理和维护;

辅助函数

mapState

vue2中使用

数组写法
...mapState(['映射的元素']) 他返回的是函数 所以需要展开运算符
对象写法。可以重命名
...mapState({
  sName:state=>state.name
  sAge:state=>state.age
})
然后需要在computed:{
  ...mapState({})
}

vue3中使用

如果在vue3中使用mapState的话 虚啊哟在computed调用该函数的时候 手动绑定一个this this的值就是{store:store}** 因为在vue3中是没有this的 所以在computed内部解析的时候 **无法通过this.store.state.name 来取值的 因为取不到this 所以这个时候需要手动绑定这个this

  • 第一种方式 通过调用computed的时候 利用bind 绑定一个this
import { computed } from 'vue';
import { useStore, mapState } from 'vuex';
const store = useStore()
const {name,age}=mapState(['name','age'])
const cName = computed(name.bind({$store:store}))
  • 第二种:封装一个hook
import { computed } from 'vue';
import { useStore, mapState } from 'vuex';

export default function useState(mapper){
    const store= useStore
    const stateFnsObj = mapState(mapper)
    const newState={}
    Object.keys(stateFnsObj).forEach((item,key)=>{
       newState[key]=computed(stateFnsObj[item].bind({$store:store}))
    })
    return newState
}
import { useStore } from 'vuex';
因为这块返回的是一个computed 所以及时结构里 但是还是响应式的
const { name, age } = useStore(['name', 'age'])
  • 第三种直接对store.state进行解构
const store=useStore()
 const {name:sName='111',age}=toRefs(store.state)

getters

  getters: {
  基本使用 而且第二个参数可以获取其他getters的函数
    doubolCounter(state, getters) {
      return state * 2;
    },
    也可以返回一个函数
    getFridenById(state) {
      return function (id) {
        return state.friend.find(item => item.id === id);
      };
    },
  },

mutations

 mutations: {
    // 这块可以使用一个常量 ADD_MumBer定义一个常量 用的对象的计算属性[一个变量]
    // [ADD_MumBer](state){state.counter}
    increment(state) {
      state.counter++;
    },
  },
   可以在template里面调用引入的函数
   methods:{
  changName(){
    this.$store.commit('increment','www')
  }
    ...mapMutations(['changeName','increnentievel'])
  }

actions

actions提交的是mutation 而不是直接修改状态 因为在vuex里面 它所有的修改状态state的值都是通过mutation的 它可以进行任意异步操作 context是一个和store实例均有相同方法和属性的context对象;所以我们可以从其中获取到commit方法来提交一个mutation,或者通过 context.state 和 context.getters 来获取 state 和getters **可以在actions中的异步函数调用一个promise 这样就可以在dispatch中监听是否异步函数执行完毕 **

  actions: {
    incremen(context) {
      context.commit('increment');
      return new Promise(async (resolve, reject) => {
        const data = await '调用请求返回的值';
        resolve(data);
      });
    },
  },

modules

  • 由于使用单一状态树,应用的所有状态会集中到一个比较大的对象,当应用变得非常复杂时,store 对象就有可能变得相当臃
  • 为了解决以上问题,Vuex 允许我们将 store 分割成模块(module);
  • 每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块;

WeChat50b1c287fc6c9b86921e51e32f19b8ce.png

  • 在获取state的时候需要store.home.message 在模块化中 actions/getters/mutations 他都默认合并在store里面
  • **namespaced: true **模块抽离出来的代码是否有命名空间 这样的话actions/getters/mutations 使用的话需要语法糖$store.getters["counter模块/doubleCount方法"]
  • 在store.dispatch("counter模块/doubleCount方法") 如果我们需要在action里面修改root的state

WeChatbe0c3f75c53d1fd4f1e6181da6a809e4.png

pinia

本质:pinia 最初是为了探索 vuex 的下一次迭代会是啥样的 结合了vuex5核心团队的想法 最终团队意识到了pinia已经实现了vuex5中的大部分内容 最终选择了pinia代替了vuex

与vuex相比

  • pinia提供了更简单的api 具有更少的仪式感 提供了堆compositionAPI的支持
  • 重要的是 与ts 一起使用时候具有可靠的类型推断支持
  • mutation将不再存在 **它们经常认为这个操作是非常鸡肋的 pinia也提供了devtools的集成 **
  • 更友善的ts支持
  • 不再有modules的嵌套结构
  • 可以灵活使用每一个store 它们是通过扁平化的方式来互相使用 它们都是互相独立的
  • 也不再有命名空间的概念 不需要记住他们的复杂关系

定义store

需要通过defineStore('store名称 这个是连接devtools的标识',{}) 返回函数统一用useX命名 当你再次调用它的时候 他就返回时当前定义的store

  • 在获取数据的解构赋值的时候 可以通过vue的toRefs包裹 或者用pinia的storeToRefs来包裹进行数据响应式处理
  • store 中也有this 这个this就是当前store的实例
  • state 其实就是相当于组件的data
  • getters 其实就是相当于组件的computed 并且他可以直接使用this
  • actions 其实就是相当于组件中的methods 适合定义业务逻辑 并且也可以使用this
import { useCounter } from '../stores/counter';
import { defineStore } from 'pinia';
const useCounter = defineStore('counter', {
  state: () => {
    message: 111;
  },
  getters: {
    //1.
    doubleCount(state) {
      return state.message * 2;
    },
    // 2.引入另一个getter
    doubleCou1nt(state) {
      return this.doubleCount + 1;
    },
    // 3.返回一个函数
    getFriendByid(state) {
      return function (id) {
        return state.message === id;
      };
    },
    // 使用到其他模块的getters
    showMessage(state) {
      // 获取到user的信息
      const userStore = useCounter();
      // 获取到自己的信息
      // 拼接信息
      return `name:${userStore.name}-count:${state.message}`;
    },
  },
  actions: {
    increment() {},
  },
});

在调用/操作的时候

const consterStore = useCounter()
const { message } = storeToRefs(consterStore)
// 可以直接进行修改state的值
consterStore.message = 299

// $reset 恢复初始化数值 因为在他初始化的时候 有过缓存保存过的
consterStore.$reset()

// $patch 一次性修改多个状态
useCounter.$patch({
  message: 222,
  name: '3123'
})

// 直接替换整个store 
useCounter.$state = {
  name: '3213',
  age: '312312'
}