用2048個 SVG 節點做動畫:這些 MVVM 框架誰的性能更好?

分類:技術 時間:2017-01-13

曾經想知道如果播放一個由 2048 個 SVG 節點組成的動畫,使用哪個前端框架會更流暢?這里有一些 GIF 動畫展示。

同樣是 畢達哥拉斯樹動畫 ,同樣是在2012年中的視膜屏 MacBook Pro 中。所有動畫都使用 LICEcap 錄制,使用常規設置,可以在 Chrome、Spotify、Emacs 中運行。點擊 GIF 可以看到它的代碼。

Angular 2 和 CycleJS 在 12 月 23 日加入

人們都想知道這個測試的重要性及其重要的原因。

很多 GIF 都能在它們的GitHub 上找到鏈接。甚至可以復制庫然后在本地運行,非常有趣。

實現者: 本文作者

實現者: Jason Miller,Preact 的創造者

實現者: Dominic Gannaway,Inferno 的創造者

實現者: Evan You,Vue 的創造者

實現者: Tero Parviainen,JavaScript 顧問

實現者: Wayne Maurer,Lambda IT 的創始人

感謝 Jason、Dominic 和 Evan 創建分支,同樣感謝 Tero 和 Wayne 加入了他們自己的版本。如果有人能不使用框架,直接用原生 JavaScript 實現,那一定很酷。

讓我們通過 代碼 了解它是如何工作的。

在 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,我覺得它可能會掛掉。macr;\ (ツ) /macr;

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 =gt; {
    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) =gt; {
        this.svgElement = domNode;
    }
    // ...
    render() {
        // ..
 
    }

在Pythagoras 核心組件上,他添加兩個Inferno 特殊屬性:noNormalize和hasNonKeyedChildren.

從八天前的 issue 知道,noNormalize是提高性能的一個基準,hasNonKeyedChildren 的作用尚不明確。我猜想這兩個屬性都是用來為虛擬 DOM diffing 算法優化性能。

這項工作的工作量比較大,是由Evan和樹的創作者Phan An 完成的。

Vue 并沒有打算模仿 React 的 API,它擁有自己的一套代碼。我想通過 github 展示它們之間的差異,但這比較繁瑣。我建議你去 Github 自行查看

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

main.app 文件 在此,你可以點擊查看然后理解其代碼。

讓我們來試一下吧。

把它分成lt;templategt;,lt;scriptgt; 和 lt;stylegt; 三個部分。這看起來有點像JSX 或者HTML,但實際上是模板帶上了冒號前綴。

Vue 似乎采用了 React 的 put-it-all-together 組件化提示,但按語言拆分。雖然這看起來很簡潔,但按照我以往的經驗,實際操作其實很麻煩。

App 組件還是跟過去類似,但它使用data() 來定義默認的狀態,用 $refs 代替 this.refs,用一個 name 屬性代替了命名類本身,components 屬性定義子節點,用methods 屬性來定義類方法。

雖然我不是 Angular 的粉絲,但我得承認它的確很好使用。

我不知道為什么,也許 TypeScript 的那些類型檢查在轉譯后增加了運行時額外開銷?

顯然代碼是重寫了,Tero 需要將代碼遷移到 TypeScript,這真厲害,我可做不到。

我很好奇,編程語言的隔閡會怎樣影響你在網上找的隨機庫的可重用性。

代碼看起來似乎包含很多文件。而 App 是分成 app.module.ts,app.component.ts,app.component.html和app.component.css 幾個文件。和Pythagoras一樣。

當你看到Angular 的 html 文件時,意味著Angular 堅持了一個文件對應一種語言的傳統。

lt;div class=quot;App-headerquot;gt;
  lt;h2gt;This is a dancing Pythagoras treelt;/h2gt;
lt;/divgt;
lt;p class=quot;App-introquot;gt;
  lt;svg #svg
        [attr.width]=quot;widthquot;
        [attr.height]=quot;heightquot;
        style=quot;border: 1px solid lightgrayquot;gt;
    lt;g app-pythagoras
       [w]=quot;baseWquot;
       [heightFactor]=quot;heightFactorquot;
       [lean]=quot;leanquot;
       [x]=quot;width / 2 - 40quot;
       [y]=quot;height - baseWquot;
       [lvl]=quot;0quot;
       [maxlvl]=quot;currentMaxquot; /gt;
  lt;/svggt;
lt;/pgt;

這在 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$
    };
}

這需要時間適應。


Tags: SVG MVVM模式

文章來源:https://www.oschina.net/translate/animating-svg-no


ads
ads

相關文章
ads

相關文章

ad