设为首页收藏本站

LUPA开源社区

 找回密码
 注册
文章 帖子 博客
LUPA开源社区 首页 业界资讯 开源资讯 查看内容

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

2017-1-13 22:12| 发布者: joejoe0332| 查看: 1262| 评论: 0|原作者: tv_哇, 无若, 边城, Tocy|来自: oschina

摘要: 曾经想知道如果播放一个由 2048 个 SVG 节点组成的动画,使用哪个前端框架会更流畅?这里有一些 GIF 动画展示。同样是毕达哥拉斯树动画,同样是在2012年中的视膜屏 MacBook Pro 中。所有动画都使用 LICEcap 录制,使 ...
曾经想知道如果播放一个由 2048 个 SVG 节点组成的动画,使用哪个前端框架会更流畅?这里有一些 GIF 动画展示。

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

Angular 2 和 CycleJS 在 12 月 23 日加入 —

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

快速跳转:ReactPreactInfernoVueAngular 2CycleJS总结

预览

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

实现者: 本文作者

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

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

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

实现者: Tero Parviainen,JavaScript 顾问

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

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

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

React


在 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,我觉得它可能会挂掉。¯\(ツ)

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 的替代品,并且运行良好。从我目前的应用程序来看,它的性能优化会有不错的发展前景。

你可以用 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 算法优化性能。


虽然我不是 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 和 模板导入。

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

这在我的设备上显示很流畅,可能是因为我刚刚看完 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 =&gt;
        div(Styles.App, [ // ...
 
    return {
        DOM: vtree$
    };
}

这需要时间适应。

我不喜欢的部分是 Wayne 在 CycleJS 中指定 DOM 的方式。主程序看起来像是这样:

div(Styles.App, [
            div(Styles.AppHeader, [
                img(Styles.AppLogo, { attrs: { src: 'cyclejs_logo.svg' } }),
                h2('This is a dancing Pythagoras tree')
            ]),
            p(Styles.AppIntro, [
                svg('#the-svg', { attrs: { height: svgDimensions.height, width: svgDimensions.width, style: 'border: 1px solid lightgray' } }, [ x ])
            ])
        ])

这似乎与使用 React.createElement 方法非常类似,但可读性较差。 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 了。


酷毙

雷人

鲜花

鸡蛋

漂亮
  • 快毕业了,没工作经验,
    找份工作好难啊?
    赶紧去人才芯片公司磨练吧!!

最新评论

关于LUPA|人才芯片工程|人才招聘|LUPA认证|LUPA教育|LUPA开源社区 ( 浙B2-20090187 浙公网安备 33010602006705号   

返回顶部