Pinia和Vuex (vue3.x)

316 阅读4分钟

Pinia和Vuex一样都是是vue的全局状态管理器。其实Pinia就是Vuex5,只不过为了尊重原作者的贡献就沿用了这个名字。 vue3.x中建议用pinia

以vue3.x中使用 vuex 和 pinia 来对比总结:

安装

Vuex

npm i vuex -S

Pinia

npm i pinia -S

挂载

Vuex

  1. 在src目录下 新建 store目录 -> store目录下 新建 index.js文件
import { createStore } from 'vuex'
export default createStore({
    //全局state,类似于vue种的data
    state() {
      return {
        vuexmsg: "hello vuex",
        name: "xiaoyue",
      };
    },
    //修改state函数
    mutations: {
    },
    //提交的mutation可以包含任意异步操作
    actions: {
    },
    //类似于vue中的计算属性
    getters: {
    },
    //将store分割成模块(module),应用较大时使用
    modules: {
    }
})
  1. mian.js中引进注册
import { createApp } from 'vue'
import App from './App.vue'
import store from '@/store'
createApp(App).use(store).mount('#app')
  1. .vue组件中使用
<template>
  <div></div>
</template>
<script setup>
    import { useStore } from 'vuex'
    let vuexStore = useStore()
    console.log(vuexStore.state.vuexmsg); //hello vuex
</script>

Pinia

没有了mutations和modules

  1. main.js中引入注册
import { createApp } from "vue";
import App from "./App.vue";

import {createPinia} from 'pinia'
const pinia = createPinia()
createApp(App).use(pinia).mount("#app");
  1. 在src目录下 新建 store目录 -> store目录下 新建storeA.js
import { defineStore } from "pinia";
//1. Option Store
export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  getters: {
    double: (state) => state.count * 2,
  },
  actions: {
    increment() {
      this.count++
    },
  },
})

// 2. Setup Store: 与 Vue 组合式 API 的 setup 函数 相似,可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象
export const useCounterStore = defineStore("counter", () => {
  const count = ref(0);
  const doubleCount = computed(() => count.value * 2);
  function increment() {
    count.value++;
  }

  return { count, doubleCount, increment };
});

在 Setup Store 中:

  • ref() 就是 state 属性
  • computed() 就是 getters
  • function() 就是 actions
  1. 组件中使用
<template>
  <div></div>
</template>
<script setup>
    import { storeA } from '@/store/storeA'
    let piniaStoreA = storeA()
    console.log(piniaStoreA.count); //0
</script>

pinia中没有了mutations和modules,pinia不必以嵌套(通过modules引入)的方式引入模块,因为它的每个store便是一个模块,如storeA,storeB... 。在我们使用Vuex的时候每次修改state的值都需要调用mutations里的修改函数(下面会说到),因为Vuex需要追踪数据的变化,这使我们写起来比较繁琐。而pinia则不再需要mutations,同步异步都可在actions进行操作,至于它没有了mutations具体是如何最终到state变化的,这里我们不过多深究,大概好像应该是通过hooks回调的形式解决的把(我也没研究过,瞎猜的。

修改状态

Vuex

import { createStore } from "vuex";
export default createStore({
  strict: true,
  //全局state,类似于vue种的data
  state() {
    return {
      vuexmsg: "hello vuex",
    }
  },

  //修改state函数
  mutations: {
    setVuexMsg(state, data) {
      state.vuexmsg = data;
    },
  },

  //提交的mutation可以包含任意异步操作
  actions: {
    async getState({ commit }) {
      //const result = await xxxx 假设这里进行了请求并拿到了返回值
      commit("setVuexMsg", "hello juejin");
    },
  }
});
<template>
  <div>{{ vuexStore.state.vuexmsg }}</div>
</template>
  1. 可以直接修改state中的值,但一般不建议这样做,这样vuex就追踪不到数据的变化了
import { useStore } from 'vuex'
let vuexStore = useStore()
vuexStore.state.vuexmsg = 'hello juejin'
console.log(vuexStore.state.vuexmsg)
  1. 可以通过提交mutation,通过mutation去修改state中值, 建议这样做
import { useStore } from 'vuex'
let vuexStore = useStore()
vuexStore.commit('setVuexMsg', 'hello juejin');
console.log(vuexStore.state.vuexmsg)
  1. 可以通过dispatch分发action, vuex内action在去提交mutation来修改state
import { useStore } from 'vuex'
let vuexStore = useStore()
vuexStore.dispatch('getState')

一般vuex中actions是放异步函数,拿请求后端接口为例,当后端接口返回值的时候,actions中会提交一个mutations中的函数,然后这个函数对vuex中的状态(state)进行一个修改,组件中再渲染这个状态

Pinia

import { defineStore } from "pinia";
export const storeA = defineStore("storeA", {
  state: () => {
    return {
      piniaMsg: "hello pinia",
      name: "xiao yue",
    };
  },
  actions: {
    setName(data) {
      this.name = data;
    },
  },
});
  1. 直接修改,相比于vuex,pinia每次直接修改,调试工具是可以记录到变化的
import { storeA } from '@/store/storeA';
const piniaStoreA = storeA();
console.log(piniaStoreA.piniaMsg); // hello pinia
piniaStoreA.piniaMsg = 'hello juejin';
console.log(piniaStoreA.piniaMsg); // hello juejin
  1. 通过$patch 去修改
import { storeA } from '@/store/storeA';
const piniaStoreA = storeA();
piniaStoreA.$patch({
    piniaMsg: 'hello juejin',
    name: 'wxz'
});

// $patch的参数可以是回调函数
piniaStoreA.$patch((state)=>{
    state.piniaMsg = 'hello juejin';
    state.name = 'wxz';
});
  1. 通过调用action中方法去修改,直接调用方法而不需要dispatch。 建议用这种处理方法,这样的话,可以实现整个数据流程都在状态管理器内部,便于管理
import { storeA } from '@/store/storeA';
const piniaStoreA = storeA();
piniaStoreA.setName('wxz');
  1. 重置状态 state

Pinia可以使用$reset将状态重置为初始值

import { storeA } from '@/store/storeA';
const piniaStoreA = storeA();
piniaStoreA.$reset();

Pinia解构(storeToRefs)

当需要使用多个state中值时,使用解构往往是最方便的,但es6传统的解构会使 state中值失去响应式, 所以pinia提供了 storeToRefs api去解决这个问题

import { storeA } from '@/store/storeA';
const piniaStoreA = storeA();
const {piniaMsg, name } = storeToRefs(piniaStoreA);
piniaStoreA.$patch({
  name: 'daming'
})
// 页面数据发生变化

getters

Vuex中的getters和Pinia中的getters用法是一致的,都具有缓存作用,用于监听响应式依赖变化从而重新计算返回相应值

项目很大时分模块

vuex -> modules

每个模块都拥有自己state,mutations,actions...。般来说每个module都会新建一个文件,然后再引入总的入口index.js中,将其注册到vuex入口index.js文件的modules里。 而为了避免不同模块有重名,一般采用命名空间 namespaced: true

export const moduleA = {
  namespaced: true,
  state: () => ({
    count: 1,
  }),
  mutations: {
    setCount(state, data) {
      state.count = data;
    },
  },
  actions: {
    getuser() {
      //do something
    },
  },
}

//  store/index.js
import moduleA from './modules/moduleA'
export default createStore({
  strict: true,
  //全局state,类似于vue种的data
  state() {
    return {
      vuexmsg: "hello vuex",
      name: "xiaoyue",
    };
  },
  modules: {
    moduleA
  },
});

Pinia

Pinia每个状态库本身就是一个模块,项目很大需要多个store时,直接定义多个store传入不同的id即可

import { defineStore } from "pinia";

export const storeA = defineStore("storeA", {...});
export const storeB = defineStore("storeB", {...});
export const storeC = defineStore("storeB", {...});