vue全局状态管理vuex和pinia

179 阅读5分钟

写在前面

一个中大型单页面应用中,通常会有很多组件,组件与组件之间经常会读取或修改某些相同的数据,这时候最容易出现以下两个问题:

● 多个组件依赖同一个数据,如何获取这个数据的值?

● 不同的组件的行为改变了同一个数据,如何做到一个组件改变这个数据之后,其他页面组件也同步到这个数据的改变?

vue提供了两个管理状态工具,分别是vuex和pinia。其中pinia是官方网站推荐的。

vuex

vuex核心概念

  • state:存放共享状态。当需要在组件中使用公共状态时,通过“store.state.状态名称”进行获取。
  • mutations:操作state成员的方法集,只能是同步操作。mutations是操作state数据的方法的集合,例如对状态的修改、增加、删除等。
  • getters:加工state成员供外界使用。如果有一些属性是通过公共状态计算得出的,并且在全局范围内需要共享,那么使用Vuex的getters来定义这个派生状态是一个好方法。
  • actions:提交mutation,可以是异步操作。actions定义了一系列操作,类似于mutations,不同的是actions可以是异步的,可以通过actions来改变store中的state。
  • modules:将store模块化,各module有自己的state、getters、mutations、actions和modules。由于Vuex使用单一状态树,一个应用只有一个store,所有状态都会集中到这个store对象里面,当应用非常复杂,共享的状态非常多的时候,store对象就有可能变得相当臃肿,难以管理。这个时候,Vuex允许将store分割成模块(module),采用模块化管理模式,使store的结构更清晰而方便管理。

使用步骤

将vuex挂载到应用实例中

import { createApp } from 'vue'
import router from './router/index'
import store from './store'
import App from './App.vue'

createApp(App)
    .use(router)  // 注册路由
    .use(store)   // 注册状态管理
    .mount('#app')

定义store及子模块store

  • 子模块user
const user = {
  // 设置命名空间
  namespaced: true,
  // 自定义一个要获取的数据
  state: {
    name: "默认",
    age: 0,
  },
  // 同步操作方法
  mutations: {
    setName(state, name) {
      state.name = name;
    },
    setAge(state, age) {
      state.age = age;
    },
  },
  // 异步操作
  actions: {
    updateProfile({ commit }, profile) {
        commit('setName', profile.name);
        commit('setAge', profile.age);
      }
  },
  // 供外界使用
  getters: {
    fullName: (state) => `${state.name} (${state.age})`
  }
};

export default user;
  • 定义总的store并引入子模块store
import { createStore } from 'vuex';
import user from './modules/user';

const store = createStore({
  state: {
    count: 1
  },
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  actions: {
    increment({ commit }) {
      commit('increment');
    }
  },
  getters: {
    doubleCount: (state) => state.count * 2
  },
  // 子模块
  modules: {
    user
  }
});

export default store;

在组件中使用

<template>
  <h1>我是vuexDemo</h1>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';

// 获取 Vuex store 实例
const store = useStore();

// 计算属性 count
const count = computed(() => store.state.count);

// 方法 increment
const increment = () => {
  store.dispatch('increment');
};
</script>

<style>
</style>

持久化工具-vuex-persistedstate

地址:github.com/robinvdvleu…

  • 安装:pnpm install vuex-persistedstate --save
  • 使用插件:在createStore中配置plugins选项
import { createStore } from "vuex";
import createPersistedState from "vuex-persistedstate";

const store = createStore({
  // ...
  plugins: [createPersistedState(
    {
      paths: ['user'] //需要持久化的模块
    }
  )],
});

Vuex特点

集中式存储

  • Vuex 采用集中式存储管理应用的所有组件的状态。
  • 所有的状态都存储在一个全局的 store 对象中,便于管理和调试。

严格模式

  • Vuex 提供了严格模式,可以在开发过程中检测对状态的非法修改,确保状态的改变只能通过提交 mutation 来进行。

