Babelプラグインのしくみ
workletを自動でworklet化し、クロージャの依存を検出してくれるWorklets Babelプラグインが、裏で何をしているのかを見ていきます。本書がプラグインなしのweb構成で書かれてきた経験と絡めて、その役割を確かめます。
ここまでの章で、useAnimatedStyleやuseDerivedValueに依存配列を明示してきました。本書のデモがBabelプラグインなしのVite上で動いているから、という説明を何度か添えてきました。この最終章では、その「Babelプラグイン」が本来何を自動化しているのかを正面から見ていきます。プラグインが何をしてくれているかが分かると、これまで書いてきた注記の意味がはっきりします。
デモはありません。読み物として、しくみを確かめる章です。
プラグインは何をしているのか
Worklets Babelプラグインの仕事は、あなたのコードを、Workletランタイムで実行できる形に変換することです。具体的には、'worklet'ディレクティブが付いた関数を探し出し、シリアライズ可能なオブジェクトに変換します。第6部1章で見たworkletize(ワークレット化)そのものです。

プラグインは関数本体だけでなく、識別用のハッシュと取り込むクロージャもまとめて持ち運べる形へ変換します。
function foo() {
'worklet';
console.log('Hello from worklet');
}
この変換があるおかげで、関数がスレッドの境界を越えてUIランタイムへ持ち運べるようになります。プラグインがなければ、'worklet'ディレクティブはただの文字列リテラルのまま、何も起きません。
ディレクティブを書かずに済んでいる理由
ここで疑問がわきます。これまでuseAnimatedStyleに渡す関数に、私たちは'worklet'と書いていませんでした。なぜ動いていたのか。
答えが自動ワークレット化(autoworkletization)です。プラグインは、ReanimatedやGesture HandlerのAPIに渡されるコールバックを見つけると、'worklet'ディレクティブがなくても自動でworklet化します。
useAnimatedStyle(() => {
// 'worklet' と書かなくても、プラグインが自動で workletize する
return { width: 100 };
});
これはuseAnimatedStyleに限りません。プラグインはReanimatedやGesture Handlerの多くのAPIについて、コールバックを自動で判別します。だからこそ、私たちは大半の場面でworkletを意識せずに書けていたわけです。第1部で「多くの場面ではworkletを強く意識しなくても書けるように作られています」と述べたのは、この自動ワークレット化のことでした。
ワークレット化できるのは、関数宣言・関数式・アロー関数・オブジェクトのメソッドなど、JavaScriptの関数として素直に書かれたものです。
そして依存の検出
プラグインのもうひとつの重要な仕事が、クロージャの検出です。
第6部1章で見たとおり、workletが別のランタイムで実行されるとき、クロージャ(取り込んだ外側の変数)はコピーされて持ち運ばれます。では、その関数がどの変数を取り込んでいるのかを、誰が調べるのか。ネイティブ環境では、Babelプラグインがコードを静的に解析して、workletが参照している変数を自動で洗い出してくれます。
ここが、本書のデモに関わってくる部分です。本書のデモはVite上で動いていて、Worklets Babelプラグインを通していません。プラグインがないと、この依存の自動検出が効きません。そこでReanimatedは、web環境でプラグインを使わない場合に、依存配列を手で渡す道を用意しています。
// プラグインがあれば scale は自動検出される。
// 本書のようにプラグインなしの web では、依存配列で明示する。
const style = useAnimatedStyle(() => {
return { transform: [{ scale: scale.value }] };
}, [scale]);
これまでの章でuseAnimatedStyle・useDerivedValue・useAnimatedReactionに依存配列を添えてきたのは、まさにこの「プラグインが本来やってくれる依存検出」を人力で肩代わりしていたからです。プラグインがある通常のReact Nativeアプリでは、この配列は不要です。本書がプラグインなしの構成を選んだからこそ、普段は隠れているこの役割が表に出てきた、というわけです。
プラグインの名前が変わった話
導入まわりで一点、知っておくとよい変更があります。かつてreact-native-reanimated/pluginという名前だったこのプラグインは、react-native-workletsへ移され、Worklets Babelプラグインに改名されました。Reanimated 4では後方互換のためのエクスポートも残っていますが、babel.config.jsでは新しい名前を直接指定することが強く推奨されています。
plugins: [
- 'react-native-reanimated/plugin',
+ 'react-native-worklets/plugin',
],
Expo SDK 54以降のスターターテンプレートには、このプラグインが最初から含まれています。多くの場合、自分で追加する手間すらありません。
プラグインのオプション
プラグインはいくつかのオプションを受け取ります。日々の開発で触ることは少ないものの、名前だけでも知っておくと役に立つものがあります。
bundleMode: 第6部1章で触れたBundle Modeを有効にするstrictGlobal: workletの中のグローバル変数を、取り込みではなくランタイム自身のグローバルスコープから読むようにする(将来的に既定になる予定で、有効化が推奨されている)omitNativeOnlyData/substituteWebPlatformChecks: web向けにバンドルサイズを削るためのオプション
strictGlobalは、第6部1章の「グローバルスコープも別物」の話とそのまま対応しています。有効にすると、workletのglobalは実行先ランタイムのグローバルを直接参照し、別ランタイムの値がほしければローカル変数に取り込んでクロージャで渡す、という明示的な形になります。
オプションは他にも複数あります。全体像は付録の公式リソースとその先へ経由で公式リファレンスを参照してください。
自動ワークレット化の限界
便利な自動ワークレット化にも、プラグインが判別できないケースがあります。そういう場面では、自分で'worklet'ディレクティブを書く必要があります。
- 別ファイルやモジュールからimportした関数をworkletとして使うとき。プラグインはファイルをまたいだ解析までは行わないため、その関数側に手で
'worklet'を書きます。 - 式の結果として作られる関数(三項演算子で関数を選ぶなど)は自動判別されません。
- ホイスティングは効きません。workletは定義より前では使えないので、
useAnimatedStyle(foo)より後ろにfooを定義するとクラッシュします。
これらはいずれも、プラグインが「静的な解析」でワークレット化とクロージャ検出をしている、という性質から来る制約です。裏側のしくみを知っていれば、こうした制約も理由とともに納得できます。
第6部を終えて
第6部では、workletが動くランタイムの正体から始めて、測定・リアクション・カスタムイベント、そして最後にそれらを支えるBabelプラグインまでを見てきました。第1部で輪郭だけを描いた「しくみ」を、一段深いところまで掘り下げたことになります。
普段のアニメーション開発では、ここで扱った内容の多くは表に出てきません。プラグインが静かに肩代わりし、Reanimatedのフックがきれいに包んでくれているからです。それでも「この関数はどのランタイムで動き、何が自動化されているのか」という視点を持てるようになると、想定外の挙動に出会ったときの見通しが大きく変わります。それが、この最後の部で持ち帰ってほしいものです。