Reactant: 一个渐进式React框架

2,854 阅读7分钟

动机

React是一个用于构建UI的JavaScript库,但当我们要基于React开发应用时,往往要做很多的构建配置和很多其他库的选型(挑选和学习一个React状态库和路由器库等)。我们还需要考虑我们的业务逻辑应该如何抽象和结构化。每个React开发者都在实践着自己对React构建的认知,但这并不能让我们快速关注业务逻辑本身。随着应用业务规模的扩大,我们迫切需要一个易于理解和维护的框架。

而对于应用的业务逻辑结构化设计,关注点分离是个好主意。它要求明确责任边界,避免UI逻辑和业务逻辑混杂时可维护性低。我们在构建应用时,总是要关注业务逻辑。它是一个应用的业务核心价值之一。我们希望它易于维护、测试。Redux仍然是React中最流行的状态库。它完全符合React不可改变的原则。Redux只是一个状态容器,而我们往往不知道如何真正管理这些状态。我们需要一个可扩展、松散耦合、易维护的React应用框架。

React是一个优秀的UI库,但就算React有了hooks,它依然不足以解决我们在开发大型应用中的种种问题。我们依然没有模块依赖注入,我们没有很好的AOP实践模型,我们没有达到很好最小化模块系统抽象的可能,我们也无法更好的实践DDD,等等。这些问题都是React之外,我们需要思考和解决的。

当然,这里我并不是要讨论React是否需要加入这些特性,它已经足够优秀了。这里真正要讨论的是:我们是否需要一个React框架呢?

为了解决这些架构问题,Reactant应运而生 ——— 它是一个React框架。

Reactant简介

Reactant能高效地构建可扩展、可维护的React应用。Reactant基于TypeScript,但它同时支持TypeScript和JavaScript(为了更好的开发体验,推荐使用TypeScript)。Reactant提供了依赖注入、模块化模型、不可变状态管理、模块动态化等功能。它是可插件化、高度可测试的。它不仅能够快速构建React应用(Web和Native Mobile),还带来了一些新的React开发体验。Reactant也保持恰当的灵活性。使用了Reactant,你依然可以拥抱OOP,FP,FRP等编程范式,你依然能够拥抱整个React生态。

Reactant受到Angular不少优秀特性的启发,例如Reactant提供和Angular相类似的依赖注入的API。但Reactant并不是Angular编程思想在React框架上的一次复制,Reactant提供了更少更简洁的API,它足够应付各种开发应用的编程场景。

它是对React的一次完整架构。

解决了什么问题

Reactant是一个渐进式的框架。在开发应用从简单到复杂的过程中,它都能在每个阶段提供适合的feature,基于它的系统架构设计也能进行各种渐进式的平滑升级与演进。

更好的immutable状态管理

React更倡导的immutable状态类型管理,Redux显然符合它。但事实上,类似MobX这样简洁的突变的更新操作越来越符合当下流行的趋势。因此Reactant基于Redux和Immer提供了新的不可变状态管理模型,它融合了MobX相似的API元素,更重要的是它依然保持了状态的不可变性。

@injectable()
class TodoList {
  @state
  list: Todo[] = [];

  @action
  addTodo(text: string) {
    this.list.push({ text, completed: false });
  }
}

模块化

虽然React推出了Hooks后,似乎整个React社区都越来越推崇函数式编程,但是在复杂的企业级业务中,函数式编程未必上最佳的解决方法。当然,如果只在构建UI上,Hooks确实带来很好的渲染逻辑解耦的解决方案。但在业务逻辑领域,我们有更好的选择,尤其在一个企业级应用中的多人协作开发,事实上基于class的模块设计常常带来并行开发和易于维护与测试带来。类并不是有害的,错误的模块设计才是有害的。

因此在Reactant提倡使用class进行模块实现,更重要的是Reactant定义了Service Module, View Module,Plugin Module,使它们更明确职责与边界。任何一个模块都可以是Service Module,它是灵活的,许多不同应用的架构都可以基于它进行;View Module必须定义当前Module绑定的视图组件,它是视图模块的渲染入口,它依赖的模块状态都将通过useConnector进行直观地Props注入;Plugin Module是一个完整的Redux的中间件以及Context的再封装,它提供了一个设计插件的模型,它让插件API简洁无比变得可能。

此外,Reactant提供完整的依赖注入API。基于TypeScript装饰器的metadata来实现的DI,使得使用它变得特别简单。

@injectable()
class AppView extends ViewModule {
  constructor(public counter: Counter) {
    super();
  }

  component() {
    const count = useConnector(() => this.counter.count);
    return (
      <button
      	type="button"
      	onClick={() => this.counter.increase()}>
        {count}
      </button>
    );
  }
}

简洁和轻量化

Reactant的API不超过30个,核心API更是少于15个。无须过多的熟悉和适应,你就能快速上手Reactant,并用它来开发任何复杂的React应用。

在运行时,Reactant的核心代码gzip压缩小于50KB。Reactant不仅支持代码拆分,它也支持模块动态注入,这非常有利于许多大型应用程序最小化运行。

拥抱React生态

Reactant是开放的,它基于React和Redux的Reactant抽象了一些模型。这些API带给开发者便利的同时,它也支持了React和Redux的生态系统。很多优秀的第三方库能直接在Reactant上使用或者进行再封装,这给Reactant应用带来无限可能。

更好的开发体验

Reactant提供了更简洁的路由模块(reactant-router)和持久化模块(reactant-storage)。如果有必要,你可以基于Reactant插件模块开发出任何你需要更好的模块API。

在开发调试中, devOptions支持autoFreeze和reduxDevTools两个选项。当autoFreeze启用时,任何不在@action装饰过的函数中改变状态的操作都将报错;当reduxDevTools启用时,Reactant将激活对Redux的DevTools的支持。

Reactant还很新,更多的提升开发体验的特性还在进行中。

性能

Reactant和MobX+React的基准性能测试中,Reactant在启动时间和衍生计算上具有优势,而在值的更新上MobX+React更具有优势,总体而言,两者性能差别没有特别明显。因为Reactant基于Immer,因此当遇到极少数极端的性能瓶颈,Reactant也提供的性能优化的解决方案。

Reactant致力于保持良好性能的情况下,持续构建一个具有生产力的React框架。

结论

Reactant的初衷是希望能够帮助React开发者们能进行高效地构建和开发一个可维护、可测试的应用。Reactant的目标就是最小化系统的寿命成本并最大化开发者的生产力。

作为一个全新的框架,Reactant才开发了几个月,还有很多工作需要完成,包括构建工具集,开发工具集,SSR,以及React Native CLI,等等。

如果你已经熟悉React,那么你只需要快速阅读Reactant部分文档后并使用Reactant CLI快速构建Reactant应用,你就可以开启你全新的React应用开发体验。

Repo:

github.com/unadlib/rea…