Skip to content

为什么我们还需要一个 React like 框架?

yuche edited this page Dec 5, 2017 · 5 revisions

Why React?

是的,我们又造了一个轮子。

但我们并不是无事找事,为了造轮子而造轮子。这个轮子的核心需求还是来源于业务线的需求。在京东前端之前大部分的业务都是老式的 jQuery 技术栈,而在这些年摸爬滚打并结合前端技术的发展,我们大概需要这么一个东西:

  1. 它能提供一个优良的模块化方案,能够无缝接入现代前端工程化方案,能够赋予我们应对复杂业务更好的抽象能力;
  2. 它能提供数据绑定,提供真正的事件驱动能力,能够自动地处理数据到视图或视图到数据的更新,让我们从繁琐的 DOM 操作中解脱出来;
  3. 它拥有非常好的性能,只要遵守特定的、简单的 Guide line 就能快速地、优雅地进行开发;
  4. 它好学、好用,拥有平滑的学习曲线,不管是刚毕业的新手小白还是多年经验的老司机在一两天的学习之后都能进行开发;
  5. 它拥有一个良好的社区和生态系统,能够持续地不断改进自身,大量社区投入生产环境能够有效降低风险;

很显然,React 完全能满足以上所有的需求。除此之外,React 的思维方式 (paradigm) 也是我们非常推崇的:声明式的 UI、可预测的状态、数据单向流动和函数式编程。但即便如此,React 仍然有一些不能满足我们需求的地方:

  1. IE8 浏览器兼容性。当前环境所限,即便很不情愿,我们仍然要支持 IE8;
  2. 体积。React 大概 130kb 的体积在低网速 / 低版本浏览器 / 低配置设备的加载速度和解析速度都不能让我们满意;
  3. 性能。React 的 virtual-dom 算法(React 自己叫 Reconciler)并没有做太多的优化,例如从一个长度为 n 的数组,把最后一个元素移动到第一位,实际上要进行 n - 1 次 DOM 操作。

而我们的新轮子 —— Nerv,它完全能提供上述 React 的所有优点,并且它也能完全满足我们自己的需求:更好的兼容性、更小的体积、更高的性能。

Why not preact / Inferno?

如果稍稍探索一下 React 社区的话,不难发现其实也有其他库同样实现 React 的 API,并且看起来似乎也能满足我们的需求,但其实走进仔细观察,情况并非我们想的那么简单。

preact

preact 是目前最火的一个 React-like 框架。它火的原因也很简单,首先 gzip 之后只有 3kb 的 size,其次它宣称和 React 有着 “the same ES6 API”。这两个优点咋一看给人一种很厉害的感觉,但实际在我们看来都是劣势。

在前端届很容易会产生一种错觉,如果一个库它的体积更小,就一定加载速度更快,一定性能更高,一定实现更简单。但实际情况完全不是这样。浏览器解析和运行的速度主要取决于代码运行的次数以及 JavaScript 引擎是否对代码进行了优化。因为 preact 的代码量少,所以很多情况都不能分别处理,这样性能反而会拖慢。 其次 preact 为了尽可能的小,代码的风格和具体的实现都难以理解,一些能确确实实解决痛点的 pull request 都因为代码太多而被拒绝 merge。在我们看来,这实在是因噎废食的举动。

而对于 the same API,这实际上是个文字游戏。 the same(相似) 和 identical(相同) 是有着很大的不同的。preact 提供 preact-compat 来提高对 React 的兼容性,但 preact-compat 还需要对 preact 原有的数据结构和流程进行一些 hack,实际上会对性能造成比较大的影响。而对于一些细节的实现来说,例如 children 一直是数组,而 React 可能是单节点。这样一来即便加上了 compat,preact 也无法无缝替换原有 React 老项目。

Inferno

Inferno 是一个以高性能为卖点的库,不得不说 Inferno 内部的实现的确相当精彩,它的数据结构、大量的性能优化和高效的 virtual-dom 算法都非常值得学习。如果说 preact 为了实现极端小的体积的而采用的实现和代码风格算是 JavaScript the bad parts 的话,Inferno 的数据结构和实现则是 JavaScript the good parts,它把 JavaScript 动态语言的特性和原型链的特点发挥得淋漓尽致。Nerv 也从 Inferno 那里取了不少经。但不出意外,Inferno 也有不能满足我们需求的问题。

首先是它的 API,连 “the same” 都没有做到,暴露出来的 API 和 React 不一致。而 inferno-compat 和 preact-compat 的问题一样,仍然是 hack 原有数据结构和流程,要做到无缝替换 React 还需要做大量的工作。

其次是它使用了大量的 ES6 新特性,并且没有在低版本浏览器对事件做兼容,如果要兼容到 IE 8 的话同样相当麻烦。

The one and only: Nerv

分析了我们的需求并对比其他竞品之后不难发现,我们除了再造一个轮子之外别无他法。

重要的事情不妨再说一次,Nerv 不单解决了我们需要接入现代前端工程化发展的问题,同时也能帮助我们平滑过渡需要兼容低版本浏览器和低性能设备的阵痛期,这些特点包括但不限于:

  1. 无缝对接 React 和其繁荣的生态。Nerv 的 API 和 React 完全一致,不需要一个类似于 nerv-compat 的东西来保证兼容性,只要在 webpack 稍微设置一下就能替换老项目的 React 依赖;
  2. 超高的性能。不管在任何一个性能测试中,我们都大幅超越 React/preact/anu 等竞品;
  3. 超小的体积。Nerv gzip 之后只有大约 8kb 的大小,只有 inferno + compat 的一半,React 四分之一的体积。在移动端设备和低速网络下这尤为重要;
  4. 良好的浏览器兼容性。我们在 CI 方面做了大量的工作保证测试即便在 IE 8 和低版本安卓也能跑,每一个 commit 都要在近 20 个设备中都跑通才能进入主分支。

除此之外,Nerv 还像一名共产主义战士一样经得住考验。它在京东双十一时期和今后的一段日子,都将部署在京东商城的首页和 Toplife 整站上。在这段时期,它接受 XX 万 PV,服务了 XX 万用户,节约了 XX 时间,提升了 XX 效益。

现在我们把 Nerv 开源,希望它能经受开源社区的考验,希望它能帮助更多的和我们有同样痛点的同行,希望大家能早点下班陪老婆孩子,也希望大家能帮助我们把 Nerv 做得更好。