vue:状态管理库及其部分原理(Vuex、Pinia)

1、为什么要用状态管理库?

多组件的状态共享问题: 当多个组件需要访问和修改相同的数据时,我们需要在组件之间传递 props或者使用事件总线。当,应用就会变得难以维护和调试。

多组件状态同步问题: 当一个组件修改了状态,其他组件可能无法立即得知该变化。

状态变更的追踪问题: 无法追踪到状态的变化是由何处引起的,使得调试和维护变得困难。

2、Vuex

2.1、核心概念

2.1.1、State:用于存储应用程序的状态数据

当你需要在多个组件之间共享数据时,可以将这些数据放入state中。

例如,保存用户登录状态、购物车中的商品列表等。

可以通过在组件中使用store.state.xxx或计算属性来获取状态数据。

2.1.2、Mutation:用于修改状态的方法,必须是同步函数。

什么时候用? 当你需要修改状态时。让所有的状态变更都经过 mutation可以保证状态的变更是可追踪的。

通常,一个 mutation 对应一个状态变更操作。例如,修改用户登录状态、添加商品到购物车等。

2.1.3、Action:用于处理异步逻辑或提交多个 mutation。

什么时候用? 当你需要处理异步操作(例如发起网络请求)或需要在一个 action 中提交多个 mutation 时。

Action 可以包含任意的异步操作,并可以通过提交 mutation 来修改状态。

例如,获取用户信息的异步请求、添加多个商品到购物车等。

2.1.4、Getter:用于从状态中获取派生数据的方法。

什么时候用? 当你需要根据状态state.xxx计算出一些数据时。

Getter 可以将一些复杂的数据计算逻辑封装起来,并在组件中使用 store.getters 来获取计算后的数据。

例如,基于购物车商品列表计算购物车总价、根据用户权限判断是否具有管理员角色等。

2.2、原理(v4.0.2)

2.2.1、vuex如何挂载到vue实例的

install (app, injectKey) {
  // 使用`vue.provide()`将`vuex`提供给整个应用
  app.provide(injectKey || storeKey, this)
  // 将vuex实例赋值给vue.$store;
  // 在项目的非setup中可以使用this.$store.state.xxx取值就是这样来的
  app.config.globalProperties.$store = this
}

2.2.2、useStore 源码

import { inject } from 'vue'

export const storeKey = 'store'

export function useStore (key = null) {
  return inject(key !== null ? key : storeKey)
}

commit源码

  commit (_type, _payload, _options) {
    const { type, payload, options } = unifyObjectStyle(_type, _payload, _options)

    const mutation = { type, payload }
    // 查找该类型对应的 mutations
    const entry = this._mutations[type]
    if (!entry) {
      return
    }
    // 执行mutations对应的处理函数
    this._withCommit(() => {
      entry.forEach(function commitIterator (handler) {
        handler(payload)
      })
    })

    // 通知订阅者
    this._subscribers
      .slice() // shallow copy to prevent iterator invalidation if subscriber synchronously calls unsubscribe
      .forEach(sub => sub(mutation, this.state))
  }

3、Pinia

相比vuex的优势:

  • 可通过devtools追踪数据变化,无需通过commit提交Mutation
  • 支持TS,提供代码自动补全,源码为TS编写;vuex是用JS编写的,vuex要支持TS需要安装插件
  • pinia更轻,大小只有 1kb 左右

改变状态的方法

  • 直接修改变量
  • 调用action
  • 调用patch

3.1原理

install 原理与vuex一致

  	let toBeInstalled: PiniaPlugin[] = []
    install(app: App) {
      if (!isVue2) {
        pinia._a = app
        // 暴露pinia,组件通过inject注入pinia实例
        app.provide(piniaSymbol, pinia)
        // 模版中可通过$pinia访问
        app.config.globalProperties.$pinia = pinia
        // 将pinia的plugin 存到插件列表
        toBeInstalled.forEach((plugin) => _p.push(plugin))
        toBeInstalled = []
      }
    },

pinia的plugin实现原理
1、在调用vue.use(pinia)之前注入插件的情况,会将plugin存放到toBeInstalled列表,
2、调用vue.use(pinia)之后,会将toBeInstalled的插件存到pinia实例的_p中
3、调用useStore时将plugin注入每个Store实例

    pinia.use(plugin) {
      if (!this._a && !isVue2) {
        toBeInstalled.push(plugin)
      } else {
        _p.push(plugin)
      }
      return this
    }
useStore(pinia) {
 if (!pinia._s.has(id)) {
      // creating the store registers it in `pinia._s`
      if (isSetupStore) {
        createSetupStore(id, setup, options, pinia)
      } else {
        createOptionsStore(id, options as any, pinia)
      }

      /* istanbul ignore else */
      if (__DEV__) {
        // @ts-expect-error: not the right inferred type
        useStore._pinia = pinia
      }
    }
}
createSetupStore () {
	pinia._p.forEach((extender) => {
		assign(
        store,
        scope.run(() =>
          extender({
            store: store as Store,
            app: pinia._a,
            pinia,
            options: optionsForPlugin,
          })
        )!
      )
	}}

你可能感兴趣的:(vue.js,前端,javascript)