基于组件的 Web 应用程序






4.94/5 (9投票s)
Web 应用程序正在演变为更基于组件、数据驱动和面向服务的模式。本文记录了我们在评估 Polymer 和 React 用于基于组件的 Web 应用程序开发时所寻找和发现的内容。
引言
大约三个月前,我收到了一个需要回答的问题:我们需要扩展我们的 Web 应用程序,还需要将架构演变为更模块化和组件化,因为我们将添加更多功能,我们应该使用哪种技术?
此外,我们的 Web 应用程序已经投入生产服务客户,无论我们决定采用哪种技术,都不能破坏当前功能,现有的发布周期需要继续和维护,迁移对最终用户来说需要完全透明。
这确实意味着新的组件化工作需要是一个“分阶段的过渡”,我们需要逐步“切入”新组件来替换 Angular 指令。它要求新的组件技术与当前的 Angular 应用外壳平稳工作,并最终从 Angular 转向新架构。
作为技术评估过程的一部分,以下是我需要研究的子问题
- 有哪些值得深入研究的选项?
- 组件模型是什么?它是如何封装和沙盒化的?
- 组件内部和组件间交互机制是什么?
- 数据集成/数据流如何工作?是双向绑定还是单向绑定?
- 加载/渲染性能如何?
- 编程模型是什么?是命令式还是函数式?
- 它是否已达到生产就绪状态并面向未来?它是否拥有广泛的社区支持?
- 最后,我们应该开始迁移到哪种技术?
在回答这些问题之前,让我们退一步,先看看大局。
背景
随着 Web 应用程序的增长和扩展,“解耦”是技术演进的主题。服务器端单体应用程序很久以前就被解耦为多层客户端-服务器架构,而服务器页面技术几年前能够进一步解耦为原生客户端应用程序和可共享 API,例如面向服务的 HTML 应用程序(Ajax/多页面/会话同步)。然后,单页面应用程序通过客户端JavaScript 框架和服务器端RESTful API简化和改进了它。如今,Web 应用程序一直在不断演变为越来越基于组件、数据驱动和面向服务的模式。
基于组件的 Web 应用程序延续了使 SPA / SOHA 更具可组合性和高性能的趋势。它通过可重用性、可组合性和模块化促进封装和划分,将关键业务逻辑保留在服务器端数据服务 API 中,开始进一步解耦应用程序功能,从而为大型 Web 应用程序将可重用性、可维护性和可伸缩性提升到新的水平。
这种演变的一个优点是,服务器端 API 越来越与应用程序无关,可以被不同类型的原生客户端(Web、原生移动、桌面应用程序等)和服务器端消费者完全重用。当客户端向组件化发展时,服务/API 端不需要任何更改。尽管 同构 JavaScript 需要一些服务器端更改,但目前我并不相信它是 Web 应用程序的未来,即使是为了解决最初的性能问题,相反,我坚信基于组件的 Web 应用程序才是未来。
几乎所有著名的 JavaScript 框架都认识到“组件”的重要性,它们每个都以这样或那样的方式拥有“组件”的概念,包括 Ember、Angular、ExtJS 和 Backbone,当然还有 JQuery UI 和插件。但遗憾的是,每个组件模型都不同,彼此不兼容,甚至设计原则和运行时行为也不同。另一方面,当前 Web 平台的发展趋势 使单体框架的需求过时。
一些备受尊敬的行业影响者也是摆脱单一 JavaScript 框架的支持者。Jimmy Breck-McKye 撰写了一篇文章,2015 年 JavaScript 的现状,提议“倾向于专用库而不是单一框架”。《JavaScript Enlightenment》和《DOM Enlightenment》的作者 Cody Lindley 也 发推文 说“单一 JS 应用程序框架解决方案最适合企业的观念是错误的。一体式 JS 框架是问题而不是解决方案。”而且,Joe Gregorio 备受阅读的文章,不再使用 JS 框架,促成了“零框架宣言”这一短语的诞生。
Web 平台不断发展,基于组件的 Web 应用程序是未来。凭借当今的技术,我们可以发展我们的 Web 应用程序,将其从高度固定的单体框架转变为更模块化和组件化。自包含、可重用、可组合的组件也提高了生产力,最终,它能够有效地创建功能和快速迭代,从而更有效地为客户提供价值。
我们的第一个问题是:目前有哪些选择?
选项
目前,Angular 指令是我们的生产环境中的组件模型。从技术上讲,上面提到的所有框架都是潜在的候选者,它们每个都有创建“组件”的方法。由于我们决定摆脱单一框架,以下选项不在考虑范围内
- AugularJS 2.0 (alpha):尽管它从头开始重新设计,但它缺乏向后兼容性,用 TypeScript 而不是 ES6 / ES2015 编写,缺乏与 Polymer 协作的明确策略(如果“存在一个元素”,我为什么还需要 Angular 2.0?),这些都是危险信号;
- EmberJS:尽管它是一个一站式 Web 开发解决方案,但我们已经拥有了自己的开发基础设施和工具,我不是 Ember 强烈意见的忠实粉丝;
- Backbone:轻量级且功能强大,但视图未解耦,并且对其他 Backbone 特定的构造有太多依赖。Ampersand.js 继承了 Backbone 的强大功能,同时通过分解为更小的可插拔 npm 模块来“解耦一切”,这是一项非常有价值且有趣的努力。但它仍然采用命令式数据绑定模型,而且在我的相当简单的实验应用程序中,npm 模块很快就膨胀成了“太多难以管理”的混乱包。
- Vue.js:它的简洁和易用性最近获得了普及,与 React.js 类似,它只是一个视图层,也深受 Angular.js 和 Polymer 的启发。根据与其他框架的比较文档,Vue.js 确实试图解决 Angular.js 中的问题,同时保留了 HTML 模板、命令式 DOM 操作和数据绑定机制,与 React.js 相比。尽管这些天社区支持正在扩大,但 Vue.js 几个月前才“达到生产就绪状态”,它正在快速变化和发展,有很多发布。此外,由于其新生性质,它也缺乏广泛和成熟的“附加”库,如路由器和数据流集成等。在此阶段,不适合将其视为服务我们生产 Web 应用程序的候选者,但它值得留在我们的观察名单中。
此外,Mithril JS 拥有一个令人惊讶的快速 虚拟 DOM 实现,占用空间非常小,为“组件”提供了简单清晰的 API,是轻量级快速 Web 应用程序库的一个非常值得注意的替代方案。正如 Vue.js 试图解决 Angular.js 的缺点一样,Mithril JS 确实试图解决 React.js 的重量问题。由于它仍处于初期阶段,并且没有足够广泛的社区支持,我们决定将其也列入观察名单。
现在我们只剩下两个选择:Web Components 搭配 Polymer 以及 React 搭配 Flux。
概述
Polymer 和 React 都支持带声明式标记的可组合组件,两者都具有强大的表达能力和简单的 API,两者都可以在过渡期间与我们的 AngularJS 应用程序外壳协同工作,并且两者都对 Web 开发产生了深远影响。
React 促使我们“重新思考最佳实践”,甚至“重新定义”最佳实践:它不是将 HTML 视为 Web 的一等公民,而是从 JavaScript 生成 HTML;不是构建基于类的外部 CSS,而是提倡内联样式并通过 JSON 对象创建样式;不是数据绑定,React 鼓励应用程序在状态/属性更改时渲染“整个页面”;不是 MVC,它引入了“控制器视图”的概念,用于处理顶级组件状态并将其作为属性传递给子组件等。当这些长期以来被奉为“最佳实践”的东西在 React 的世界中全部瓦解时,确实需要一些时间来理解它并亲自动手才能真正看到它的好处。
显然,“最佳实践”是一个被过度使用和可笑的术语,它指的是当前“良好”的实践,有时需要完全抛弃以追求“更好”。
当我们对 React 保持开放心态时,Polymer 看起来也很有前景。除了 开源 并围绕它构建 实用的自定义元素 之外,Web Components 已经是 W3C 标准 的一部分,正在获得 Chrome 之外的浏览器原生支持,并且社区支持和 生态系统也在不断扩大。所有这些都是在做出长期承诺之前需要考虑的关键因素。我完全赞同 Web Components 的理念:DOM 元素是 Web 上的组件模型,对其进行演进。
具体来说,Web Components 使用 Custom Element 来让开发者扩展 HTML 词汇表,以实现视觉和非视觉功能,还使用可选的 HTML 模板 和 Shadow DOM 进行实现,然后通过 HTML Import 进行打包/导入。非常直观且贴近 Web 原生。
简洁性和性能是 React 引人注目的特性,而直观性和标准化是 Web Components 的亮点。由于我们正在为生产环境做出技术选择,因此在给出可靠建议之前,我们需要深入研究。
让我们首先看看组件模型。
封装
组件模型中的封装包含三个方面,这对我们的生产至关重要
- 运行时隔离:自身与其容器之间明确的边界,以保护或隐藏组件细节(样式、脚本等)和子元素
- 交付打包:灵活打包,支持与宿主应用程序捆绑或作为自包含可下载包进行懒加载
- 开发结构:组件的 JavaScript、HTML 和 CSS 独立源代码组织,无论组件如何打包或交付
运行时隔离
Web Components 的 shadow DOM 为自定义元素提供了沙盒,宿主文档的脚本将无法通过 DOM 遍历 API(如 querySelector, getElementById
等)访问 shadow DOM 内部。此外,不会发生 CSS 样式从宿主文档泄漏到组件的情况,反之亦然。尽管 JavaScript 在当前规范中未沙盒化,但这种 DOM 级别的运行时封装在宿主文档和自定义元素之间提供了非常清晰的边界和保护。
然而,截至撰写本文时,只有 Chrome 和 Opera 具有原生支持,Firefox 通过选择加入标志支持它,而其他浏览器需要polyfill才能使其工作。现实是,通过 JavaScript polyfill Shadow DOM 在技术上是困难的,因此 polymer 1.0 将其默认设置为 Shady DOM。Shady DOM 提供了跨浏览器的一致性和性能,但失去了 DOM 树和样式的封装。
显然,Polymer 1.0 未能实现 Shadow DOM 的开箱即用封装承诺。从样式保护和本地 DOM 沙盒的角度来看,Polymer 的 Shady DOM 实现与 React 的 Virtual DOM 几乎相同,而 React 的 Virtual DOM 从未设计用于封装。
在当前阶段,在运行时封装方面没有赢家,虚拟 DOM 封装的承诺将更多地依赖于浏览器的原生支持而非 polyfill。
打包
Polymer 在通过 HTML Import 进行组件打包方面明显胜出。由应用程序决定是通过 Polymer 的构建工具 Vulcanize 捆绑它,还是在需要懒加载时在运行时托管/导入自包含组件(HTML、CSS 和 JavaScript 文件)。
相比之下,React 组件不是自包含的(除非所有内容都内联),并且懒加载不如 Custom Elements 直观。
以下是通过 HTML 导入加载自包含 Web 组件的示例
<link rel="import" href="scripts/dev-studio/lib/studio-navbar/studio-navbar.html" />
然后在 HTML body 中实例化它
<studio-navbar project-info="{{projectInfo}}" ng-controller="NavBar" action-items="{{actionItems}}"></studio-navbar>
*注:`ng-controller` 是连接到现有 Angular 应用程序外壳的钩子,一旦我们完全脱离 Angular,它就会消失
然而,现实是,实际使用 HTML Import 存在一些注意事项,尤其是在整合来自不同团队或提供商的多个 Web 组件时。例如,如果组件 A 依赖于 jQuery 1.9,而组件 B 依赖于 jQuery 2.1,浏览器将同时加载两者。鉴于 JavaScript 未被沙盒化,这可能导致不良行为。在 JavaScript 沙盒化实现之前,所有组件提供商都需要就共同的依赖管理策略达成一致并坚持,但有时即使在同一公司内部,也很容易不同步。
代码结构
Polymer 和 React 都没有明确规定源代码应如何组织,Polymer 支持外部 JS/CSS 和内联,尽管我发现为了使其在 Firefox V1.1 之前工作,我必须内联 JavaScript 代码。如今,随着 Browserify 和 Babel 部署到几乎所有新的 Web 应用程序项目,开发人员可以完全控制组件源代码的结构。
既然 Polymer 使用 Custom Elements 作为组件模型,而 React 组件都派生自 React.Component 基类(带或不带 JSX),我们可以继续探讨组件交互方式的差异。
组成
组合对于组件模型也至关重要,以下是我们所期望的
- 组件可以轻松嵌入/嵌套,具有清晰的接口,默认行为/状态应该易于获取和定制
- 组件包(实现特定功能)应该是自治的、可插拔的且可样式化的
- 不允许直接的组件间(兄弟)交互/通信,组件内部交互机制应明确定义(父子)
Polymer 的组合功能与常规 HTML 元素的工作方式相同,它比 React 的 JSX 语法 更直观。从语法上看,它们非常相似,尽管 Polymer 使用标准 HTML 而 React 使用 JSX。JSX 只是一个语法糖,它总是编译成 JavaScript 函数。从组件消费者的角度来看,两者都高度可组合和可定制。
这是一个复合 React 组件的示例
'use strict';
import React from 'react';
import NavBarMenus from './react.navbar.menus';
import NavBarLinks from './react.navbar.links';
class StudioNavBar extends React.Component {
static get defaultProps() {
return {};
}
constructor(props) {
super(props);
}
render() {
return (
<div>
<NavBarMenus></NavBarMenus>
<NavBarLinks></NavBarLinks>
</div>
);
}
}
module.exports = StudioNavBar;
显然,组件间交互与“组件”或“封装”的概念相矛盾,Polymer 和 React 都不允许它,兄弟组件从不直接相互通信,它们通过它们的父组件协同工作。
Polymer 将促进子-父通信的父组件称为“中介模式”,而 React 则将其命名为“控制器组件”或“容器”。它们都以相同的方式工作:子组件在发生某些事情时需要通知父组件,由父组件来处理,无论是通知其他子组件还是冒泡。
同样,Polymer 具有直观的标准事件机制,自定义事件的工作方式与内置 HTML 元素事件相同,包括事件冒泡。在我的测试应用程序中,一个 Polymer Web 组件作为组合测试被深埋其中,其自定义事件与期望事件冒泡的 document.onclick 事件处理程序无缝协同工作。
相比之下,React 组件没有事件冒泡的概念。推荐的方法是使用 Flux 单向数据流:子组件使用数据触发 Action,而 Action 分派到监听的 Store,Store 整合数据更改(或 Action 的 CRUD 的 Ajax 结果),然后发出更改事件。“控制器组件”或“容器”通过回调响应 Store 事件,更新组件“状态”,然后将状态更改作为子组件属性传播以进行重绘。
这是上面所示的相同组件的示例代码,但使用了单向数据流
'use strict';
import React from 'react';
import navbarStore from './react.navbar.store';
import navBarActions from './react.navbar.actions';
import NavBarMenus from './react.navbar.menus';
import NavBarLinks from './react.navbar.links';
class StudioNavBar extends React.Component {
static get defaultProps() {
return {};
}
constructor(props) {
super(props);
this.state = navbarStore.get();
this._onChange = this._onChange.bind(this);
this.onNavBarAction = this.onNavBarAction.bind(this);
}
componentDidMount() {
navbarStore.addChangeListener(this._onChange);
}
componentWillUnmount() {
navbarStore.removeChangeListener(this._onChange);
}
_onChange() {
this.setState(navbarStore.get());
}
render() {
return (
<div>
<NavBarMenus projectInfo={this.state.projectInfo} menus={this.state.menus}></NavBarMenus>
<NavBarLinks links={this.state.links} ></NavBarLinks>
</div>
);
}
}
module.exports = StudioNavBar;
乍一看,Flux 的单向数据流听起来很复杂,这促使我更深入地研究两者之间的数据集成差异。
集成
我们的 Web 应用程序是重度数据驱动的,我们正在寻找的组件模型数据集成机制更多地考虑效率和性能
- 支持面向服务架构,无需通过 Ajax 和 RESTful API 更改现有客户端/服务器集成
- 通过在客户端保留用户会话,实现无状态服务器端 API
- 增量数据更改(用户交互或 API 响应的结果)应高效处理,视图始终反映并与数据/模型同步
Polymer 和 React 都不是成熟的框架,两者对会话如何维护以及应用程序状态/会话数据如何检索都没有强烈的主张。这正是我们所需要的,我们已经有相当多的 Ajax 代码用于客户端/服务器集成,我们的 RESTful API 相当稳定,也被移动应用程序使用,我们不希望在前端演进时改变这一点。
从架构上看,Polymer 和 React 都很好地支持了第 1 项和第 2 项(或没有限制),真正的区别在于第 3 项。
Polymer 1.0 为了性能而限制了双向数据绑定,有时我被迫使用繁琐的“计算属性”,错过了类似 mustache 的数据绑定语法的简洁和便利。当涉及到大量集合绑定时,您可能还需要直接操作 DOM 以实现性能。
在数据集成方面,Polymer 仍然处于传统的数据绑定世界中,组件进出数据通过经典的 HTML 元素的属性/特性和事件。相比之下,React 更令人震惊,因为 React 本质上告诉我们不需要双向数据绑定,并且首选单向数据流:数据只从“控制器组件”或“容器”向下流向子组件,这样一致、简化和确定性的状态管理成为可能。
实际上,React 不需要单向数据流,它反而实现了单向数据流。
对于大型数据驱动应用程序,React 虚拟 DOM 的设计原则和运行时性能真正大放异彩,它不仅极大地简化了组件的状态/属性/数据管理,而且使高级应用程序状态管理变得轻而易举。无需处理双向数据绑定可能导致的级联数据/视图循环更新,React 真正使我们能够轻松识别确定性应用程序/组件状态的单一事实来源。
以下是利用 Reactive Extensions 进行单向数据流(而非 Flux)的相同复合组件
'use strict';
import React from 'react';
import NavBarMenus from './react.navbar.menus';
import NavBarLinks from './react.navbar.links';
import navBarAction from './reactive.navbar.action';
import navBarStore from './reactive.navbar.store';
let StudioNavBar = React.createClass({
getDefaultProps() {
return {};
},
getInitialState() {
return {
projectInfo: {
name: "",
version: ""
},
menus: [
{title: "", icon: "", action: ""}
],
links: [
{title: "", icon: "", action: ""}
]
};
},
componentDidMount() {
this.stateSubs = navBarStore.subscribe( (stateData) => this.setState(stateData) );
},
componentWillUnmount() {
navBarStore.dispose(this.stateSubs);
},
render() {
return (
<div>
<NavBarMenus projectInfo={this.state.projectInfo} menus={this.state.menus}></NavBarMenus>
<NavBarLinks links={this.state.links}></NavBarLinks>
</div>
);
}
});
module.exports = StudioNavBar;
简化的应用状态管理对于我们高度数据驱动的应用是一个很大的优势,而运行时性能是虚拟 DOM 实现的另一个好处。在数据集成方面,React 比 Polymer 具有很大的优势。
性能
尽管许多方面会影响应用程序性能,例如优化、构建、Web 服务器配置等,但这里我们只关注直接由库贡献的因素,而忽略其他应用程序和服务器的特定因素。快速加载和高效渲染对于数据驱动的大型应用程序至关重要,基于组件的 Web 应用程序的性能应满足以下要求:
- 跨浏览器一致的高性能,包括加载、渲染和更新
- 大型集合渲染和事件处理的高性能
- 灵活高效的应用程序状态管理,实现确定性的快速视图更新
我的测试应用构建了两个分支,一个用于 Polymer 1.1.5 实现,另一个用于 React 0.14.0 和 Flux,以下是 localhost 上的一些性能数据:(Firefox v41.0.2,带 webcomponents-lite.min.js polyfill)
|
加载中 |
脚本 |
渲染 |
绘制 |
其他 |
加载时 |
JS 堆 |
节点 |
|
Polymer v1.1.5 |
29.63 毫秒 |
859.12 毫秒 |
26.95 毫秒 |
2.39 毫秒 |
148.44 毫秒 |
1,570 毫秒 |
30,838,896 |
13,771 |
|
React 0.14.0 |
19.18 毫秒 |
919.49 毫秒 |
34.33 毫秒 |
2.92 毫秒 |
174.56 毫秒 |
1,710 毫秒 |
56,132,296 |
16,031 |
|
以下是值得注意的阅读内容
- Polymer 应用程序加载速度比 React 慢 54.48%。我的组件打包可能导致了这种缓慢:所有 3 个组件都是按需下载的(未使用 Vulcanize),并且还需要额外往返一次以获取 polyfill;
- 一旦下载完成,Polymer 应用程序的初始渲染速度略快于 React。React 应用程序在脚本方面慢 7.02%,渲染慢 27%,绘制慢 22%,加载时脚本慢 8.9%;
- React 应用程序的内存占用比 Polymer 大得多:JavaScript 堆大小大了 82.02%,这主要是虚拟 DOM 实现的结果;
- (注:我没有列出重新渲染(由模型中大型集合更新触发)的性能数据,从技术上讲,React 的虚拟 DOM 效率很高,但我会将其留到另一篇文章中讨论用 Reactive Extensions(如 RxJS)替换 Flux。)
值得注意的是,如果初始加载性能至关重要,React确实支持同构JavaScript,而Polymer不支持服务器端渲染。
在 Polymer 应用构建中进行 Vulcanizing 将提高加载速度,而一致的依赖管理和打包策略可以极大地影响基于 Polymer 的应用加载性能。在我们的用例中,重新渲染(视图更新)性能比初始加载更重要,我将在完成所有 React + RxJS 实验 的测试后,撰写 另一篇文章 进行后续讨论。
编程模型
无论有没有组件,理想的 Web 开发编程模型都应该是
- 简单而强大:避免不必要的复杂性,如消化循环、依赖注入等,拥有强大功能的简洁 API
- 高效且安全:高效渲染,简化状态管理和 DOM 操作,内置安全内容
- 函数式和响应式:这是非常好的特性,因为 DOM API 是命令式的
Polymer 使构建可扩展的 Web 变得容易,它的创新真正依赖于扩展当前众所周知和基于标准的 Web 范式,其基于自定义元素的组件模型与 React JSX 相比再直观不过了。作为组件开发者,延续性和一致性也适用于编程模型,它遵循经典的命令式 DOM API,可以直接访问 DOM。
然而,由于 JavaScript 在 Shadow DOM 中未沙盒化,没有什么能真正阻止组件的 JavaScript 外部操作宿主文档。这种非作用域 JavaScript 命令式编程模型就像当前 Shadow DOM 规范中缺少的功能。
与 Polymer 的数据绑定和命令式编程模型不同,React 支持并倡导由虚拟 DOM 驱动的函数式编程。
虚拟 DOM 具有高性能的差异算法,可以识别实际需要更改的 DOM 元素,应用程序可以避免昂贵且缓慢的直接 DOM 操作。因此,应用程序状态数据可以是不可变的,不需要复杂的状态管理和跟踪,每当数据更改时,我们可以依靠 React 来找出更新 DOM 最有效的方式。
此外,React.js 中不需要数据绑定,JavaScript 和 CSS 也可以内联。
我花了一些时间来理解 React 的这些“非最佳实践”,结果发现它们是 React.js 最引人注目的创新:在传统的命令式 DOM API 之上,使函数式和无状态 DOM 编程成为可能。绝对不需要数据绑定,简化了状态管理,这一切都因为应用程序可以完全将 DOM 操作委托给虚拟 DOM。
显然,React 在编程模型方面胜出。它为利用 immutable.js 和 Reactive Extensions 实现简化而强大的数据和状态操作打开了大门。
生产就绪
当我们改进已经投入生产的应用程序架构时,我们真正寻求的是产品就绪的解决方案,主要考虑因素是
- 稳定性:我们正在寻找相对稳定、能够支持我们生产发布周期的尖端解决方案
- 采用率:被高度扩展的商业网站采用得越多,它就越稳固
- 社区支持:活跃的开源社区支持,以及生态系统的发展(附加组件、辅助工具、实用工具、工具等)也是非常重要的方面
这是使用 React.js 的网站列表,这是使用 Polymer 的精选 Web 应用程序和网站集合。尽管两者都有广泛而活跃的社区,但 React.js 在一些知名网站中拥有更广泛的采用,例如 AirBnB、BBC、CloudFlare、Dropbox、Flipboard、Netflix、Salesforce、Uber、Yahoo,当然还有 Facebook。
从 GitHub 来看,React.js 拥有比 Polymer 更多的观察者、星标和分支(截至撰写本文时:2,274、31,194、4,892 vs 987、13,204、1311)。Polymer 有 74 个版本,而 React.js 有 34 个版本。React.js 比 Polymer 更经生产验证。
除了生产就绪之外,未来可靠性也是我们的考量。有 W3C 标准机构的支持,Polymer 拥有最光明的前景。然而,在浏览器原生支持、采用、成熟度以及更广泛的社区支持和生态系统方面,标准机构和 Polymer 在未来都有一些工作要做。
总结
无论这项技术探索多么引人深思,最终我们都必须为下一阶段的开发选择一个。当我们退一步,整体地看待这两种解决方案时,React.js 在这一点上(React.js v0.14.2 与 Polymer v1.2.1 相比)具有明显的优势。
W3C Web Components 标准和 Google 的工程实力使 Polymer 成为基于组件的 Web 应用程序最具未来前景的解决方案,它扩展了 Web 平台的演进,同时深刻地改变了 Web 应用程序的开发方式和 API 的暴露方式。尽管我相信 Web Components 是未来,无论有没有 React,但在组件依赖管理得到有效管理(通过 HTML Imports)以及 JavaScript 被沙盒化(像 CSS 和 HTML 模板一样)之前,Shadow DOM 不会获得广泛的浏览器原生支持。
React.js 的虚拟 DOM 具有颠覆性,其生态系统(附加组件、响应式扩展、immutable.js、单向数据流架构等)正在不断发展和扩大,随着在 Facebook 和 Instagram 上的成功部署,它更加稳定,同时以适当的速度持续演进,更适合生产使用。
尽管有一些努力将两者的优点结合起来(例如,将 React 组件实现为更小的 Web Components 的组合,或使用 Web Components 作为顶层可组合组件接口并用 React 实现它们等),但我会将这些尖端探索留给先行者,我们下一阶段的生产开发将采用 React.js 组件和编程模型。
目前,我的回答是:我们应该从 React 开始,并准备好迎接 Web Components。