Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2021年再次探讨前端状态管理 #106

Open
chunpu opened this issue Oct 15, 2021 · 0 comments
Open

2021年再次探讨前端状态管理 #106

chunpu opened this issue Oct 15, 2021 · 0 comments

Comments

@chunpu
Copy link
Owner

chunpu commented Oct 15, 2021

我们为什么需要状态管理?

  1. 数据共享
  2. 模块化
  3. 数据更新
  4. 单向数据流, 单一来源
  5. 视图响应式
  6. watch
  7. computed

我们看看哪些特性是必需的, 缺它不可

  1. 数据共享是必需品, props 和 $emit 功能只能解决父子组件的数据共享, 数据更新问题, 但实际业务中任何非父子组件都有可能需要数据共享
  2. 模块化是中型, 大型业务程序的必需品
  3. 单向数据流, 单一来源, 一般通过 flux 架构来解决, 代价是不能直接改 state, 会让复杂度上升, 只有少数时序敏感的复杂程序才需要这种特性
  4. 数据更新, 必需品, 数据共享当然不只是读, 也要能写
  5. 视图响应式, 必需品, 修改数据应该能改变视图, 声明式UI框架的核心功能
  6. watch, 必需品, 响应系统关键特性之一
  7. computed, 必需品, 响应系统关键特性之一

因此这里面只有单向数据流不是必需品, 状态管理并不等于数据共享和flux架构的组合, 应该根据业务各取所需

简单 store 模式

Vue 官方文档中的一段话:

如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了

Vue 官方提供了简单状态管理起步使用官方文档, https://cn.vuejs.org/v2/guide/state-management.html, 此文档很容易让人以为是 Vuex 的原理科普, 但其实这就是一种剥离 Flux 架构的状态管理的常见方式

简单 store 模式支持数据共享, 视图响应式, 更新状态, watch, computed, 天然支持模块化全部必需特性

优点是可以直接像操作变量一样修改状态, 指哪打哪

Vue通用的单组件文件开发方式让很多人以为 data 和组件是一一对应关系, 或者说data就属于组件

事实上 data 和 视图完全可以一对多, 一对多, 一对多, 重要的事情说三遍, 这是很多人忽略的点, 一对多的data依然保持响应式

此方法模块化非常容易, 一个抽象业务对应一个文件, 可以很容易的把业务剥离, 但仍然可以直接操作状态

此方法也是 MobX 推崇且默认的方法, 渲染代码引用业务 store, 而不是写在同一个文件, 感官上就是分离的

count.js

export default {
  state: Vue.observable({
    count: 0
  }),
  increment() {
    this.state.count++
  },
  decrement(state) {
    this.state.count--
  }
}

ComponentA.vue

import count from '@/count'

export default {
  data() {
    return {
      count: count.state
    }
  },
  methods: {
    handleClick() {
      count.increment()
    }
  }
}

使用 $root 共享状态

支持数据共享, 视图响应式, 更新状态, watch, computed 几乎全部必需特性, 需要自行模块化

和简单共享 store 几乎一样, 但使用成本更低, 是最简单的状态共享方案

Vue 官方推荐这种模式仅在小型应用中使用

// main.js
new Vue({
  data: {
    user: null
  }
})

// Component.vue
export default {
  watch: {
    '$root.user'() {}
  }
}

官方文档: https://cn.vuejs.org/v2/guide/components-edge-cases.html

和Vuex一样的单一状态树, 模块化, 可以自己写一些辅助函数便于访问和更新状态

export default {
  moduleA: {},
  moduleB: {}
}

使用 provider / inject

只可以读, 无法写, 更新状态会直接有警告, provider端改状态, inject端也接收不到

写起来需要两个属性兼顾, 有点繁琐, react 也有这种模式

此模式仅适用于公共方法 method, 而不适用状态数据

https://cn.vuejs.org/v2/api/#provide-inject

使用 Vuex

支持响应式, watch, computed, 模块化, 更新状态, 单向数据流

不能直接修改状态, 需要严格遵守 flux 约定, 单项数据流

支持 time travel, 可以使用 devtool 看状态流动

主要有以下缺点

  1. 更新状态非常繁琐, 需要给每一种状态更新写一个 mutation, 异步操作要写 mutation + action
  2. 套了一层 commit 和 mutation 后代码不直观
  3. Vuex 是单一状态树, 所有状态会集中在一起变成全局, 无法按需引用, 需要用命名空间和辅助函数

Vuex 继承了 flux 的信条, (state, action) => newState, 截至 Vuex 4.0, 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation, 异步函数更麻烦, 大部分业务并不适合, 杀鸡用牛刀

MobX 默认是可以直接修改 state, 只有开启严格模式才是强制 action 修改 state, 也就是说 Vuex 比 MobX 更严格

Vue 官方的意思是复杂程序适合用 vuex, 但也不完全对

明确适合 flux 架构的有编辑器类app, 协同类app等有明确时序概念的复杂程序, 用户每一步操作是一个 action, 并且可以撤销, 有反向操作, 动作录制等功能, 这样调试起来也很清晰

总结

  1. 中型大型业务使用 简单 store 模式 / MobX 管理状态
  2. 小型业务使用 $root模式 管理状态
  3. 复杂时序程序使用 Vuex / MobX严格模式 / Redux 管理状态
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant