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实例的

js 复制代码
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 源码

js 复制代码
import { inject } from 'vue'

export const storeKey = 'store'

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

commit源码

typescript 复制代码
  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一致

typescript 复制代码
  	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实例

typescript 复制代码
    pinia.use(plugin) {
      if (!this._a && !isVue2) {
        toBeInstalled.push(plugin)
      } else {
        _p.push(plugin)
      }
      return this
    }
typescript 复制代码
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
      }
    }
}
typescript 复制代码
createSetupStore () {
	pinia._p.forEach((extender) => {
		assign(
        store,
        scope.run(() =>
          extender({
            store: store as Store,
            app: pinia._a,
            pinia,
            options: optionsForPlugin,
          })
        )!
      )
	})
}
相关推荐
腾讯TNTWeb前端团队4 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰7 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪7 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪8 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy8 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom9 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom9 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom9 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom9 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom9 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试