Vue3のコンポーネントファイルサイズを検証する
Vue2のときはコンポーネントを切りすぎるとファイルサイズが増大すると言われていた
ではVue3になった今、実際どのくらいファイルサイズが変わるのか、検証してみる
まずはインストール
% npm init vite vue3-component-file-size -- --template vue npx: 6個のパッケージを3.688秒でインストールしました。 ✔ Select a framework: › vue ✔ Select a variant: › vue-ts
package.json
{ "name": "vue3-component-file-size", "private": true, "version": "0.0.0", "scripts": { "dev": "vite", "build": "vue-tsc --noEmit && vite build", "preview": "vite preview" }, "dependencies": { "vue": "^3.2.25" }, "devDependencies": { "@vitejs/plugin-vue": "^2.3.1", "typescript": "^4.5.4", "vite": "^2.9.5", "vite-plugin-compression": "^0.5.1", "vue-tsc": "^0.34.7" } }
vite.config.ts
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue()] })
このようなファイルが生成される
<template> <div> <h1>{{ h1 }}</h1> </div> </template> <script setup lang="ts"> import { ref } from 'vue'; const h1 = ref('App') </script>
このVueのコードをエンドポイントのApp.vueとして定義、各コンポーネントをimportすることでファイルサイズの変化を確認する
確認時は npm run build
で確認。devビルドではなくminifyされた状態で確認
% npm run build > vue3-component-file-size@0.0.0 build /Users/igayamaguchi/IdeaProjects/vue3-component-file-size > vue-tsc --noEmit && vite build vite v2.9.6 building for production... ✓ 9 modules transformed. dist/index.html 0.36 KiB dist/assets/index.530e5712.js 51.28 KiB / gzip: 20.64 KiB
この時点でのファイルサイズは51.28 KiB、gzipで20.64 KiB
npm run preview
でサーバーを立ち上げてSafariでファイルサイズを確認するとヘッダーが304 B、本文が21.16 KBとなっている
ビルド時の表示がKiB表記になっているが大体近しい数字になっている。少しだけずれているがまあ誤差の範囲として無視する
このファイルサイズがどのくらい増えるかを確認していく
scriptやstyleのありなしでもファイルサイズが変わるので以下の4つのコンポーネントを作ってみた
templateとscriptのコンポーネント
コンポーネント側
TemplateAndScript.vue
<template> <div> <h2 @click="handleClick">Component{{ id }}</h2> </div> </template> <script setup lang="ts"> const props = defineProps<{ id: number }>() function handleClick() { alert(`click ${props.id}`) } </script>
読み込み側
<template> <div> <h1>{{ h1 }}</h1> <TemplateAndScript :id="1" /> </div> </template> <script setup lang="ts"> import TemplateAndScript from './components/TemplateAndScript.vue'; import { ref } from 'vue'; const h1 = ref('App') </script>
ビルド結果
% npm run build > vue3-component-file-size@0.0.0 build /Users/igayamaguchi/IdeaProjects/vue3-component-file-size > vue-tsc --noEmit && vite build vite v2.9.6 building for production... ✓ 10 modules transformed. dist/index.html 0.36 KiB dist/assets/index.fcfb82ce.js 51.45 KiB / gzip: 20.72 KiB
- 51.28 KiB → 51.45 KiB = 0.17 KiB = 174.08 byte
- gzipで20.64 KiB → 20.72KiB = 0.08 KiB = 81.92 byte
templateとscriptとstyleのコンポーネント
コンポーネント側
TemplateAndScriptAndStyle.vue
<template> <div> <h2 :class="$style.title" @click="handleClick">Component{{ id }}</h2> </div> </template> <script setup lang="ts"> const props = defineProps<{ id: number }>() function handleClick() { alert(`click ${props.id}`) } </script> <style module> .title { color: red; } </style>
読み込み側
<template> <div> <h1>{{ h1 }}</h1> <TemplateAndScriptAndStyle :id="1" /> </div> </template> <script setup lang="ts"> import TemplateAndScriptAndStyle from './components/TemplateAndScriptAndStyle.vue'; import { ref } from 'vue'; const h1 = ref('App') </script>
ビルド結果
% npm run build > vue3-component-file-size@0.0.0 build /Users/igayamaguchi/IdeaProjects/vue3-component-file-size > vue-tsc --noEmit && vite build vite v2.9.6 building for production... ✓ 12 modules transformed. dist/index.html 0.42 KiB dist/assets/index.cdfc0d7e.css 0.03 KiB / gzip: 0.05 KiB dist/assets/index.d9a18fc7.js 51.64 KiB / gzip: 20.82 KiB
- 51.28 KiB → 51.64 KiB = 0.36 KiB = 368.64 byte
- gzipで20.64 KiB → 20.82 KiB = 0.18 KiB = 184.32 byte
ここでindex.htmlにも変化があり、かつCSSファイルが追加された
これはHTMLに <link rel="stylesheet" href="/assets/index.cdfc0d7e.css">
が追加されて、App.vueの描画に必要となるCSSファイルが出力されているだけ
今回の例だとCSSの中身は ._title_y99vv_2{color:red}
となっている
templateのみのコンポーネント
コンポーネント側
Template.vue
<template> <div> <h2>Title</h2> </div> </template>
読み込み側
<template> <div> <h1>{{ h1 }}</h1> <Template :id="1" /> </div> </template> <script setup lang="ts"> import Template from './components/Template.vue'; import { ref } from 'vue'; const h1 = ref('App') </script>
ビルド結果
% npm run build > vue3-component-file-size@0.0.0 build /Users/igayamaguchi/IdeaProjects/vue3-component-file-size > vue-tsc --noEmit && vite build vite v2.9.6 building for production... ✓ 11 modules transformed. dist/index.html 0.36 KiB dist/assets/index.05c6e5e0.js 51.49 KiB / gzip: 20.73 KiB
- 51.28 KiB → 51.49 KiB = 0.21 KiB = 215.04 byte
- gzipで20.64 KiB → 20.73KiB = 0.09 KiB = 92.16 byte
templateとstyleのコンポーネント
コンポーネント側
TemplateAndStyle.vue
<template> <div> <h2 :class="$style.title">Title</h2> </div> </template> <style module> .title { color: red; } </style>
読み込み側
<template> <div> <h1>{{ h1 }}</h1> <TemplateAndStyle :id="1" /> </div> </template> <script setup lang="ts"> import TemplateAndStyle from './components/TemplateAndStyle.vue'; import { ref } from 'vue'; const h1 = ref('App') </script>
ビルド結果
% npm run build > vue3-component-file-size@0.0.0 build /Users/igayamaguchi/IdeaProjects/vue3-component-file-size > vue-tsc --noEmit && vite build vite v2.9.6 building for production... ✓ 12 modules transformed. dist/index.html 0.42 KiB dist/assets/index.cdfc0d7e.css 0.03 KiB / gzip: 0.05 KiB dist/assets/index.3ee1a737.js 51.58 KiB / gzip: 20.78 KiB
- 51.28 KiB → 51.58 KiB = 0.3 KiB = 307.2 byte
- gzipで20.64 KiB → 20.78 KiB = 0.14 KiB = 143.36 byte
結果
各コンポーネントを追加した場合、以下のようにファイルサイズが追加される形となった
templateとscript | templateとscriptとstyle | templateのみ | templateとstyle |
---|---|---|---|
174.08 byte, gzip: 81.92 byte | 368.64 byte, gzip: 184.32 byte | 215.04 byte, gzip: 92.16 byte | 307.2 byte, gzip: 143.36 byte |
こうみると大体1コンポーネントを追加するたびに多い場合、最低360byte(gzipなら180byte)から追加される形になる模様
このファイルサイズなら結構細かくコンポーネントを切っても問題ないように思う
なんかVue2とかはもっとファイルサイズが大きかった気がする
おまけ
Dynamic import
読み込み側をDynamic importに変えてみる
<template> <div> <h1>{{ h1 }}</h1> <div> </div> <TemplateAndScriptAndStyle v-if="visible" :id="1" /> </div> </template> <script setup lang="ts"> import { ref, defineAsyncComponent } from 'vue'; const h1 = ref('App') const TemplateAndScriptAndStyle = defineAsyncComponent(() => import('./components/TemplateAndScriptAndStyle.vue')) const visible = ref(false) function importComponent() { visible.value = true } </script>
% npm run build > vue3-component-file-size@0.0.0 build /Users/igayamaguchi/IdeaProjects/vue3-component-file-size > vue-tsc --noEmit && vite build vite v2.9.6 building for production... ✓ 13 modules transformed. dist/index.html 0.36 KiB dist/assets/TemplateAndScriptAndStyle.5352c308.js 0.41 KiB / gzip: 0.32 KiB dist/assets/TemplateAndScriptAndStyle.ceb24f11.css 0.03 KiB / gzip: 0.05 KiB dist/assets/index.78270881.js 53.41 KiB / gzip: 21.56 KiB
- 51.28 KiB → 53.41 KiB = 2.13 KiB = 2181.12 byte
- gzipで20.64 KiB → 21.56 KiB = 0.92 KiB = 942.08 byte
Dynamic importで0.41 KiBのJSファイルと0.03 KiBのCSSが追加される形になった
TSX
TSXで記述した場合にサイズは変わるのか
import { FunctionalComponent } from 'vue' export const FC: FunctionalComponent<{ id: number }> = (props, ctx) => { function handleClick() { alert(`click ${props.id}`) } return <> <div onClick={handleClick}> <h2>Title{props.id}</h2> </div> </> }
<template> <div> <h1>{{ h1 }}</h1> <FC :id="1" /> </div> </template> <script setup lang="ts"> import { FC } from './components/TSX' import { ref } from 'vue'; const h1 = ref('App') </script>
% npm run build > vue3-component-file-size@0.0.0 build /Users/igayamaguchi/IdeaProjects/vue3-component-file-size > vue-tsc --noEmit && vite build vite v2.9.6 building for production... ✓ 10 modules transformed. dist/index.html 0.36 KiB dist/assets/index.639095d2.js 51.47 KiB / gzip: 20.73 KiB
- 51.28 KiB → 51.47 KiB = 0.19 KiB = 194.56 byte
- gzipで20.64 KiB → 20.73 KiB = 0.09 KiB = 92.16 byte
templateとscriptで実装した時とほとんど変わらない。10~20byteだけ変わるが誤差
2つのコンポーネントを読み込む場合
最初の検証はコンポーネントを1つだけ追加する形だった
そこに加えてもう一つコンポーネントを追加した場合のファイルサイズも確認してみる
通常
1つ目のコンポーネント。templateとscriptの時と同じ
<template> <div> <h2 @click="handleClick">Title{{ id }}</h2> </div> </template> <script setup lang="ts"> const props = defineProps<{ id: number }>() function handleClick() { alert(`click ${props.id}`) } </script>
2つ目のコンポーネント。1つ目のコンポーネントの文字列を少し変えただけ
<template> <div> <h2 @click="handleClick">Title2{{ id }}</h2> </div> </template> <script setup lang="ts"> const props = defineProps<{ id: number }>() function handleClick() { alert(`click2 ${props.id}`) } </script>
読み込み側
<template> <div> <h1>{{ h1 }}</h1> <TemplateAndScript :id="1" /> <TemplateAndScript2 :id="1" /> </div> </template> <script setup lang="ts"> import TemplateAndScript from './components/TemplateAndScript.vue'; import TemplateAndScript2 from './components/TemplateAndScript2.vue'; import { ref } from 'vue'; const h1 = ref('App') </script>
ビルド結果
% npm run build > vue3-component-file-size@0.0.0 build /Users/igayamaguchi/IdeaProjects/vue3-component-file-size > vue-tsc --noEmit && vite build vite v2.9.6 building for production... ✓ 11 modules transformed. dist/index.html 0.36 KiB dist/assets/index.07aa95e9.js 51.62 KiB / gzip: 20.73 KiB
1つしか読み込まなかった時と比べると51.45 KiB → 51.62 KiB(gzipで20.72 KiB → 20.73 KiB)とコンポーネントを追加しているのに全然ファイルサイズが増えていない!
つまり最初にコンポーネントを追加した場合数百byte追加されるが、2つ目以降は100~200byteくらいに収まり、しかもgzipだと10byteくらいになる
TSX
import { FunctionalComponent } from 'vue' export const FC: FunctionalComponent<{ id: number }> = (props, ctx) => { function handleClick() { alert(`click ${props.id}`) } return <> <div onClick={handleClick}> <h2>Title{props.id}</h2> </div> </> } export const FC2: FunctionalComponent<{ id: number }> = (props, ctx) => { function handleClick() { alert(`click2 ${props.id}`) } return <> <div onClick={handleClick}> <h2>Title2{props.id}</h2> </div> </> }
<template> <div> <h1>{{ h1 }}</h1> <FC :id="1" /> <FC2 :id="1" /> </div> </template> <script setup lang="ts"> import { FC, FC2 } from './components/TSX' import { ref } from 'vue'; const h1 = ref('App') </script>
% npm run build > vue3-component-file-size@0.0.0 build /Users/igayamaguchi/IdeaProjects/vue3-component-file-size > vue-tsc --noEmit && vite build vite v2.9.6 building for production... ✓ 10 modules transformed. dist/index.html 0.36 KiB dist/assets/index.0746aaec.js 51.67 KiB / gzip: 20.75 KiB
TSXもほぼ変わらない
結論
Vueコンポーネントを切りすぎて問題になる、ということはほぼないだろう
これからは設計的に綺麗になるなら小さなコンポーネントも積極的に分割を行っていきたい