最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Redux 之殇

    正文概述 掘金(nanchenk)   2020-12-14   350

    大型应用的场景

    本文旨在分享一些对于状态管理的思考与抉择,在展开之前,我们先来定义一下何为“大型应用”。

    以笔者本身为例,日常的工作在维护公司内部的一款 BI 分析平台,从前期的数据源筛选,数据加工,到分析过程中的拖拽式配置与可视化的多端展示,最后提供告警,指标下钻等辅助决策的能力。

    而在分析过程中,会有大量交互的细节,伴随而生很多需要考虑的问题,如隔离,性能等很多应用会遇到的问题。但是只凭借这些,很难说明一个应用的规模。

    通过总结,发现一个“大型应用”会有以下的特点:

    1. 强专业性: 拥有鲜明的问题域,该问题域往往存在已久,最优解通常由现实中的一门学科或者业务专家常年的积累知识所支撑,像笔者的项目就属于商业分析的问题域,不同于普通业务的 CRUD,功能与使用需要一定专业领域的知识。
    2. 复杂度高:这里的复杂度一般体现在 3 个方面。

    1. View: 单页面中会出现大量的视图组件,各种组件间会产生正交,组合。

    2. State: 一个动作的完成,伴随着很多的琐碎且零散的中间状态。

    3. Control: 单位面积内的功能繁多,且逻辑的链路长,完整的功能需要多个模块协同完成。

    3. 量级高: 代码行数多的可能不是大型应用,但大型应用一定有一定规模的代码量级。并且需要多名开发人员,持续迭代开发。

    那么这种应用在状态管理上会有什么挑战呢?

    问题&挑战

    我负责的项目发展至今已经具备一定的规模,历时 4 年,代码量级由初始的 10w 级别增加到了 40w 级别,最多有 15 个前端开发同时进行迭代。

    在持续满足需求的过程中,我们收到了 3 个维度的反馈。

    用户: 在使用的过程中,觉得功能稳定性越来越差,发布容易造成功能碰伤。

    产品: 需求工期逐渐变长,支持的成本也越来越大。

    开发: 项目的历史包袱沉重,想要扩展功能很艰难。

    Redux 之殇

    这里虽然是该项目的反馈,但是从笔者经历过的项目来看,这是一个大型项目在长期迭代中的必然结果。因为一个产品一般情况下会经历 3 个阶段。

    在0-1阶段,快速交付上线是第一优先级,这时需要快速试错,抢占市场 ,合理性在时效性面前需要做出妥协。

    而在 1-10 阶段,产品已经有了一定的规模,会涌入大量的需求方提出各种各样的场景输入,在持续迭代的同时,需要打磨使用细节与功能的稳定性来留住用户。也是在这个阶段,需要为前期的合理性买单了,快速迭代产生的各种不合理会通过用户的场景,放大反噬在后续的迭代中。

    10-100阶段已经涉及到产品价值的传递了,不在本文的讨论中。

    Redux 之殇

    我们来分析一下。这些反噬通常是什么形式,同样以这款 BI 应用为例。

    在代码层面,发现了 2 类主要问题。

    1.模块耦合过高

    一个模块的设计最初可能是边界清晰且功能内聚的,但是随着在多人协作场景下的不断迭代,往往主动或被动地与其他模块产生了耦合。导致系统的稳定性与维护性的降低。

    2.架构职责出现偏差

    视图层的组件承担了业务逻辑,而状态层没有约束与范式,管理起来毫无章法,导致复用性的降低。

    如果仅仅是解决这两问题,一到两个经验丰富的工程师花个几个月时间进行治理重构足以解决,我们需要探究是什么原因导致了这些必然现象。

    是代码坏味道不够熟悉?亦或是设计模式讲得不够清晰?还是说现有的框架不够牛逼?

    我觉得都不是,这些客观存在的概念和框架都有很强的合理性,他们教给开发者如何去写“好”的代码,怎么去组装你的逻辑,但是却没有正视开发者本身的能力差异,即多人协作这个命题,怎么让开发者不能做什么?

    Redux 之殇

    每个开发者在面对高复杂度时,对应的解法不尽相同,解法的优劣虽然不是致命伤,但是多人协作时,不同地场景输入和考虑,会对设计思路的要求很高,很容易出现局部最优,全局格格不入的场景。

    从而,系统的“熵”增失控,复杂度得不到收敛,开始朝着腐烂的方向发展。

    这时可能需要一些约定来进行约束,毕竟基于约定总是好的,但约定也总是脆弱的,我希望从框架的层面寻求解决之道。需要抹平开发者差异对整体结果的影响,怎么让后人可以精准地做填空题,在合适的地方干合适的事

    在众多框架中,我们需要明确诉求,我将核心诉求归纳为如下 4 个方面

    1. 高效: 可以简单高效地快速支持业务需求

    2. 正式多人协作为的问题: 提供开发范式与强约束

    3. 复杂度: 可以收敛应用迭代过程中的复杂度

    4. 可维护: 面对人员流动,除了文档,希望有方法来保证项目的可维护性

    主流框架的局限性

    目前社区中已经存在非常多的优秀框架,选择时需要明确自己的项目需要什么?在我们的业务中,有以下强诉求:

    1. 报表信息的持久化

    2. 需要多版本信息

    3. 设备的兼容性

    4. 多人协作的高效性

    5. 复杂逻辑的上手门槛

    考虑到 redux 本身设计的一些缺点,如模板代码等,Dva 或 rematch 之类的二次封装的库可能更适合于此。

    以 Dva 为例,首先要承认他是一个非常优秀的状态管理的框架,并且提供的插件形式,可以暴露相当大的灵活性给开发者,同时消除了原生 redux 带来的模板问题,并且数据流非常清晰与清爽。

    用户交互行为或者浏览器行为(如路由跳转等)通过 dispatch 发起一个 action,如果是同步行为会直接通过 Reducers 改变 State ,如果是异步行为(副作用)会先触发 Effects 然后流向 Reducers 最终改变 State,所以在 dva 中,数据流向非常清晰简明,并且思路基本跟开源社区保持一致(也是来自于开源社区)。

    Redux 之殇

    如果是通用的应用场景,无脑 all in 就对了,你会爱上他的。

    但是,记得上文提到的约束吗?

    dva 解决了诉求中的【简单高效】,并且做的相当不错,但是,仅仅着手于提效是无法覆盖复杂应用的痛点的。

    需要框架层面来正视【**复杂度收敛】,【可维护】和 【多人协作】**这些大型应用中的致命伤。

    在 dva 中你可以 dispatch action 到任意的 model 中,也可以随意地访问全局状态。这种灵活性在大型应用中是一把双刃剑,虽然可以快速搭建页面,但是却导致模块之间的边界越来越模糊,耦合性逐渐上升,长期看来,是弊大于利的。

    并且仍然将所有状态聚合到 store 中,随着业务的不断迭代,这棵树可能过于丰满,承载了不该承受之重~,在随着业务的迭代碰伤,脆弱这些特点往往也会在这个时候冒出来。

    对于大型应用而言,多人协作是比提效更为重要的,毕竟这是一场马拉松而不是百米赛跑。

    Redux 之殇

    基于此,我们自研了 redux-with-domain。

    Redux-with-domain

    Redux 之殇

    redux-with-domain 是在解决大型应用中沉淀出的方法论。

    通过拆解和限制两个核心理念来收敛大型应用的复杂度。

    模块化

    模块化的概念与 dva 无异,出发点都是为了解决 redux 带来的冗余代码问题,使用 redux-with-domain 的 module(不是 model)概念,可以将一个模块的相关功能内聚,消除样板代码,提升开发效率。

    领域驱动

    领域驱动是为了提升架构的稳定性,在展开之前,先介绍一下概念:

    领域:即应用软件的问题域,像笔者的项目就属于商业分析的问题域,问题域一旦确定,边界与最优解随之确定。

    领域知识: 即业务流程与规则。

    领域模型: 则是对领域知识的严格组织和选择性的抽象。

    Redux 之殇

    在传统的前端开发流程中,前端和业务之间是通过视觉稿来联系的,一个需求对应一个视觉稿,视觉稿的变更即代表了需求的变更,随之带来大量的修改成本,像组件UI,业务逻辑和状态管理等,在这种模式下存在 2 个问题:

    1. 视觉稿与真实的业务诉求存在偏差,开发者没法理解真实的业务需求,很难触及用户的真实痛点
    2. 见山便是山的开发方式,很难沉淀出稳定的功能结构,容易产生历史包袱,从而复杂度不易收敛

    Redux 之殇

    而领域驱动设计的开发流程,会先由业务专家和开发团队根据领域知识与业务期望从问题域中抽象出稳定的领域模型,因为领域模型是现实世界中业务的映射,所以天然与UI无关。

    有了稳定的模型,再去搭建上层的业务逻辑,不易受到需求的冲击。

    而这时,组件层只需实现视觉稿和组装业务逻辑,可以有很高的灵活性。并且因为数据层与逻辑层的稳定,多端适配和数据同步的问题也很容易得到解决。

    状态分层

    Redux 之殇

    状态分层是想通过细化分层的手段来拆解复杂度

    主流框架下,所有的数据状态和业务逻辑集中在一起,随着迭代的增加,状态数和逻辑逐渐膨胀,复杂度无法得到收敛。

    而在 redux-with-domain 中,状态层由 UI 层和 domain 层组成,其中 UI 层中的 page 和 contianer 分别对应页面视图与组件视图,同时可以各自调度 domain 层去维护业务数据的状态管理。

    调度约束

    讲完了拆解,看看怎么做限制。

    限制的出发点是为了降低耦合,方案是禁止了模块之间的直接调度,为什么执着于直接调度呢?

    Redux 之殇

    因为模块的依赖是应用复杂度的根源,如果不加以限制,在多人协作的环境下,模块本身的边界会模糊,功能不再内聚,形成泥团效应,危害项目的可维护性。

    那么做法分为两步:

    1. 限制了状态的访问,仅使用模块提供的接口去访问模块的状态,以 dva 为例,主流的框架都开放了全局的状态访问,但是无形之中会增加隐式的依赖,而在 redux-with-domain 中只能通过 selector 去访问局部状态,可以约束这种依赖。
    2. 禁止同级模块之间直接 dispatch,并选择了依赖注入 + 中介者的方式提供调度机制,由 page 去监听和调度下层 container 的action 逻辑

    Redux 之殇

    基于上述的拆解和约束,我们的架构图如图所示:

    状态层由 UI 层和 domain 层组成,其中 UI 层中的 page 和 contianer 分别对应页面视图与组件视图,同时可以各自调度 domain 层去维护业务数据的状态管理

    而 page 与 contianer 之间限制了同层调度,统一使用依赖注入 + 中介者的方式进行逻辑调用。

    通过这样,来最大程度的解决大型应用的复杂度问题。

    工程体系

    为了开发的提效,我们也搭建了以 redux-with-domain 为核心的工程体系来全方位的解决前端的挑战。

    在 redux-with-domain 的基础上,拥抱 TypeScript 与 Hooks,并且利用 ts 的自动推导开发了一键切换的能力,让既存项目可以很方便地切换至 ts,享受ts带来的约束与补全能力。

    然后开发了脚手架工具与代码补全,去提升开发效率,同时也开发了 bigfish 的对应插件,让使用 bigfish 体系的项目也可以享受 redux-with-domain 带来的好处,最后,准备了详细的文档与 example,降低了 redux-with-domain 的上手门槛。

    对比

    Redux 之殇

    在模块调度方面,模块调度由之前的网状依赖,变为分层管理,模块间的耦合性降低,稳定性得到了增强。

    Redux 之殇

    在数据流方面,原先的视图层直连状态层造成的复用性低,变为page和contianer链接状态层,components 层仅做展示,整层得到复用,研发效率得到提升。

    总结

    本文从探索大型应用的问题中,梳理出核心的问题——多人协作。并讨论了主流框架中的定位都是在提效方面,并没有提供复杂度的拆解理念,从而通过自研的状态管理框架 redux-with-domain 来具化了,沉淀出的方法论。希望可以在各位看官面对相同场景时提供一些灵感。

    附录

    1. redux-with-domain
    2. join us

    起源地下载网 » Redux 之殇

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元