Hao
Hi, I am Hao (👋): a coder, a woodworker, a blogger, and a father.
记录一个优雅的React框架解决方案
April 27, 2016

从14年开始接触React,已经一年有余,不仅在公司的大大小小项目上全面采用基于React的前端架构,也在自己的私人项目上大量采用。React不仅搭网站上效率感人,结合Electron后写全平台桌面应用也是十分方便,实乃居家旅行必备工具。但当年jQuery的地位似乎也是如此,在前端这个技术爆炸的行业,React未必会坚持很久。很快就会有更新的技术理念诞生。因此,一个小巧、可靠、可扩展的简单架构是应用开发的王道。开发者不必受限于大型架构的种种限制,而可根据实际情况灵活变化。React中的Flux架构已有多重实现,除了官方的Flux外,还有Fluxxor,Redux,Reflux等,无一不要引入大量概念和模块,使简单的模型也变得愈发笨重。我认为React本来并不是为了大型Web应用而准备,但却具备其潜力,并且以模块化的思想使开发过程变得异常简单。

我所使用的最后一个React架构,严格说来只是两个工具:react-cursor和async/await函数。并未形成具体的框架体系,也不必把整个应用引入各种奇奇怪怪的模块。

react-cursor是小型架构理念的代表性作品,作者并没有大包大揽地企图控制整个应用,而是仅仅提供了一个状态管理器,使开发者能够在任何模块里接触并操纵程序的状态。这个想法并不新鲜,事实上,任何熟悉React的人都可以把程序包裹在一个大的一般称作“App”的模块中,然后传递这个模块的状态到子模块,然后通过Pub/Sub方式来传回App中更新,从而达到了数据单向和单一状态的目的。react-cursor除了使这个过程变得简单清晰之外,对数据传递过程中的异步回调问题有了简单的解决方案。

举例来说,子模块需要通过http下载列表数据并显示。在Pub/Sub模式中,子模块只能操作一个状态,并且在状态改变后需要继续操作改变后的状态,因此程序的耦合变得之分紧密。

this.props.state.isLoading.set(true);
setTimeout(() => {
	this.props.state.data.set([1, 2, 3]);
}, 1000);

在此例中,子模块完成了两次对状态的操作,然而第二次操作中,状态中的isLoading属性依然为原来的false。第一次操作对第二次完全没有影响,这种情况下就产生了两个不同的状态,而程序也因此变得无法管理。

react-cursor模式下,代码如下所示:

cur.refine('isLoading').set(1);
setTimeout(() => {
	cur.refine('data').set([1, 2, 3]);
}, 1000);

区别不大,但是子模块操作完成以后,程序仍然是单一状态。即在Redux框架中,作者提出函数式编程,通过reduce函数对传递过来的状态加工,返回一个最终状态。而在react-cursor工具里,动作函数只是接受到状态的一个引用值,从而可以在不同的动作函数里对单一状态进行更新。

这样的好处是,对于那些有很多异步请求的应用,对状态的更新变得非常简单,减少混乱。

第二个工具是ES7的async/await方法。虽然Promise的使用减少了回调的陷阱,但最大的问题在于长链中无法捕获错误。async和await让代码变得简洁。

WordMark的开发中主要是以以上两种工具搭建的桌面应用。尽管应用功能越来越多,代码越来越复杂,但始终保持着简洁、可维护以及有组织的高效状态。