学习前端框架时,我们总要接触到一些关于状态管理的知识,但是,什么是状态?你为什么需要状态管理?我将以编写一个新版 SSPanel 前端页面的时候遇到的问题作为案例,为你详细分析。

可以让你舒适阅读本文章的前置条件

  • 了解常见的 MVC 前端框架,如 React, Vue 等。

首先,什么是状态?

让我们先来做个实验。

打开 CodeSandbox, 新建一个 React 沙盒, 写一个非常非常基本的组件。

这个 React 组件里面有一个数据 data ,我们让组件中的 H1 标签引用了它。并且希望这个数据在每一次按钮单击的时候,这个 H1 标签里面的数字可以自动的变化。但是我们无论怎么点击,这个标签里面的数据也都没有任何的变化。打开这个沙盒的 Console 却发现每一次点击时,这些数据的确是在发生变化的。

现在,我们将这个组件稍微进行一下改写,用一个 React Hooks 将它变为 State(状态) 看看。

OHHHHHHHHHH, H1 标签已经可以按照数据的变化自动变化了!这个将数据反应在用户界面上的过程,就叫渲染,当数据发生变动时,网页的显示也会跟着一起变化,这就叫重渲染。

可是,为什么相同的 data 变量,使用不同的写法,产生的效果会截然不同呢?

你可以这么理解:React、Vue 等视图层前端框架只关注那些已经被声明成需要特别监视的变量,只有这些变量发生变动时,才会触发组件的重渲染。这些通过特殊方法声明的变量,就叫做 State(状态)。比如说上面例子中的 data 就是使用了 React 中的一个 Hook 函数 useState() 声明了这个变量需要进行监视,那么 data 就是一个 State 。在检测到 data State 变化时,React 就会重新渲染这个组件。而在 Vue 中,这些 State 则是放到了 data() 函数里。

前端框架中的 State 一词,其实起源于编译原理中的状态机(State Machine)。如果你学过数字电路的时序逻辑电路的话,是不是能回忆起来一点了呢?

何时需要状态管理?

当网站 API 数据十分集中、多个组件需要共享相同的 State,且它们之间相互依赖时,你应该考虑使用状态管理工具。

使用前端框架构建的网站由许多的组件组成,这些组件可能会在自己内部保留有许多的 State。但是这些内部的 State 其它组件是无法访问的。当一个组件需要访问其它组件的少量 State 时,你可以使用 Vue 中的 Provide/Inject 和 React 中的 Context。随着组件数量的增多,各个组件内部维护的 State 也将变多。这个时候如果继续使用上述方法来访问 State,代码将变得难以维护。最好的解决办法就是将这些 State 全部抽离到一个统一的状态存储(Store)中统一管理,组件本身不持有任何的状态,获取数据的唯一方式就是从 Store 中获取。如果组件想要修改状态,必须通过 Store 内置的方法(mutation/reducer)执行。这样,网站里的大部分组件就符合了单一数据流原则。

有没有更好的例子?

当然有。我们以 SSPanel 提供的 API 设计一个用户签到按钮为例。

首先,我们要判断用户能不能签到。SSPanel 提供了一个 API /getuserinfo 用来获取用户的基本信息,返回的 info.user.isAbleToCheckin 字段,可以判断用户是否可以进行签到。如果可以签到,我们可以发出一个请求 /user/checkin 进行签到操作,完成后,将这个按钮状态修改为不可点击即可。

但是这个 API 获取到的用户信息非常多,其它组件也要使用这个组件获取数据,我们不可能让每一个组件都单独发起一次请求来获取数据。这个时候,我们可以将 API 返回的用户信息全部放到 Store 里,所有的组件通过统一访问这个 Store 进行数据的获取。签到按钮的 disable 属性是否激活自然可以通过访问 Store 中的相应字段进行判断。如果按钮的 disable 属性没有激活,签到按钮启用,用户点击发起签到请求,然后 Store 再次访问一次 /getuserinfo,这个时候 info.user.isAbleToCheckin 值自然为 falsedisable 属性生效,按钮禁用。这样就保证了在其它组件可以获取数据的同时,不干扰这个签到按钮的正常交互。

你说了这么多,不就是个全局变量吗?

啊,好像是的。但是我们之前说过,前端框架只会监视 State/Props 的更改,因此想直接通过抽离成全局变量的方式进行状态管理,那还是不行的。

我应该使用什么状态管理框架?

对于 React 应用来说,主流的状态管理主要有两种:以 Mobx、原生 React Hooks 为代表的直接式,以 Redux 为代表的严格式。

对于小型项目,可以采用原生 React Hooks 或者 Mobx 一类的简单式状态管理框架,它们是一种无痛易上手的状态管理方案。React Hooks 无需额外的学习成本,而 Mobx 只要你会写 Javascript Class,就可以非常轻松的为组件接入状态管理。

而对于大型项目,则可以考虑使用 Redux 这种严格遵循 Flux 原则的状态管理工具,这会让程序更具条理性,但是上手成本会比较高,比如异步操作时要使用到的 Middleware

Vue?那没什么好说的,Vuex 一把梭吧(x

总结

现代的前端框架中,状态管理是大家永远逃不开的一个问题。通过这篇文章,我们也得出了状态管理的本质——像数据库一样集中管理从 API 中获取到的数据,让组件更加方便地进行维护。但是别急着为你的网站添加状态管理,如果你的网站组件内部的状态没有严重到需要相互依赖的“蜘蛛网”形状,那么还是先维持现状吧。正如 Redux 的开发者所说:

Don’t use Redux just because someone said you should - take some time to understand the potential benefits and tradeoffs of using it.

不要仅仅只是因为别人告诉你应该用 Redux 就强上 Redux —— 花点时间了解一下利弊再做定夺。