OvtoにPureComponentを実装した
Ovtoとは
Rubyで書かれたフロントエンドのフレームワーク。 Hyperappをベースにしていて、VDOMを使っている。
https://magazine.rubyist.net/articles/0059/0059-Ovto.html とかを読むのが良いと思います。
PureComponentとは
Reactとかにあるそれとだいたい同じで、コンポーネントに渡る引数が変わらない場合にrender
メソッドの呼び出しを省略できる。
たとえば次の例だと、Pure#render
はstate.foo
が更新された時にだけ呼ばれるが、NotPure#render
はstate.foo
が更新されてもstate.bar
が更新されても呼ばれる。
つまり、頻繁に更新されるstateがあるけどそれに依存していないコンポーネントがある場合、render
メソッドの呼び出しを省略することで高速化が期待できる。
def render o 'div' do o Pure, foo: state.foo o NotPure, bar: state.bar end end
ちなみに、PureComponent
内ではstateにアクセスできない。
同一性の比較
なお、render
の引数が変化していることはObject#==
を使って比較される。つまり浅い比較を行っているReactのPureComponentとは異なっている。
これはPull Request上でもyharaさんから「Object#equal?
を使っては?」とコメントをもらっていた1のだけど、やるなら結構頑張って実装する必要がありそうだったため見送った。
ただ浅い比較をした方が速くできるだろうし、それが期待する仕様であるようにも思えるので、パフォーマンスに困ったりしたら浅い比較を実装しても良いと思う。 これぐらいの仕様変更はまだ許される段階ではないだろうか。Ovtoのstateはimmutableに扱うことが求められているような気配がするので、浅い比較をするようになっても実際問題はなさそうな気がする。
実装の経緯
開発中のパズルアプリケーション2で、PCでは高速にレンダリングされていたのがスマートフォンでは遅いという問題があった。
これのパフォーマンス改善をしている中でPureComponent
が実装された。
ただ、この問題の原因はrender
メソッドの呼び出し速度ではなく、ダブルタップ判定のためのdelayだった。
https://twitter.com/p_ck_/status/1151884133259345926
つまり、今回の問題においてはPureComponent
にすることは直接の問題の解決にはなっていない。
これは早すぎる最適化とも言えると思う。とはいえもう少し巨大なアプリケーションがもう少し遅いマシンの上で走ると充分効果が出るのではと予測ができるので、(せっかく実装してしまったし) Pull Requestにして本家に機能提案することにした。
慣れないWebフロントエンドのパフォーマンスチューニングでプロファイリングをおろそかにしてしまったが、ちゃんとプロファイリングしないといけないという教訓を得た。