React试用

最近公司需要弄一个统计分析系统来收集和展示产品中的一些关键数据。因为数据采集和聚合分析部分我之前已经做好了并且提供了REST接口,实际上剩下的工作只需要做一个web前端来方便使用即可。 如果仅仅从快速撸一个能用的前端出发,那么我可能会沿用之前的后端模版动态渲染+bootstrap前端布局+jquery等js库操作前端交互的方法。但这种方法的问题在于后端已经有现成的数据接口了,没有必要改动后端服务从而污染API。 那么自然而然的就考虑完全抛弃后端动态渲染仅用AJAX调用来获取数据来分离前后端功能,这样能完全不改动后端代码的情况下完成这个服务。于是问题收纳为用bootstrap和jquery做一个单页应用。 但是这种传统的方法存在另外一些问题。

  1. 页面或者组件的模块化程度很低,难以复用。
  2. 视图和控制部分分离,css控制式样,js控制逻辑,业务复杂以后代码难以扩展和维护。

于是自然想去寻找一些新的前端解决方案。事实上,经过这几年来移动互联网行业的发展,前端的工程化探索已经取得了一些显著的成果了,各种前端MVC,MVVM等前端框架层出不穷,新的设计思路如雨后春笋。 经过基本的探索,我主要考察了angular.js,backbone.js,react和vue.js这几种相对主流和热门的前端技术。 作为一名非专业的前端开发者,我无法深入的研究每一种流行框架或技术并总结出最佳实践,比较优劣,只能根据有限的前端开发经验和项目需求来选择适合当前环境的工具,框架的对比网上已有很多文章,在此不再赘述。 最后选择了react作为新的尝试来完成手头的工作。本文便是对react的一些实践总结。

首先react的定位是一个view层,这点与backbone,angular这样的前端mvvm框架有很大的区别。简单来说react制定了一套只用javascript来构建视图组件的规则,利用这个规则你可以写出模块化程度高,耦合度低,复用性好的前端组件。 为了理解react的设计目标,我们首先要理解三个react的概念。component,jsx和virtual DOM。

react的component的定位是一个具有完备功能的视图对象。首先它定义了视图的布局和式样(通过内联css式样,当然外部css式样也可以,但我认为这与react的设计理念不符),然后非常重要的一点,component有状态。react认为component是一个状态机。component在初次渲染时会有一个初态,react用props来描述component的初态。props是只读的,只能被初始化一次。用state来描述组件的状态。compoment接受输入转化状态state,state的改变会触发react对组件进行更新从而展现出不同的内容。因此按照react理念创建的component具有高度的模块化特性,然后通过不同的component搭积木一般构筑起复杂的应用。

然后说jsx,这是react对于标准javascript语法的拓展,可以在javascript代码中直接的(literally)插入html代码片段。jsx的编译器会自动将jsx中的html代码转换为react的视图对象。这个和后端模版例如python的jinja,以及php这样语言层面身就兼容html语法的语言本质上并没有什么区别。用户完全可以不依赖jsx,而用react提供的原生(vanilla)的javascript API来创建这些视图对象,jsx语法无非提供了更加亲民的书写便利。对于jsx,我一开始是排斥和拒绝的,因为我一般是不太喜欢框架或技术原创自己的语言,主要是怕该技术发展不好掉坑里去。不过在使用了一段时间后我发现,首先jsx确实能极大的提高书写效率,减少冗余的键盘输入,让代码更加清晰。更重要的是,这种js的扩展几乎没有任何学习成本,熟悉后端web模版的开发者几乎可以无缝接入使用。然后,在babel的帮助下,能方便的把jsx的代码转化为原生js代码,使得你的应用能兼容绝大部分浏览器。 综合这几点,我认为在实践中使用jsx是可行的。

virtual DOM则涉及到react的实现细节了。首先要澄清的是,react不鼓励操作DOM,或者说是直接的操作DOM。取而代之的是react实现了virtual DOM这个东西。顾名思义,就是一个虚拟DOM,用户利用react创建的所有视图对象并不是真正直接作用到DOM上。而是被react维护了一颗虚拟的树。react通过自己的算法,找出最优化最高效的真实DOM结构然后代替用户来完成渲染。 例如,当一个表格组件的数据发生变化时,react会比较前后状态的差异而增量的更新显示新的内容,删除旧的内容,而不是完全刷新。这种增量式的更新减少了浏览器的渲染时间,提高了用户体验。

说了这么一些React主要的优点,再总结一些缺点或者说存在的问题。

首当其冲的就是数据管理或者说数据流的解决方案,React并没有给出一个非常完整强大的机制,这集中表现在component之间的通信没有很好的解决办法。前文我们提到用props来设置初态,实践中通常父组件可以通过props来向子组件传递参数来控制子组件的初态,但是子组件想要方便的给父组件发送消息或者传递参数不太容易。常规的做法有两种,一是父组件向子组件传递一个回调,二就是通过全局对象来交换信息,不论那种方法都不能算是一个扩展性好,安全的做法,容易造成组件强耦合,不便维护。同理,两个不具有共同祖先的组件之间的通信也面临这个问题。当然,对于这个明显的缺点,facebook官方提供了一个叫做flux的数据管理方法,大概意思是提供一个centralized的store来管理状态,组件通过action来触发状态变动。然后状态变动进一步dispatch到目标组件完成数据的单向流动。后来又有人改进了这个思路,创造了叫redux的数据管理方法,引入了reducer。总而言之是引入了所谓函数式的思想来辅助管理复杂的数据流动。对于这一些亡羊补牢的工具,目前我没有深入实践,无法得到可观准确的评价。但是从直觉上,flux也好redux也罢,打破了react组件原本的OO-style的设计,使得所有组件在全局被魔术般的拧在了一起,这和使用全局对象通信并无二致。

另一个不算太大的问题就是如何集成现有的大量js库。React固然是提供了一个模块化view层的机制,但是前端开发中有海量的常用js库,ui库并不是按照react的风格设计,这些库或多或少都有直接操作DOM等违背react理念的行为。 因此随着业务需求的扩展,可能时不时会如何完美引入第三方库的问题。当然,npm上已经有很多常用库的React封装可以拿来就用,有些简单的自己封装也不难,更进一步使用material-ui这种完全针对react开发的ui库自然更加舒服。 React作为一个view层是很简单的,但是要用他开发一个具有完整功能的应用,你就发现需要每个环节都是react-style才能维持这种整洁和秩序,不然就会孕育出杂糅难看的奇美拉。一个新工具好用与否不单是建立在创新的设计和优雅的解决问题的能力,更重要的是能否简单的继承已有的劳动成果,站在巨人的肩上,让自己飞的更高。这一点目前只能依赖社区的力量来提供更多更完善的react-binding吧。

links