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'
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.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で記述した場合にサイズは変わるのか
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だけ変わるが誤差
最初の検証はコンポーネントを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くらいになる
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コンポーネントを切りすぎて問題になる、ということはほぼないだろう
これからは設計的に綺麗になるなら小さなコンポーネントも積極的に分割を行っていきたい