Introduction
In the world of web development, optimizing bundle size can have significant impacts on performance. This article explores how porting a portfolio landing page to Vue 3 resulted in a 41% smaller gzip size compared to its React counterpart.
The Experiment Setup
The experiment maintained identical specifications, data, and CSS as the original React version. The only change was in the component layer, ensuring that the shared code, including types.ts, filter.ts, and data.ts, remained byte-identical. This controlled approach allowed for a clean comparison of framework overheads.
Vue 3's Efficient Syntax
Utilizing Vue 3 with the Composition API and <script setup> provided a streamlined component syntax. This setup eliminated the need for multiple useState calls and dependency arrays required in React, simplifying state management and reducing potential errors.
<script setup lang="ts">
import { ref, computed, watchEffect } from 'vue'
import type { PortfolioData, Entry, Lang } from './types'
// ... other imports and setup
</script>
URL Sync Made Simple
Vue's two-way binding with v-model combined with watchEffect facilitated seamless URL synchronization, removing the cumbersome declaration requirements of React.
<template>
<input type="text" v-model="filter.query" :placeholder="m.searchPlaceholder" />
<select v-model="filter.category">
<!-- options -->
</select>
</template>
Understanding the Byte Reduction
The significant reduction in gzip size is attributed to Vue's proxy-based reactivity and compile-time optimizations. In contrast to React's virtual DOM, Vue's model precomputes the exact DOM updates needed, leading to denser compiled output.
Component Structure
Vue's Single File Components (SFCs) allow for cohesive organization of template, script, and styles in one file, enhancing maintainability and type inference, especially with TypeScript.
Consistent Shared Code
The experiment confirmed the byte-identical nature of the shared code across frameworks, ensuring the observed differences were solely due to the rendering layer.
Testing Frameworks
Tests were consistently executed using Vitest, validating that the logic remained unchanged across different frameworks.
test('filters by category', () => {
const r = filterAndSort(entries, { ...defaults, category: 'dev-tool' }, 'en')
assert.ok(r.every((e) => e.category === 'dev-tool'))
})
Conclusion
Porting to Vue 3 demonstrates a substantial reduction in bundle size without compromising functionality. The next phase will explore further optimizations with Svelte 5, promising even smaller sizes.