OSC 协作翻译
原文:Animating 2048 SVG nodes in React, Preact, Inferno, Vue, Angular 2, and CycleJS – a side-by-side comparison
链接:https://swizec.com/blog/animating-svg-nodes-react-preact-inferno-vue/swizec/7311
译者:tv_哇, 无若, 边城, Tocy
曾经想知道如果播放一个由 2048 个 SVG 节点组成的动画,使用哪个前端框架会更流畅?这里有一些 GIF 动画展示。
同样是毕达哥拉斯树动画,同样是在2012年中的视膜屏 MacBook Pro 中。所有动画都使用 LICEcap 录制,使用常规设置,可以在 Chrome、Spotify、Emacs 中运行。点击“阅读原文” 可以看到它的代码。
预览
很多 GIF 都能在它们的 GitHub 上找到链接。甚至可以复制库然后在本地运行,非常有趣。
实现者: 本文作者
实现者: Jason Miller,Preact 的创造者
实现者: Dominic Gannaway,Inferno 的创造者
实现者: Evan You,Vue 的创造者
实现者: Tero Parviainen,Java 顾问
实现者: Wayne Maurer,Lambda IT 的创始人
感谢 Jason、Dominic 和 Evan 创建分支,同样感谢 Tero 和 Wayne 加入了他们自己的版本。如果有人能不使用框架,直接用原生 Java 实现,那一定很酷。
让我们通过代码了解它是如何工作的。
React
在 Jason 和 Evan 的提示下,对鼠标事件进行节流能使 demo 更快。结果证明原始版本的动画树运行缓慢不是 React 自身的原因,而是每个刷新周期内太多的请求让渲染引擎不堪重负。
我尝试过对 requestAnimationFrame 进行节流,但是实际效果并不好。相比之下限制 React 重绘周期的方法就简单而有效。
先检查是否在进行更新,如果没有就手动更新。这个之所以生效是因为 React 的引擎是同步的。
要是没有 React Fiber,我觉得它可能会挂掉。¯(ツ)/¯
Preact
Jason 用 preact-compat 层使得 Preact 看起来很像 React。这很有可能影响它的性能。
我喜欢 Preact 的示例是因为它使用异步渲染让效果更流畅。鼠标移动后,你能看到重绘周期滞后而产生的神奇效果。我很喜欢这效果。
代码实现:diff on github
在 package.json 中,他添加了 preact,preact-compat 和 React 库的 preact-compat 克隆,后者能让你不需要改变 imports。
他把无状态的 Pythagoras 功能组件转换成一个有状态的组件从而实现异步渲染。
并启用去抖动异步渲染:
我最喜欢 Preact 的部分是,它可以作为 React 的替代品,并且运行良好。从我目前的应用程序来看,它的性能优化会有不错的发展前景。
Inferno
你可以用 Inferno 替代 React。Dominic 说这会影响性能,所以他就创建了新分支。你可以在 github 上比对差异。
Dominic 把所有相关的 react-s 改成 inferno-s。他同时也把 react 改成 inferno-beta36,这意味着我的CTO肯定不允许我在生产环境中使用它。
从上面看出,它最主要的是各种导入的改变——React 变成 Inferno,还把许多类方法改成绑定箭头函数。我不知道这是出于风格的选择还是 Inferno 的需要。
他也把基于字符串引用改成基于回调引用,Inferno 因为性能的原因不能使用基于字符串引用。取而代之,我们可以用 D3 来检测 SVG 上的鼠标位置。这比起我们自己弄简单很多。
在 Pythagoras 核心组件上,他添加两个 Inferno 特殊属性:noNormalize 和 hasNonKeyedChildren.
从八天前的 issue 知道,noNormalize 是提高性能的一个基准, hasNonKeyedChildren 的作用尚不明确。我猜想这两个属性都是用来为虚拟 DOM diffing 算法优化性能。
Vue
这项工作的工作量比较大,是由Evan和树的创作者Phan An 完成的。
Vue 并没有打算模仿 React 的 API,它拥有自己的一套代码。我想通过 github 展示它们之间的差异,但这比较繁琐。我建议你去 Github 自行查看。
你可以识别出 Pythagoras 核心组件。Evan 使用了 transform-vue-jsx,使得 JSX 能在 Vue 里使用。
main.app 文件在此,你可以点击查看然后理解其代码。
让我们来试一下吧。
把它分成 template, 和 style 三个部分。这看起来有点像 JSX 或者 HTML,但实际上是模板带上了冒号前缀。
Vue 似乎采用了 React 的 put-it-all-together 组件化提示,但按语言拆分。虽然这看起来很简洁,但按照我以往的经验,实际操作其实很麻烦。
App 组件还是跟过去类似,但它使用 data() 来定义默认的状态,用 $refs 代替 this.refs,用一个 name 属性代替了命名类本身,components 属性定义子节点,用 methods 属性来定义类方法。
Angular 2
虽然我不是 Angular 的粉丝,但我得承认它的确很好使用。
我不知道为什么,也许 Type 的那些类型检查在转译后增加了运行时额外开销?
显然代码是重写了,Tero 需要将代码迁移到 Type,这真厉害,我可做不到。
我很好奇,编程语言的隔阂会怎样影响你在网上找的随机库的可重用性。
代码看起来似乎包含很多文件。而 App 是分成 app.module.ts,app.component.ts,app.component.html 和 app.component.css 几个文件。和 Pythagoras 一样。
当你看到 Angular 的 html 文件时,意味着 Angular 坚持了一个文件对应一种语言的传统。
这在 HTML 中看起来很有趣。
我对模块和组件之间区别的还没有完全理解。似乎模块定义了某些确定的引入,子组件等。但是每个组件仍然会定义它自己的 CSS 和 模板导入。
也因为我对这一块内容的了解不够深入,在此不对其用例的好处做过多描述。
CycleJS
这在我的设备上显示很流畅,可能是因为我刚刚看完 Angular 版本的演示。
Wayne 把所有东西都转译成 Type,但似乎不是 CycleJS 要求的那样。尽管如此,他能保持与原始文件相同的简单结构,这一点深得人心。
在此我无法详细说明 Wayne 因 Type 和 CycleJS 做的一些改变,他没有使用类定义 CycleJS 组件,其结构更像是在学校学的闭包结构。
这需要时间适应。
我不喜欢的部分是 Wayne 在 CycleJS 中指定 DOM 的方式。主程序看起来像是这样:
这似乎与使用 React. 方法非常类似,但可读性较差。 CycleJS 确实支持 JSX,但作者并没有调用它,这一点让我纳闷。
编辑于12月24日:正如 @spion 指出的,结果证明 CycleJS 示例仅渲染 2^10 个矩形。由于 .take 工作方式的不同,相比其他示例多了近一半。这对 jankiness 有着巨大的影响。
“
@spion @dan_abramov @waynemaurer @andrestaltz 刚刚检查, document.getElementsByTagName('rect').length 确是只返回 1024 。
— Swizec (@Swizec) 12. 24, 2016
编辑2:修正本地副本中的代码,更新 gif 图,效果非常平滑。我对此印象非常深刻。
总结
它们看起来都很平滑,但我不知道谁是最快的,可能是 VUE,也可能是 Inferno。我比较喜欢 Preact,因为它可以异步渲染,非常酷,CycleJS 也给我留下了极为深刻的印象,但我仍然对 Angular 持有偏见。在我用过的框架中,口碑好也的确好用的要算 React 了。
推荐阅读
2016 年度开源中国新增开源软件排行榜 TOP 100
2016 年度最受欢迎中国开源软件 TOP 20
2016 年度码云热门项目排行榜 TOP 50
2017 值得关注的十个开源项目
2017 最值得关注的十大 APP、Web 界面设计趋势
点击“阅读原文”查看更多精彩内容