Vue 3 で Options API を無効化するという選択肢
概要
本記事は、Vue 3 で Options API を使用するためのフラグである __VUE_OPTIONS_API__
を無効化した場合の挙動やバンドルサイズの違いについてまとめになります。
TL;DR
Options API
を一切使わないプロジェクトなら、Vue アプリケーションの バンドルサイズを 5.49kB 削減 できました (gzip なら 2.14 kB)
バージョン情報
-
vue
3.2.45 -
vite
4.0.2 -
@vitejs/plugin-vue
4.0.0 -
rollup
3.8.0
Options API と Composition API
Vue 3 のコンポーネントスタイルには、 Options API
と Composition API
の2種類があります。
前者は Vue 2 時点での基本スタイルで、オブジェクトにコンポーネントの挙動を示す各フィールドを定義します。
後者は Vue 3 から登場した新スタイルで、リアクティブに関する API を使用し、手続き的にコンポーネントを定義します。 <script setup>
というマクロ記法と合わせることで、 Svelte
に近いテンプレートバインディングを自然に行うことができます。
// Options API
export default {
data() {
return {
count: 0
}
},
methods: {
increment() {
this.count++
}
},
computed: {
doubleCount() {
return this.count *2
}
}
}
// Composition API
import { ref, computed, onMounted } from 'vue'
const count = ref(0)
count doubleCount = computed(() => count * 2)
function increment() {
count.value++
}
Options API
と Composition API
はコンポーネントによって使い分けることも、一つのコンポーネントで同時に使用することもできます。
関連リンク
__VUE_OPTIONS_API__
__VUE_OPTIONS_API__
(以下 機能フラグ
) は、Vue 3.0.0-rc.3 から追加された、バンドラ向けのグローバル変数です。
機能フラグは webpack
や Rollup
のようなバンドラから設定可能で、デフォルトでは true
ですが、これを false
にすることで Options API
の機能を完全に無効化することができます。
Options API
を無効化することで、それに関するコードがツリーシェイキングの対象となり、最終的なバンドルサイズを削減することができます。
関連リンク
create-vite
で動作確認用プロジェクトを作成
ここでは create-vite
を用いてスキャフォルドされたミニマムプロジェクトを用いて、機能フラグの設定による挙動の違いを確認します。
$ yarn create vite vue-3-disable-option-api --template vue
$ cd vue-3-disable-option-api
$ yarn install
Vite
は開発用サーバーではバンドラが作成されませんが、Rollup
を用いたバンドルのビルドが可能なため、以下のように動作確認をします。
$ vite build
$ vite preview
リアクティブなカウンターが表示されるサンプルアプリケーションが起動しました。
機能フラグを設定する
vite
の場合、vite.config.js
からグローバル変数の注入ができるため、ここで __VUE_OPTIONS_API__
を設定します。
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
export default defineConfig({
plugins: [vue()],
define: {
__VUE_OPTIONS_API__: true, // Options API を有効化 (デフォルト)
},
});
この状態でビルドをすると、バンドル(dist/assets/index-f59c7c2f.js
) は 54.21kB であることがログからわかります。
$ vite build
vite v4.0.3 building for production...
✓ 16 modules transformed.
dist/index.html 0.45 kB
dist/assets/vue-5532db34.svg 0.50 kB
dist/assets/index-351bd726.css 1.29 kB │ gzip: 0.66 kB
dist/assets/index-f59c7c2f.js 54.21 kB │ gzip: 21.89 kB
続けて、機能フラグを無効にしてみます。
export default defineConfig({
plugins: [vue()],
define: {
__VUE_OPTIONS_API__: false, // 無効化
},
});
再度ビルドすると、バンドルサイズが 48.72 kB となり、 5.49 kB 削減できた ことがわかります。
$ vite build
vite v4.0.3 building for production...
✓ 16 modules transformed.
dist/index.html 0.45 kB
dist/assets/vue-5532db34.svg 0.50 kB
dist/assets/index-351bd726.css 1.29 kB │ gzip: 0.66 kB
dist/assets/index-2809b276.js 48.72 kB │ gzip: 19.75 kB
機能フラグが無効の場合の挙動
create-vite
でスキャフォルドされたコンポーネントは以下のように Composition API
で書かれているため、機能フラグの有無に関わらず動作しました。
<script setup>
import { ref } from 'vue'
defineProps({
msg: String,
})
const count = ref(0)
</script>
これを Options API
を使うように書き換えます。
<script>
export default {
data: () => ({
count: 0,
}),
props: {
msg: String,
},
};
</script>
機能フラグが有効な場合は動作しますが、無効にするとカウンターの部分が動作動作しなくなる(≒テンプレートにバインドされなくなる)ことがわかります。
しかし、コンソールエラーが出ているわけでもクリックするとクラッシュするわけでもありません。
これがどういうことか、 Vue のコードを追って確認してみましょう。
Vue は機能フラグをどう見ているか
軽く調べてみると、 __VUE_OPTIONS_API__
の設定は、バンドル前のコードでは __FEATURE_OPTIONS_API__
という変数に埋め込まれているようです。
__FEATURE_OPTIONS_API__
を使用している箇所を一つ見てみましょう。以下は Vue インスタンのパブリックメソッドのマッピングです。
$watch
に対応するメソッドが instanceWatch
で、 Options API
における this.$watch
に該当します。
この関数は前述の componentPublicInstance
モジュールからしか参照されないため、機能フラグが無効な場合は
$watch: i => (false ? instanceWatch.bind(i) : NOOP)
に展開され、 instanceWatch
が到達不能コードとなり、ツリーシェイキングが成立します。
こういったコード分岐が Vue に仕込まれているおかげで、機能フラグの設定によってバンドルサイズが変わることがわかりました。
また、条件分岐先にある NOOP
は以下のように何もしない関数として定義されています。
そのため、機能フラグを無効化した状態で Options API
を使用しても、空の関数が呼び出されるだけに書き換わるため、 Uncaught TypeError
のようなコンソールエラーが発生しなかったというわけですね。
機能フラグの注意点
Vue プロジェクトにて Options API
を一切使用していない場合、機能フラグを無効化することで無条件にバンドルサイズを削減することができたり、意図せず Options API
を使用してしまうことを避けることができます。
ドキュメントにも
it is strongly recommended to properly configure them in order to get proper tree-shaking in the final bundle.
と書かれているため、積極的に利用しても良いと思われます。
一方で、機能フラグを無効化した場合は、 Options API を利用した外部コンポーネントが利用できない という大きなデメリットがあります。
例えば以下のパッケージは Vue 3 向けに作成したコンポーネントではありますが、Vue 2 版を簡易移植しただけなので Options API
を利用して書かれています。
そのため、機能フラグを有効にしなければ上記パッケージは利用できないという、ライブラリ選定の足かせにもなります。
とはいえ Vuetify や vee-validate, vue-chartjs といった主要ライブラリを見る限りは、 Composition API
で書かれているものがほとんどのため、大きな問題はないかもしれません。
コードの一例
まとめ + 所感
本記事では、Vue 3 における機能フラグである __VUE_OPTIONS_API__
の挙動について調査した結果をまとめました。
結果として、劇的なバンドルサイズの削減とまでは言えませんでしたが、 Options API
を使用しないプロジェクトであれば有用であると考えられます。
一方で、外部ライブラリの選定の足かせになりえることや、バンドルサイズの削減幅も限られていることから、 Options API
で書かれた既存のコードをリプレイスしてまで機能フラグを無効化する価値もなさそうでした。
とはいえ、 <script setup>
や Reactivity Transform といった、Composition API
をさらに強化するための仕組みが今後もどんどん追加されることを考えると、 Composition API
をベースとした開発を行う方向に寄せるほうが良いのかなとも個人的に思っています。
Discussion