模块化

  • Vuex 支持模块化,可以将状态、mutations、actions 和 getters 分成多个模块,适用于大型项目。

插件支持

  • Vuex 支持插件,可以扩展其功能,例如持久化插件、日志记录插件等。

pinia

Vuex 有一个概念,带有多个模块的单一 store。这些模块可以被命名,甚至可以互相嵌套。

将这个概念过渡到 Pinia 最简单的方法是,你以前使用的每个模块现在都是一个 store。每个 store 都需要一个 id,类似于 Vuex 中的命名空间。这意味着每个 store 都有命名空间的设计。嵌套模块也可以成为自己的 store。互相依赖的 store 可以直接导入其他 store。

pinia核心概念

  • state: 需要共享的状态信息
  • actions: 在actions中提供方法并且修改数据(包括同步和异步,pinia中没有mutations)
  • getters: 计算属性

使用步骤

将pinia挂载到应用中

在main.ts中挂载pinia

import { createApp } from 'vue'
import App from './App.vue'

import { createPinia } from 'pinia'

const pinia = createPinia()

createApp(App)
  .use(pinia)		//注册pinia
  .mount('#app')

定义store

import { defineStore } from 'pinia'
// 创建store,命名规则: useXxxxStore
// 参数1:store的唯一表示
// 参数2:对象,可以提供state actions getters
const useUserStore = defineStore('user', {
  state: () => {
    return {
      name: '默认',
      age: 0
    }
  },
  getters: {
   
  },
  actions: {
    
  },
})

export default useCounterStore

在组件中使用

<script setup>
import useUserStore from './store/User'

const user = useCounterStore()
</script>

<template>
  <h1>使用用户信息---{{ user.name }}</h1>
</template>

<style></style>

持久化工具-pinia-plugin-persistedstate

地址:prazdevs.github.io/pinia-plugi…

  • 安装:pnpm add pinia-plugin-persistedstate --save
  • 将插件安装到pinina实例:

位置一般在src/store/index.ts

import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
  • 使用插件:设置 persist选项为true
export const useUserStore = defineStore('user', {
  state: () => ({
    ...
  }),
  getters: {
    ...
  },
  actions: {
    ...
  },
  persist: true
})

pinia官方使用说明

pinia特点

  • 组合式 API:Pinia 是基于 Vue 3 的组合式 API 设计的,更符合 Vue 3 的设计理念。 允许你在组件内部直接定义和使用状态,更加灵活和直观。
  • 更灵活的定义方式:Pinia 的定义方式更加灵活,支持在组件内部直接定义和使用状态,减少了样板代码。 更好的调试工具:
  • Pinia 提供了更好的调试工具和开发者体验,例如自动类型推断和更好的错误提示。
  • 无严格模式:Pinia 没有严格模式,但提供了更好的调试工具和开发者体验。

vuex与pinia项目结构

Pinia 的目录一般被命名为 stores 而不是 store。这是为了强调 Pinia 可以使用多个 store,而不是 Vuex 的单一 store。

# Vuex 示例(假设是命名模块)。
src
└── store
    ├── index.js           # 初始化 Vuex,导入模块
    └── modules
        ├── module1.js     # 命名模块 'module1'
        └── nested
            ├── index.js   # 命名模块 'nested',导入 module2 与 module3
            ├── module2.js # 命名模块 'nested/module2'
            └── module3.js # 命名模块 'nested/module3'

# Pinia 示例,注意 ID 与之前的命名模块相匹配
src
└── stores
    ├── index.js          # (可选) 初始化 Pinia,不必导入 store
    ├── module1.js        # 'module1' id
    ├── nested-module2.js # 'nested/module2' id
    ├── nested-module3.js # 'nested/module3' id
    └── nested.js         # 'nested' id

参考资料:

mp.weixin.qq.com/s/Gi-10LhIB…