复杂 Web 应用的状态管理思考

2017-03-31  本文已影响1776人  binggg_booker

目录

1.背景

Web 页面开发的应用化趋势

随着 Web 能力的不断提升,用户的 Web 页面体验的要求,要求像原生一样,无刷新、离线缓存等和原生应用的功能。


Coding WebIDE

Web 开发已经进入组件化时代

Web页面开发组件化

2.一个常见的多个组件共享状态的问题

在下图中播放器组件上点赞以后,如何同步更新其他地方有关这个视频的点赞信息,比如右侧信息区组件、详情页?

点赞以后如何更新点赞的状态

可能的解决方案

这些方案的问题

问题的根源:
不同视图上的相同数据没有一个统一的来源,如果点赞信息能统一存储,只需要更新同一个地方,并且都从这个地方获取信息显示到视图上,就不会有这个问题了

3.MVC 架构

MVC 模式(Model–view–controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。

MVC组件之间的典型合作

比如点赞信息作为一个 Model, 每个视图都可以读取这个 Model,然后视图通过通知 Controller 去更新 Model,视图通过监听 Model 的变化再进行同步。

mvc的数据流动

4.Flux 架构

Flux 是由 Facebook 提出的,用于组织应用的一种架构,它基于一个简单的原则:数据在应用中单向流动。这就是所谓的“单向数据流”
,简单的记法是把数据比作鲨鱼:鲨鱼只能向前游

Paste_Image.png flux架构设计

Flux 试图通过强制单向数据流来解决这个复杂度。在这种架构当中,Views 查询 Stores(而不是 Models),并且用户交互将会触发 Actions,Actions 则会被提交到一个集中的 Dispatcher 当中。当 Actions 被派发之后,Stores 将会随之更新自己并且通知 Views 进行修改。这些 Store 当中的修改会进一步促使 Views 查询新的数据。

5.MVC 与 Flux 的对比

EventBus 数量

视图同步数据方式

查询和写入数据的区别

数据改变的可感知性

6.Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
借鉴了 FluxRedux、和 The Elm Architecture。与其他模式不同的是,Vuex 是专门为 Vue.js 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。

多个组件共享状态的问题

由于 Flux 架构有多个 store,我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:

问题的解决

最终解决方案

因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!

另外,通过定义和隔离状态管理中的各种概念并强制遵守一定的规则,我们的代码将会变得更结构化且易维护。

这就是 Vuex 背后的基本思想。

7.使用 Vuex 的正确姿势

背景:前端数据来源的多样性

不同端的数据来源多样性

同一端数据来源的多样性

如果服务端的接口不是基于 restful 设计,或者接口内容是一些聚合的接口,比如服务端设计一个获取用户信息接口,返回了用户的 vip 信息、关注信息、粉丝信息等,同时设计了分别的获取 vip 信息接口、关注信息、粉丝信息接口,这样同一个内容在服务端这一端会有不同的来源。

如果前端组件之间对同一个状态的读取和操作没有做到复用,假设一个评论列表组件和弹幕组件之间,都用到了用户的评论信息,如果没有做到统一读取和更新,就会导致同样的状态信息有两个数据来源,最终导致同步问题。

数据读取,保证读取单一状态来源

需要保证不同的 View 或者组件读取某个状态信息都是从同一个地方读取,所以,回归到 Vuex 设计的本意上来,我们应该把同一状态信息集中管理。

数据存储,从聚合到原子

思路

前端数据存储原子化设计

下面是一个组队加速需求的状态设计:

{
  users: {
    [uid]: {
      userid: String,
      nickName: String,
      avatar: String,
      // 存储team的索引
      teams: String[]
    }
  },
  teams: {
    [groupId]: {
      teamId: String,
      taskId: String,
      // 存储user的索引
      users: String[]
    }
  },
  tasks: {
    [taskID]: {
      taskID: String,
      name: String,
      gcid: String,
      status: String,
      suffix: String,
      teams: String[]
    }
  },
  increases: {
    [gcid]: {
      [userId]: {
        uploadAmount: String,
        increaseAmount: String
      }
    }
  }
}

数据结构设计原则

8.有了 Vuex 还需要什么?

数据校验设计

Vuex 对 state 的写入没有统一的校验机制,复杂的单页面应用应保证 state 内存储的数据结构和类型都是一致的,可以在 Vuex 这一层做一层类似组件的 props 验证那样的验证机制。

数据缓存设计

需要一套完整的缓存机制,包括缓存的读、写及过期清理逻辑。

数据同步设计

数据同步频率控制设计

就像一窝蜂的人去排队看演出,队伍很乱,看门的老大爷每隔 1 秒,让进一个人,这个叫 throttle,如果来了这一窝蜂的人,老大爷一次演出只让进一个人,下次演出才让下一个人进,这个就叫 debounce

上一篇 下一篇

猜你喜欢

热点阅读