转载

用2048个 SVG 节点做动画:这些 MVVM 框架谁的性能更好?

曾经想知道如果播放一个由 2048 个 SVG 节点组成的动画,使用哪个前端框架会更流畅?这里有一些 GIF 动画展示。

同样是 毕达哥拉斯树动画 ,同样是在2012年中的视膜屏 MacBook Pro 中。所有动画都使用 LICEcap 录制,使用常规设置,可以在 Chrome、Spotify、Emacs 中运行。点击 GIF 可以看到它的代码。

Angular 2 和 CycleJS 在 12 月 23 日加入

人们都想知道这个测试的重要性及其重要的原因。

很多 GIF 都能在它们的 GitHub 上找到链接。甚至可以复制库然后在本地运行,非常有趣。

用2048个 SVG 节点做动画:这些 MVVM 框架谁的性能更好?

实现者: 本文作者

用2048个 SVG 节点做动画:这些 MVVM 框架谁的性能更好?

实现者: Jason Miller,Preact 的创造者

用2048个 SVG 节点做动画:这些 MVVM 框架谁的性能更好?

实现者: Dominic Gannaway,Inferno 的创造者

用2048个 SVG 节点做动画:这些 MVVM 框架谁的性能更好?

实现者: Evan You,Vue 的创造者

用2048个 SVG 节点做动画:这些 MVVM 框架谁的性能更好?

实现者: Tero Parviainen,JavaScript 顾问

用2048个 SVG 节点做动画:这些 MVVM 框架谁的性能更好?

实现者: Wayne Maurer,Lambda IT 的创始人

感谢 Jason、Dominic 和 Evan 创建分支,同样感谢 Tero 和 Wayne 加入了他们自己的版本。如果有人能不使用框架,直接用原生 JavaScript 实现,那一定很酷。

让我们通过 代码 了解它是如何工作的。

用2048个 SVG 节点做动画:这些 MVVM 框架谁的性能更好?

在 Jason 和 Evan 的提示下,对鼠标事件进行节流能使 demo 更快。结果证明原始版本的动画树运行缓慢不是 React 自身的原因,而是每个刷新周期内太多的请求让渲染引擎不堪重负。

我尝试过对 requestAnimationFrame 进行节流,但是实际效果并不好。相比之下限制 React 重绘周期的方法就简单而有效。

onMouseMove(event) {
    if (this.running) return;
    this.running = true;     // calculate stuff     this.setState({
        heightFactor: scaleFactor(y),
        lean: scaleLean(x)
    });
    this.running = false;
}

先检查是否在进行更新,如果没有就手动更新。这个之所以生效是因为 React 的引擎是同步的。

要是没有  React Fiber,我觉得它可能会挂掉。¯/ (ツ)

用2048个 SVG 节点做动画:这些 MVVM 框架谁的性能更好?

Jason 用 preact-compat 层使得 Preact 看起来很像 React。这很有可能影响它的性能。

我喜欢 Preact 的示例是因为它使用异步渲染让效果更流畅。鼠标移动后,你能看到重绘周期滞后而产生的神奇效果。我很喜欢这效果。

代码实现: diff on github

在 package.json 中,他添加了 preact,preact-compat 和 React 库的 preact-compat 克隆 ,后者能让你不需要改变 imports。

他把无状态的 Pythagoras 功能组件转换成一个有状态的组件从而实现异步渲染。

// src/Pythagoras.jsexport default class {
    render(props) {
        return Pythagoras(props);
    }
}

并启用去抖动 异步渲染:

// src/index.js
import { options } from 'preact';
options.syncComponentUpdates = false;
 
//option 1:  rIC + setTimeout fallback
let timer;
options.debounceRendering = f => {
    clearTimeout(timer);
    timer = setTimeout(f, 100);
    requestIdleCallback(f);
};

我最喜欢 Preact 的部分是,它可以作为 React 的替代品,并且运行良好。从我目前的应用程序来看,它的性能优化会有不错的发展前景。

用2048个 SVG 节点做动画:这些 MVVM 框架谁的性能更好?

你可以用 Inferno 替代 React。Dominic 说这会影响性能,所以他就创建了新分支。你可以 在 github 上比对差异 。

Dominic 把所有相关的 react-scripts 改成 inferno-scripts。他同时也把 react 改成 inferno-beta36,这意味着我的CTO肯定不允许我在生产环境中使用它。

从上面看出,它最主要的是各种导入的改变——React 变成 Inferno,还把许多类方法改成绑定箭头函数。我不知道这是出于风格的选择还是 Inferno 的需要。

