LoginSignup
2
1

More than 3 years have passed since last update.

変わらないReact VDOMをBabelで最適化

Posted at

不思議なもので、少し前に悩んでいたことを解決するためのBabelプラグインにちょうどめぐりあうことができました。

JSXの中身

Reactを書くときに使うJSXですが、実際にJavaScriptとして処理する際にはReact.createElementという関数(リファレンス)に変換されます。

// before

(<p>
  文章
  <a href="https://qiita.com/">Qiita</a>
</p>)

// after

React.createElement("p", null, 
  "文章", 
  React.createElement("a", { href: "https://qiita.com/" }, 
    "Qiita"
  )
);

<br />のような表記を見ればリテラルのようにも思えますが、実態としては関数呼び出しになっています。さらに、React.createElementの中身は引数チェックをして、与えられた引数をオブジェクトに詰め込む程度のシンプルなもので、特にキャッシュなどはなされないので、<br />のようなごくシンプルなVDOMエレメントであっても、呼び出すたびに新しいものが作られます。

毎回生成するデメリット

もちろん、React.createElementは、本物のDOMの生成と比べれば、ずっと軽い処理です。とはいえ、全く同じ仮想DOMを何度も生成すると、以下のような点で無駄が発生します。

  • React.createElementの実行時間
  • 各回ごとに別なオブジェクトが生成するので、
    • メモリを消費し、ガベージコレクタにより負荷をかける
    • 比較しても一致しないので、そのVDOMを別なコンポーネントの引数にした場合にReact.memoが効かなくなる

自分が少し前にした投稿では、最後のデメリットが気になったのでした。

Babelで解決

何気なくBabelのChangeLogを読んでいたところ、@babel/plugin-transform-react-constant-elementsというプラグインの存在を知りました。これは、JSXが同じスコープ内の変数を使っていない場合に。できるだけ上位のスコープ(完全に何も変わらない場合はトップスコープ)まで引き上げる、というものです、

書かれていた例
// before
const Hr = () => {
  return <hr className="hr" />;
};

// after
const _ref = <hr className="hr" />;

const Hr = () => {
  return _ref;
};

手動でこのような作業をやっていたので、喜び勇んでyarn addしたところで、望み通りの結果が得られるようになりました。ただ、(もちろん有害になることではなさそうとはいえ)childrenの中身の一部で変化しないエレメントも外側にくくりだされていて、けっこう積極的に処理しているんだなと感じた次第でした。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1