vue3 局部状态管理

vue3 父子组件通信能满足大部分的需求,无需应用状态管理。但某功能模块内组件多、层级深时,就可能有状态管理的需求。

Provide / Inject

若仅需要在某个模块内管理状态,完全可以使用 vue3 的 provide / inject api,在模块的根组件上定义数据,后代组件中注入依赖

在线查看示例

模块根组件中定义数据及管理方法

js
const data = reactive({
  name: 'Zzz',
  info: { age: null, gender: null },
})
provide('data', readonly(data))
provide('setName', val => (data.name = val))
provide('setInfo', info => {
  Object.assign(data.info, info)
})

后代组件中注入使用

js
const data = inject('data')
const setName = inject('setName')
const setInfo = inject('setInfo')

注意,注入后,直接更改引用类型内部数据会增加管理上的混乱,是需要避免的。因为多个组件可能会同时修改和访问同一个状态,导致难以追踪和理解状态的变化。

为了避免管理上的混乱,在设计状态管理时:

  • 明确定义状态的作用域和职责,确保每个状态只被相关的组件使用和修改。
  • 使用明确的操作来更新状态。建议使用具有明确目的的方法来更新状态,而不是直接修改引用类型状态的内部值。这样可以更容易地跟踪和理解状态的变化。

vuex / pinia

pinia

vuex

参照官方文档

pinia 示例:

store.js:

选项式风格

js
import { defineStore } from 'pinia'

export const usePatientStore = defineStore('patient', {
  state: () => ({
    name: 'Zzz',
    info: { age: null, gender: null },
  }),
  actions: {
    setName(val) {
      this.name = val
    },
    setInfo(data) {
      if (typeof data === 'object') {
        Object.assign(this.info, data)
      }
    },
  },
})

组合式风格

js
import { ref, reactive } from 'vue'
import { defineStore } from 'pinia'

export const usePatientStore = defineStore('patient', () => {
  const name = ref('Zzz')
  const info = reactive({ age: null, gender: null })

  const setName = (val) => {
    name.value = val
  }
  const setInfo = (data) => {
    if (typeof data === 'object') {
      Object.assign(info, data)
    }
  }
  return { name, info, setName, setInfo }
})

使用:

js
import { usePatientStore } from './store.js'

const patientStore = usePatientStore()

// 读取
console.log(patientStore.name)
// actions
function handleSetInfo() {
  patientStore.setInfo({ age: 18, gender: 'male' })
}

store 模式

对于简单的需求,完全可以使用 store 模式实现局部状态管理

在线查看示例

store.js:

js
import { reactive } from 'vue'

export default {
  state: reactive({
    name: 'Zzz',
    info: { age: null, gender: null },
  }),
  setInfo(data) {
    if (typeof data === 'object') Object.assign(this.state.info, data)
  },
}

使用:

js
import store from './store.js'

function handleSetInfo() {
  store.setInfo({ age: 18, gender: 'male' })
}

EventBus

组件间通信利器

mitt: mitt 是一个轻量级的JavaScript事件总线库,拥有简单而强大的API,总体大小仅为200字节。它具有与Node.js的EventEmitter类似的API和命名约定,并且没有依赖项。它支持IE9+以及任何JavaScript运行时环境。

使用示例
js
import mitt from 'mitt'

const emitter = mitt()

// listen to an event
emitter.on('foo', e => console.log('foo', e) )

// listen to all events
emitter.on('*', (type, e) => console.log(type, e) )

// fire an event
emitter.emit('foo', { a: 'b' })

// clearing all events
emitter.all.clear()

// working with handler references:
function onFoo() {}
emitter.on('foo', onFoo)   // listen
emitter.off('foo', onFoo)  // unlisten

有时间可以看看它的源码,非常精简,几分钟就能看完

或者查看另一篇文章Mitt源码学习

Last updated: