How About a Nice Cup of React

接触 React 一年左右,期间在单页面、单页应用都有使用, 它极大地改变了我个人的前端开发方式。为方便快速出产品,闲暇之余也尝试写了点基础组件: react-component, react-image-cropperreact-touch-gallery, react-file-upload。也一直计划着写点心得总结什么的,迫于忙、浮躁,难静下心写东西,屡次作罢。这两天刚离职,决定把这篇总结补上。

在 React 之前,玩过一点点 Angular 跟 Backbone,Backbone 是比较轻量级 MVC,Angular 较重,它的指令、注入式写法, 较死板的规则,不够简洁的 API,诸多概念等等,最终只是被我当玩具折腾了下。由于接触到项目多数时间花在 view 的交互、事件、变化更新上,所以大多数情况还是选择熟悉的 jQuery,原因无非是:熟练,可控,最轻依赖,快速灵活。过程基本上是: 命名 DOM,给 DOM 加特效,取数据更新 DOM, 绑定事件到 DOM 等等。时间一久,问题就很明显了:即使多次尝试做不同层次的抽象分层,还是很乱,太频繁与 DOM 打交道,遇到问题不好排查,维护也很痛苦。如果能尽量少碰 DOM,以数据、逻辑导向来写 view 呢?

Github 上有 xx 步从 jQuery 到 Backbone, 就是通过一步步迭代,解释 Backbone 如何抽象分层,将数据、逻辑、事件、view 操作做分离,使得层级清晰,逻辑分明。可如果只为解决 view 层问题的话,是没必要引入过多概念和规则的,且 view 本身的问题,例如高复用性等也没有被解决。

React 带着变革前端开发的使命而来,声称专注 view 层,特点是组件式开发,有着简洁合理的 API,不错性能等优势。

说到组件式,大概包括了外部属性跟内部状态。比如电视盒子,对屏幕来说,只需要提供几种插口;内部构造对外是透明的,根据插口接入的不同,呈现截然不同的输出,屏幕也无需知道盒子内部的运作方式。实际上用 jQuery 时,大家也多多少少封装过一些『组件』,供外部使用,比如购物车 Counter,自定义播放器等,但并不会像 React 那样通通都是组件。在 React 里,组件包含 props(外部属性)跟 state(内部状态), 具体细节可以去看 React 官网。

初次对组件式思维赞叹的是评论的加载更多功能。一般用 jQuery 的话,思路是请求数据,组装 list DOM ,append 上去; 换做组件式思路呢?答案是数据驱动。评论的数据应该属于评论内部状态,那么加载更多调整内部状态就好了(不断练习这种数据导向的思维,合理设计 props 跟 state,对于通常项目需求,写起来就非常快了,同时也基本避开了对 DOM 的操作),React 帮你把状态的更新高效的反映到 DOM 更新。

逐渐对组件式开发轻车熟路,反过来也不断巩固组件化开发思维,于是开始真正信服 React 的 Learn once, write anywhere.

组件真的那么好么?

组件堆叠组合的方式让高复用变得简单,然而在开发中还是遇到了些问题:

  1. 刚上手很快就遇到组件如何通信的问题,看了 React 文档才弄明白。
  2. 在写 Dropdown 时,改变外部默认选中值并不会导致 Dropdown 选中值变化。当时头脑不清晰,折腾了半天,这个问题归纳为:state 依赖于 props 的初始值(该变化后也需向内传递), 而 state 也通过自身内部维护,这个问题有很多使用场景,这里内部维护是指 Dropdown 选中来改变 state。那么如何让 props 改变再来触发 state 呢,看了 React 生命周期才知道有 componentWillReceiveProps 这样的方法,也不得不赞叹 React 生命周期设计的周全,这个问题在后来用 Ember.js 写组件时同样也遇到了,只是 Ember 实现稍有不同。
  3. 组件层级深,层层传递 props 到子组件很麻烦,React 提供了 context,使子组件方便的获取父组件链上下文 props。
  4. 组件的被动方法如何调用,例如点击按钮触发组件的操作?通过 refs 可以拿到组件实例,从而调用实例方法,而 findDOMNode 则能拿到 DOM 元素等等。不断有新问题涌出,问题也在一一被解决,React 之路进阶还在继续。

当然组件模式并不是只有 React 一家, Ember 2.0 弱化了 controller, 加入 component 的概念, Vue.js 也是组件的思路,甚至更轻量,简洁优雅。为什么选择 React ? 原因有几点:相比 Emberjs 这样的 MVC,React 轻量、概念少,简单清晰,高效的 DOM diff 算法保证了性能,只做 view 且做的足够好。Vue 是后起之秀,高性能,更简洁,十足野心要替代 React, 在前公司生产环境投入使用,遇到几个问题:

  1. 在 template 里用 computed 属性、method 甚至是 data 看起来傻傻分不清楚,component 里 data 跟 props 也差不多,协作的话需有团队约束。
  2. 看似比 JSX 片段清晰的 template,实际上却因语法导致引号、括号、字符串、方法调用等使用造成视觉混乱。
  3. 还是模板问题,指令必须依附 DOM,逻辑好不清晰(据说这点在2.0得到改善)。基本上都是关于 template,也不算大问题。

有人说经过很多年发展,终于模板跟 js 逻辑分离了,React 一下子又倒退回去了。说出这言论的一定没用 React,当你真正体验 React 的『合并』后或许才能体会到它的方便之处。如果你也曾经历模板逻辑分离的不同时期,回头想想,分离真的那么好么?逻辑部分还好,而模板呢功能太弱,要做到精确控制输出很费力,而且脱离逻辑谈复用性也并不靠谱,真的是我们想要的么?React 的 JSX 用类 XML 的语法糖,使得可以通过 JS 语法来方便精确控制输出逻辑,再加上组件作用域的光环也显得十分合理。

总结下吧,React 带来了前所未有的开发体验,也引领了组件式开发的热潮,对于开发者来说,优势很多,例如:

  1. learn once, write everywhere.
  2. 模板跟逻辑在一起,逻辑精确方便的控制输出。
  3. 组件式开发思维,让开发者专注数据、逻辑。
  4. 简洁清晰的 API,较少的概念,易上手。
  5. 周边生态逐渐完善,开发者也很容易做出贡献。
  6. 社区活跃,库本身在 Facebook 及很多大厂使用,能力得到充分验证。

问题、疑惑及待解决:

  1. 方法普遍太长(各种编辑器补全插件可以有效解决)
  2. 生命周期的不同阶段需要弄清楚,不然会遇到些奇怪的问题。
  3. 组件化必然导致状态的零散,如何集中管理状态,又如何通知更新所有组件状态,可以去研究下 flux 跟 redux,但各有缺点。
  4. 模板跟逻辑在一起,更容易写不好,乱,对人要求不算低。
  5. 组件样式编写问题。

注意: 本文主观色彩较严重,细节并不考究,若有问题欢迎指出。

好吧,更多的,以后想到了再扯吧。