π Vue.js Production Ready: SSR and Performance
The Restaurant Kitchen Story π³
Imagine you own a restaurant. Every time a customer orders food, you have two choices:
- Cook everything from scratch at the table (slow, customer waits)
- Prepare dishes in the kitchen first, then serve (fast, customer happy!)
This is exactly how SSR (Server-Side Rendering) vs CSR (Client-Side Rendering) works in Vue.js!
π½οΈ SSR vs CSR: Kitchen vs Table Service
What is CSR (Client-Side Rendering)?
Think of CSR like giving your customer raw ingredients and a recipe. They cook at their table!
// CSR: Browser does ALL the work
// 1. User opens website
// 2. Gets empty HTML + JavaScript
// 3. Browser runs JavaScript
// 4. Page finally appears!
The Problem? Customer sees empty plate first. Then waits. And waitsβ¦
What is SSR (Server-Side Rendering)?
SSR is like cooking in the kitchen FIRST, then serving a ready meal!
// SSR: Server does the work
// 1. User opens website
// 2. Server runs Vue code
// 3. Sends COMPLETE HTML page
// 4. User sees content INSTANTLY!
The Magic? Customer sees a beautiful dish immediately!
graph TD A["User Opens Website"] --> B{Which Method?} B -->|CSR| C["Empty Page"] C --> D["Download JavaScript"] D --> E["Browser Builds Page"] E --> F["User Finally Sees Content"] B -->|SSR| G["Server Builds Page"] G --> H["User Sees Content Fast!"]
Real Example: Nuxt.js (Vueβs SSR Friend)
// nuxt.config.js
export default {
// Enable SSR mode
ssr: true,
// Your page is ready
// BEFORE it reaches the user!
}
When to use what?
| Use CSR | Use SSR |
|---|---|
| Dashboard apps | Blog/News sites |
| Behind login | E-commerce |
| Fast internet users | SEO important |
π§ Hydration: Waking Up the Page
The Sleeping Beauty Story
Remember when SSR sends a complete HTML page? That page is like Sleeping Beauty - it looks beautiful but canβt move!
Hydration is the magic kiss that wakes it up!
graph TD A["Server sends HTML"] --> B["Page looks ready"] B --> C[But buttons don't work yet!] C --> D["Vue loads JavaScript"] D --> E["Hydration happens"] E --> F["Page is ALIVE! Buttons work!"]
What Actually Happens?
// The server sent this HTML:
<button>Click Me!</button>
// But it's just a picture of a button!
// Hydration connects Vue to make it work:
createSSRApp(App).mount('#app')
// Now clicking actually does something!
The Golden Rule
The server HTML and Vueβs expected HTML must match exactly!
// β BAD: Different on server vs client
<p>{{ new Date() }}</p>
// Server: "Dec 30, 10:00:00"
// Client: "Dec 30, 10:00:01"
// MISMATCH! Hydration fails!
// β
GOOD: Same everywhere
<p>{{ userData.name }}</p>
// Server: "Alex"
// Client: "Alex"
// Perfect match!
βοΈ Code Splitting: Donβt Carry Everything!
The Backpack Story
Imagine going to school. Would you carry:
- ALL your textbooks for the ENTIRE year? ππ
- Or just TODAYβs books? ππ
Code Splitting = Only carry what you need RIGHT NOW!
How Vue Does It
// β Without Code Splitting
// User downloads EVERYTHING at once
// Even pages they'll never visit!
import HomePage from './HomePage.vue'
import AboutPage from './AboutPage.vue'
import ContactPage from './ContactPage.vue'
// Downloads: 500KB at start
// β
With Code Splitting
// Downloads only when needed!
const HomePage = () => import('./HomePage.vue')
const AboutPage = () => import('./AboutPage.vue')
// Downloads: 100KB at start
// Other pages load when visited
Vue Router Magic
// router/index.js
const routes = [
{
path: '/',
component: () => import('../views/Home.vue')
},
{
path: '/about',
component: () => import('../views/About.vue')
}
]
// Each page = separate chunk
// Loaded only when user visits!
graph TD A["User Opens App"] --> B["Load Home Page Only"] B --> C{User Clicks About?} C -->|Yes| D["Load About Page"] C -->|No| E["No Extra Download!"]
π³ Tree Shaking: Removing Dead Leaves
The Garden Story
Imagine you have a huge tree. Some branches are dead - they have no leaves, no fruit. Do you keep them?
NO! You shake the tree and dead branches fall off!
Tree Shaking does the same for your code!
How It Works
// lodash-library.js (Huge library!)
export function add(a, b) { return a + b }
export function subtract(a, b) { return a - b }
export function multiply(a, b) { return a * b }
export function divide(a, b) { return a / b }
// ... 300 more functions
// your-app.js
import { add } from 'lodash-library'
// You only USE add()!
// BEFORE Tree Shaking:
// Bundle = ALL 300+ functions (huge!)
// AFTER Tree Shaking:
// Bundle = ONLY add() function (tiny!)
Vue 3βs Tree Shaking Power
// β
Vue 3: Only imports what you use!
import { ref, computed } from 'vue'
// Bundle only includes ref + computed
// β Vue 2 way (everything included)
import Vue from 'vue'
// Bundle includes ENTIRE Vue library
Make Tree Shaking Work
// β
Named imports (tree-shakeable)
import { Button } from 'my-ui-lib'
// β Default imports (NOT tree-shakeable)
import MyUILib from 'my-ui-lib'
const { Button } = MyUILib
π΄ Lazy Loading Components: Wake Up When Needed!
The Lazy Cat Story
Imagine a cat sleeping on the couch. Does it wake up for no reason?
NO! It only wakes up when you shake the treat bag!
Lazy Loading = Components sleep until you actually need them!
Basic Lazy Loading
// β Eager Loading (loads immediately)
import HeavyChart from './HeavyChart.vue'
// β
Lazy Loading (loads when used)
const HeavyChart = defineAsyncComponent(
() => import('./HeavyChart.vue')
)
With Loading States
import { defineAsyncComponent } from 'vue'
const HeavyChart = defineAsyncComponent({
// The actual component
loader: () => import('./HeavyChart.vue'),
// Show while loading
loadingComponent: LoadingSpinner,
// Wait before showing spinner
delay: 200,
// Show if loading fails
errorComponent: ErrorDisplay,
// Give up after 30 seconds
timeout: 30000
})
Real World Example
// Only load modal when button clicked!
<template>
<button @click="showModal = true">
Open Settings
</button>
<SettingsModal v-if="showModal" />
</template>
<script setup>
import { ref } from 'vue'
// Lazy! Only downloads when
// showModal becomes true
const SettingsModal = defineAsyncComponent(
() => import('./SettingsModal.vue')
)
const showModal = ref(false)
</script>
graph TD A["Page Loads"] --> B["Modal NOT downloaded"] B --> C{User clicks button?} C -->|No| D["Saved bandwidth!"] C -->|Yes| E["Download Modal NOW"] E --> F["Show Modal"]
π¦ Bundle Size Optimization: Pack Light, Travel Fast!
The Suitcase Story
Youβre going on a weekend trip. Would you bring:
- A truck full of clothes? π (Takes forever to load!)
- One small bag? π (Quick and easy!)
Bundle optimization = Pack only what matters!
Strategy 1: Analyze Your Bundle
# See what's making your app fat!
npm install -D rollup-plugin-visualizer
// vite.config.js
import { visualizer } from 'rollup-plugin-visualizer'
export default {
plugins: [
visualizer({
open: true,
filename: 'bundle-stats.html'
})
]
}
Strategy 2: External Libraries
// vite.config.js
export default {
build: {
rollupOptions: {
external: ['moment'],
output: {
globals: {
moment: 'moment'
}
}
}
}
}
// Load moment.js from CDN instead!
Strategy 3: Smart Imports
// β Import entire library (HUGE)
import moment from 'moment'
// Bundle: +300KB π±
// β
Use lighter alternative
import { format } from 'date-fns'
// Bundle: +2KB π
// β Import all icons
import * as Icons from '@heroicons/vue'
// Bundle: +500KB π±
// β
Import only needed icons
import { HomeIcon } from '@heroicons/vue/24/solid'
// Bundle: +1KB π
Strategy 4: Compression
// vite.config.js
import viteCompression from 'vite-plugin-compression'
export default {
plugins: [
viteCompression({
algorithm: 'gzip'
})
]
}
// Your 500KB bundle β 100KB gzipped!
The Ultimate Checklist
| Action | Impact |
|---|---|
| Use code splitting | -40% initial load |
| Tree shake imports | -30% bundle size |
| Lazy load routes | -50% initial load |
| Compress with gzip | -70% transfer size |
| Use modern formats | -20% bundle size |
graph TD A["Original Bundle 2MB"] --> B["Code Splitting"] B --> C["1.2MB"] C --> D["Tree Shaking"] D --> E["840KB"] E --> F["Lazy Loading"] F --> G["500KB Initial"] G --> H["Gzip Compression"] H --> I["150KB Final! π"]
π― Quick Summary
| Concept | What It Does | Like⦠|
|---|---|---|
| SSR | Server builds page first | Kitchen cooking |
| CSR | Browser builds page | Table cooking |
| Hydration | Makes SSR page interactive | Waking up |
| Code Splitting | Load code in pieces | Pack for today |
| Tree Shaking | Remove unused code | Dead leaves fall |
| Lazy Loading | Load when needed | Cat wakes for treats |
| Bundle Optimize | Make everything smaller | Pack light |
π You Did It!
You now understand how to make Vue.js apps production-ready!
Remember:
- SSR = Fast first paint, great for SEO
- Hydration = Makes static HTML interactive
- Code Splitting = Donβt load everything at once
- Tree Shaking = Remove what you donβt use
- Lazy Loading = Load components when needed
- Bundle Optimization = Keep your app lean and fast
Your users will thank you with faster load times and smoother experiences! π
