Closed16

Vue 3 で Option API を無効化した場合のバンドルサイズの差異を調べたい

shingo.sasakishingo.sasaki

Vue の README によると、Vue 3 では __VUE_OPTIONS_API__ というフィーチャーフラグをビルドタイムに注入すると、 Option API の機能を無効化することが出来るらしい。これによって Option API に関するコードがバンドルからツリーシェイキングされ、Vue本体の軽量化が出来るようなので、その実態を調べてみることにする。

https://github.com/vuejs/core/blob/main/packages/vue/README.md#with-a-bundler

shingo.sasakishingo.sasaki

この設定は webpack や rollup のバンドラから注入可能だが、Vite はバンドルに rollup を使用しているため、今回は Vite プロジェクトで試す。

shingo.sasakishingo.sasaki

TS 用の defineComponent を使うとまた事情が変わってきそうな気配がするので、今回は JS を使って、export deafult でピュアオブジェクトを返す方式で試す。

shingo.sasakishingo.sasaki

プロジェクトは create-vite で作成する。

$ yarn create vite vue-3-optout-option-api --template vue

今回はバンドル生成が目的なので、開発用サーバーは使わずに、ビルドとプレビューで動作を確認する。

$ yarn install
$ yarn build
$ yarn preview

いつもの

shingo.sasakishingo.sasaki

rollup-plugin-visualizer 導入する。

yarn add -D rollup-plugin-visualizer

vite から使う場合はプラグイン注入の形式で

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { visualizer } from "rollup-plugin-visualizer";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue(), visualizer()],
});

ビルドする

yarn build

stats.html が生成される

ミニマムプロジェクトなので当然 Vue 本体が大半を締めてるけど、211.96KB であることがわかる。

shingo.sasakishingo.sasaki

無効化する。Vite の場合は define オプションからビルド時の環境変数を設定できる模様。

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { visualizer } from "rollup-plugin-visualizer";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue(), visualizer()],
  define: {
    __VUE_OPTIONS_API__: false,
  },
});

これで再度ビルドする

190.0KB

shingo.sasakishingo.sasaki

ので、Vite のログに基づく実際のファイルサイズで改めて比較する。

__VUE_OPTIONS_API__: true,

$ yarn build
yarn run v1.22.19
$ 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
✨  Done in 0.78s.

__VUE_OPTIONS_API__: false,

$ yarn build
yarn run v1.22.19
$ 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
✨  Done in 0.68s.

バンドルされたJSファイルのサイズは 54.21KB から 48.72KB に。6KB の削減(gzip だと2KB 程度)

shingo.sasakishingo.sasaki

ちなみに弊社サービスのメインバンドルサイズは Brotli 形式で 178kB なので、限りなく誤差に近いことはわかってきた。

shingo.sasakishingo.sasaki

create-vite で作成されたコンポーネントは以下のように composition API で書かれてる。

<script setup>
import { ref } from 'vue'

defineProps({
  msg: String,
})

const count = ref(0)
</script>

これを雑に Option API に書き直す

<script>
export default {
  data: () => ({
    count: 0,
  }),
  props: {
    msg: String,
  },
};
</script>

無効化した状態だとビルドも成功するコンソールエラーも出ないけど、Option API で書いたコンポーネントがリアクティブにならなくなってる。

shingo.sasakishingo.sasaki

結局何が除外されたのかだけ確認して終わろう。

__VUE_OPTIONS_API____FEATURE_OPTIONS_API__ っていうフィーチャーフラグに書き換えられる
https://github.com/vuejs/core/blob/fe77e2bddaa5930ad37a43fe8e6254ddb0f9c2d7/rollup.config.mjs#L279

以下みたいに、不要な Option API の追加を防いでる
https://github.com/vuejs/core/blob/fe77e2bddaa5930ad37a43fe8e6254ddb0f9c2d7/packages/runtime-core/src/componentPublicInstance.ts#L251

shingo.sasakishingo.sasaki

そういえばサードパーティライブラリが Option API を使ってたら、無効化しても基本的に駄目だよね。

Vue 3 向けだけど Option API を使ってる自分のパッケージで試す。
https://github.com/s-sasaki-0529/vue-slider-component

当然といえば当然、無効化した場合は何も表示されなくなる。このときエラーも出ないのが辛い。

shingo.sasakishingo.sasaki

vuetify 3 は全部 composition API で書かれてるかなぁ。

https://next.vuetifyjs.com/en/getting-started/installation/

$ yarn add vuetify@^3.0.6

雰囲気でセットアップ

import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
import "vuetify/styles";
import { createVuetify } from "vuetify";
import * as components from "vuetify/components";
import * as directives from "vuetify/directives";

const vuetify = createVuetify({
  components,
  directives,
});

createApp(App).use(vuetify).mount("#app");

適当にインタラクティブなコンポーネント(カルーセル) を使ってみる。

<template>
  <div class="wrapper">
    <v-carousel>
      <v-carousel-item
        src="https://cdn.vuetifyjs.com/images/cards/docks.jpg"
        cover
      ></v-carousel-item>

      <v-carousel-item
        src="https://cdn.vuetifyjs.com/images/cards/hotel.jpg"
        cover
      ></v-carousel-item>

      <v-carousel-item
        src="https://cdn.vuetifyjs.com/images/cards/sunshine.jpg"
        cover
      ></v-carousel-item>
    </v-carousel>
  </div>
</template>

これは無効化しても動いた!

なぜなら Composition API だけで実装されてるから。
https://github.com/vuetifyjs/vuetify/blob/next/packages/vuetify/src/components/VCarousel/VCarousel.tsx

shingo.sasakishingo.sasaki

だいたい把握して計測もできたので終わり。
一応整理して記事化するかも。

このスクラップは2022/12/22にクローズされました