他也把基于字符串引用改成基于回调引用,Inferno 因为性能的原因不能使用基于字符串引用。取而代之,我们可以用 D3 来检测 SVG 上的鼠标位置。这比起我们自己弄简单很多。

// src/App.js
 
class App extends Component {
    // ...
    svgElemeRef = (domNode) => {
        this.svgElement = domNode;
    }
    // ...
    render() {
        // ..
 
    }

在 Pythagoras 核心组件上,他添加两个 Inferno 特殊属性:noNormalize 和 hasNonKeyedChildren.

从八天前的  issue 知道,noNormalize 是提高性能的一个基准, hasNonKeyedChildren 的作用尚不明确。我猜想这两个属性都是用来为虚拟 DOM diffing 算法优化性能。

用2048个 SVG 节点做动画:这些 MVVM 框架谁的性能更好?

这项工作的工作量比较大,是由Evan和树的创作者Phan An 完成的。

Vue 并没有打算模仿 React 的 API,它拥有自己的一套代码。我想通过 github 展示它们之间的差异,但这比较繁琐。我建议你去 Github 自行查看 。

你可以识别出 Pythagoras 核心组件 。Evan 使用了 transform-vue-jsx,使得 JSX 能在 Vue 里使用。

main.app 文件 在此,你可以点击查看然后理解其代码。

让我们来试一下吧。

把它分成 <template>,<script> 和 <style> 三个部分。这看起来有点像 JSX 或者 HTML,但实际上是模板带上了冒号前缀。

Vue 似乎采用了 React 的 put-it-all-together 组件化提示,但按语言拆分。虽然这看起来很简洁,但按照我以往的经验,实际操作其实很麻烦。

App 组件还是跟过去类似,但它使用 data() 来定义默认的状态,用 $refs 代替 this.refs,用一个 name 属性代替了命名类本身,components 属性定义子节点,用 methods 属性来定义类方法。

用2048个 SVG 节点做动画:这些 MVVM 框架谁的性能更好?

虽然我不是 Angular 的粉丝,但我得承认它的确很好使用。

我不知道为什么,也许 TypeScript 的那些类型检查在转译后增加了运行时额外开销?

显然代码是重写了,Tero 需要将代码迁移到 TypeScript,这真厉害,我可做不到。

我很好奇,编程语言的隔阂会怎样影响你在网上找的随机库的可重用性。

代码看起来似乎包含很多文件。而 App 是分成 app.module.ts,app.component.ts,app.component.html 和 app.component.css 几个文件。和 Pythagoras 一样。

当你看到 Angular 的 html 文件时,意味着 Angular 坚持了一个文件对应一种语言的传统。

<div class="App-header">
  <h2>This is a dancing Pythagoras tree</h2>
</div>
<p class="App-intro">
  <svg #svg
        [attr.width]="width"
        [attr.height]="height"
        style="border: 1px solid lightgray">
    <g app-pythagoras
       [w]="baseW"
       [heightFactor]="heightFactor"
       [lean]="lean"
       [x]="width / 2 - 40"
       [y]="height - baseW"
       [lvl]="0"
       [maxlvl]="currentMax" />
  </svg>
</p>

这在 HTML 中看起来很有趣。

我对模块和组件之间区别的还没有完全理解。似乎模块定义了某些确定的引入,子组件等。但是每个组件仍然会定义它自己的 CSS 和 模板导入。

也因为我对这一块内容的了解不够深入,在此不对其用例的好处做过多描述。

用2048个 SVG 节点做动画:这些 MVVM 框架谁的性能更好?

这在我的设备上显示很流畅,可能是因为我刚刚看完 Angular 版本的演示。

Wayne 把所有东西都转译成 TypeScript,但似乎不是 CycleJS 要求的那样。尽管如此,他能保持与原始文件相同的简单结构,这一点深得人心。

在此我无法详细说明 Wayne 因 TypeScript 和 CycleJS 做的一些改变,他没有使用类定义 CycleJS 组件,其结构更像是在学校学的闭包结构。

export function App(sources: Sources): Sinks {
    const factorAndLean$ = sources.DOM.select('#the-svg') //...
 
    const args$ = xs.combine(factorAndLean$, xs.periodic(500) //...
 
    const pythagoras$ = Pythagoras(args$);
 
    const vtree$ = pythagoras$.map(x =>
        div(Styles.App, [ // ...
 
    return {
        DOM: vtree$
    };
}

这需要时间适应。

原文  https://www.oschina.net/translate/animating-svg-nodes-react-preact-inferno-vue
正文到此结束
Loading...