Riot vs React & Polymer
以及Riot与这些兄弟库之间的差异。
React
Riot 2 受到 React 和 “内聚” 思想的启发。引用 Facebook 开发人员的话:
“模板分离的是技术,而不是关注点.”
我们对此见解表示致敬。目标应该是构建可重用的组件而不是模板. 将逻辑从模板中分离出来,实际上我们是把本该在一起的东西拆开了。
通过在同一个组件中将这些相关的技术组合起来,系统变得更干净。我们要表达对 React 的敬意,因为它体现了超凡的洞察力。
我们用 React 用得挺好的。在 Disqus Importer 中我们仍然在使用它。但我们被React的语法和文件大小 (特别是 语法)所困扰. 我们开始思考事情可能可以更简单;无论是内部实现还是对用户的接口。
React 语法
以下示例直接引用自 React 首页:
var TodoList = React.createClass({
render: function() {
var createItem = function(itemText) {
return <li>{itemText}</li>;
};
return <ul>{this.props.items.map(createItem)}</ul>;
}
});
var TodoApp = React.createClass({
getInitialState: function() {
return {items: [], text: ''};
},
onChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var nextItems = this.state.items.concat([this.state.text]);
var nextText = '';
this.setState({items: nextItems, text: nextText});
},
render: function() {
return (
<div>
<h3>TODO</h3>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.onChange} value={this.state.text} />
<button>{'Add #' + (this.state.items.length + 1)}</button>
</form>
</div>
);
}
});
React.render(<TodoApp />, mountNode);
JSX 是 HTML 和 JavaScript 的混合体. 在组件中你可以在各处包含HTML; 在方法内部及属性赋值中.
Riot 语法
上例用 Riot 写是这样的:
<todo>
<h3>TODO</h3>
<ul>
<li each={ item, i in items }>{ item }</li>
</ul>
<form onsubmit={ handleSubmit }>
<input>
<button>Add #{ items.length + 1 }</button>
</form>
this.items = []
handleSubmit(e) {
var input = e.target[0]
this.items.push(input.value)
input.value = ''
}
</todo>
上面的标签这样加载到页面上:
<todo></todo>
<script>riot.mount('todo')</script>
相同,又不同
在 Riot 中先有 HTML ,然后才是JavaScript . 两者放在同一个组件中,但清晰地与彼此分离开。HTML 中可以加入 JavaScript 表达式.
除了在花括号里写表达式的写法,没有专有的东西.
几乎看不到什么脚手架(boilerplate)代码. 更少的符号,标点,系统属性和方法名. 字符串可以做插值: "Hello {world}"
替代 "Hello " + this.state.world
,方法可以用简洁的 ES6 语法来写。所有的东西都变少了.
我们认为 Riot 的语法是分离布局和逻辑,又能享受独立的组件带来的好处的最干净的方式。
基于字符串String vs 基于 DOM
组件初始化的过程中,React 解析字符串,而 Riot 遍历 DOM 树.
Riot 从DOM树中提取表达式,将它们保存在一个数组里。每个表达式拥有一个指向DOM结点的指针。每次更新,这些表达式被重新求值并与DOM中的旧值进行比较。如果有值发生了变化,相应的DOM结点也被更新。这样,Riot也有了一个虚拟DOM,只是是非常简单的一个。
由于这些表达式可以被缓存,更新周期是非常快的. 遍历 100 到 1000 个表达式通常只需要不到 1ms.
React 的同步算法要复杂得多得多,因为HTML布局在每次更新后会随机变化. 这项挑战巨大,所以必须为 Facebook 开发人员的优秀成果点一下赞.
我们看到这些复杂的比较是可以被避免的。
在 Riot 中 HTML 的结构是固定的。只有循环和条件能够增加或删除元素。例如,一个 div
不能变成一个 label
. Riot 不做复杂的子树替换,而只是更新表达式的值。
Flux 和路由
React 只搞UI, 这是正确的. 所有伟大的软件项目都专注于一个点。
Facebook 建议使用 Flux 来管理前端代码的结构。它不象一个框架而更象一种模式,里面有很多好的想法.
Riot 提供了自定义标签,事件触发器 (observable) 和路由功能. 我们相信这些是前端应用的基本组成单元,自定义标签用于用户界面, 事件用于模块化,路由用于 URL 管理和回退按钮支持.
象 Flux 一样, Riot 非常灵活,将更大的架构决策留给开发人员去做。它自己只是一个帮助你完成目标的工具库。
你可以使用Riot的observable和路由器构造一个类似 Flux 的系统. 事实上这东西已经 有了.
大小相差 3倍 - 6倍
React (v16.4.0) 比 Riot 大 3 倍
react.min.js – 33.27KB
riot.min.js – 13.21KB
推荐的 React 路由器 (v4.1.1) 比 Riot 路由器大 6 倍 .
react-router.min.js – 10.95KB
react-mini-router.min.js – 4.52KB
riot.route.min.js – 1.77KB
必须承认,这个路由器比较是不公平的,因为 react-router 功能强大得多. 但上面的图表清晰地体现了Riot的目标: 只提供最少量的API.
React 的生态系统更框架化,倾向于庞大的 API 接口. 在React社区里上面这个大路由器比 react-mini-router 更受欢迎。
Polymer
Polymer 使用 Web Component 标准,使它在最新的浏览器上可用. 这使得你可以以标准化的方式编写自定义标签。
从概念上看 Riot 的目标是一样的,但也存在区别:
-
Web Component 语法是实验性的,比较复杂
-
Riot 只更新变化了的元素,因此DOM操作更少
-
组件是通过 HTML
link rel="import"
载入的. 必须使用 Polyfill 来对 XHR 进行排队, 非常的慢. Riot 标签用script src
载入,多个标签可以用常用的工具来进行合并。 -
Polymer 使用双向数据绑定,而 riot 是单向绑定.
-
Polymer不支持服务端渲染。
大小相差 6 倍
Polymer (v1.8.0) + WebComponents(v0.7.24) 比 Riot 大 6 倍
polymer.min.js – 49.38KB
riot.min.js – 10.85KB
Web components 被称为 polyfill之王 ,这就是为什么 Polymer的实现需要这么多代码。
Web Componnets
Web Component 是标准,所以会是组件技术的最终方向。最终互联网上将全部是这种标准组件,但这可能需要很长时间
由于其中涉及的复杂性,有很大的机率这些组件并不是被直接使用。在web component上面将会有新的层次。就象目前我们有jQuery一样。大部分开发者并不是直接使用DOM API.
Riot就是这样一种抽象。它提供了应用可以一直使用的非常易用的API。一旦 web component 规范发生了进化,并带来了真实的好处,如性能上的提升,Riot可以 在内部 使用它。
Riot的目标是使 UI 开发尽可能地简单。当前的 API 。可以把它理解成 “web component的jquery” - 它提供了达成同样目标的更简短的语法。它简化了编写可重用组件的整体开发体验。