Giới thiệu

Trong các dự án Front-End hiện đại, thời gian tải trang đầu tiên (First Paint) đóng vai trò quyết định trải nghiệm người dùng. Vue 3 cung cấp cơ chế Lazy LoadingCode Splitting giúp tải chỉ những phần cần thiết khi người dùng thực sự yêu cầu, giảm đáng kể kích thước bundle ban đầu. Bài viết sẽ hướng dẫn chi tiết cách cấu hình Lazy Loading cho các route và component trong Vue 3 khi dùng TypeScript, đồng thời tối ưu Tailwind CSS để giảm kích thước CSS cuối cùng.

Tại sao cần Lazy Loading và Code Splitting

Khi một ứng dụng Vue được biên dịch thành một file JavaScript duy nhất, mọi component, thư viện và logic đều được tải đồng thời. Điều này dẫn tới:

  • Kích thước bundle lớn, làm chậm thời gian tải trang.
  • Người dùng phải chờ tải toàn bộ mã dù chỉ truy cập một phần giao diện.
  • Hiệu suất trên thiết bị di động giảm sút.

Lazy Loading và Code Splitting cho phép chia bundle thành các chunk nhỏ, mỗi chunk chỉ được tải khi cần. Kết quả là thời gian phản hồi nhanh hơn, trải nghiệm mượt mà hơn.

Cấu hình Vue Router cho Lazy Loading

Ví dụ cấu hình

import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';

const routes: Array = [
  {
    path: '/',
    name: 'Home',
    component: () => import(/* webpackChunkName: "home" */ '@/views/Home.vue')
  },
  {
    path: '/about',
    name: 'About',
    // Lazy load About component khi người dùng truy cập /about
    component: () => import(/* webpackChunkName: "about" */ '@/views/About.vue')
  },
  {
    path: '/dashboard',
    name: 'Dashboard',
    // Sử dụng route level code splitting cho Dashboard
    component: () => import(/* webpackChunkName: "dashboard" */ '@/views/Dashboard.vue')
  }
];

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
});

export default router;

Trong ví dụ trên, mỗi import() sẽ tạo một chunk riêng, được tải khi người dùng truy cập route tương ứng. Thuộc tính webpackChunkName giúp đặt tên chunk để dễ dàng kiểm tra trong công cụ dev.

Sử dụng defineAsyncComponent trong Composition API

Component async

import { defineAsyncComponent } from 'vue';

// Định nghĩa component async với fallback UI
const AsyncUserProfile = defineAsyncComponent({
  loader: () => import('@/components/UserProfile.vue'),
  loadingComponent: () => import('@/components/LoadingSpinner.vue'),
  // Thời gian chờ tối đa 5 giây, nếu quá sẽ trả về lỗi
  timeout: 5000,
  // Khi có lỗi, hiển thị component lỗi
  errorComponent: () => import('@/components/ErrorFallback.vue')
});

export default {
  components: { AsyncUserProfile }
};

Với defineAsyncComponent, chúng ta có thể cung cấp loadingComponent, errorComponenttimeout để cải thiện UX khi tải component bất đồng bộ.

Tối ưu Tailwind CSS để giảm kích thước bundle

Tailwind CSS mặc định bao gồm hàng ngàn utility class, khiến file CSS cuối cùng có thể lên tới vài megabyte. Để giảm kích thước:

  1. Sử dụng purge (hoặc content trong Tailwind v3) để loại bỏ các class không dùng trong mã nguồn.
    // tailwind.config.js
    module.exports = {
      content: [
        './src/**/*.vue',
        './src/**/*.html',
        './src/**/*.tsx'
      ],
      theme: {
        extend: {}
      },
      plugins: []
    };
  2. Khai báo darkMode: 'class' nếu không cần hỗ trợ dark mode tự động.
  3. Sử dụng @apply trong file CSS để gom nhiều utility thành một class tùy chỉnh, giảm số lần lặp lại.

Kết hợp việc purge và @apply thường giúp giảm kích thước CSS xuống dưới 100KB (gzip), đủ nhẹ cho các ứng dụng SPA.

Kết luận

Áp dụng Lazy Loading cho route và component, đồng thời tối ưu Tailwind CSS, sẽ mang lại thời gian tải nhanh hơn và trải nghiệm mượt mà cho người dùng. Nếu bạn muốn nắm vững toàn bộ quy trình từ cấu hình dự án đến triển khai thực tế, Tham khảo khóa học "Lập trình Front-End với VueJS Framework" tại đây.