Trong quá trình phát triển các ứng dụng Single Page Application (SPA) với Vue.js, việc quản lý kích thước gói (bundle size) là một yếu tố then chốt ảnh hưởng trực tiếp đến trải nghiệm người dùng. Khi cấu hình Vue Router, nếu tất cả các component được import tĩnh, Webpack hoặc Vite sẽ đóng gói chúng vào một file JavaScript duy nhất. Điều này khiến thời gian tải trang đầu tiên (First Contentful Paint) bị kéo dài do trình duyệt phải tải xuống và phân tích cú pháp một lượng mã nguồn lớn ngay cả khi người dùng chưa truy cập vào các route đó.
Để giải quyết vấn đề này, chúng ta áp dụng kỹ thuật Lazy Loading (tải chậm) kết hợp với Code Splitting (chia nhỏ mã nguồn). Cơ chế này cho phép chia nhỏ file JS thành các chunk riêng biệt và chỉ tải chunk tương ứng khi người dùng thực sự điều hướng đến route.
Phương pháp 1: Sử dụng cú pháp AMD Require
Trong các phiên bản Webpack cũ, cú pháp require bất đồng bộ thường được sử dụng để thực hiện code splitting. Phương thức này hoạt động ở thời điểm chạy (runtime), chia nhỏ các component thành các file JS riêng lẻ. Khi người dùng truy cập vào route, hệ thống mới bắt đầu tải file JS tương ứng.
import Vue from 'vue';
import VueRouter from 'vue-router';
// Import tĩnh - Component sẽ được đóng gói vào file bundle chính
import UserDashboard from '../views/user/Dashboard.vue';
import NotificationPanel from '../components/NotificationPanel.vue';
Vue.use(VueRouter);
const router = new VueRouter({
mode: 'history',
routes: [
{
path: '/admin',
// Lazy loading sử dụng AMD require
component: resolve => require(['../views/AdminPanel.vue'], resolve),
meta: { requiresAuth: true }
},
{
path: '/dashboard',
component: UserDashboard // Load đồng bộ
},
{
path: '/notifications',
component: NotificationPanel,
children: [
{
path: 'settings',
// Lazy loading cho route con
component: resolve => require(['../views/notifications/Settings.vue'], resolve)
}
]
}
]
});
export default router;
Mặc dù phương pháp này hoạt động tốt, nhưng cú pháp AMD require không phải là chuẩn ES6 và có thể gây khó khăn cho việc bảo trì hoặc tích hợp với các bundler hiện đại.
Phương pháp 2: Sử dụng ES6 Dynamic Import (Tiêu chuẩn hiện đại)
Chuẩn ES6 đã giới thiệu cú pháp import() trả về một Promise. Đây là phương pháp chuẩn mực, được đội ngũ Vue và các công cụ build như Webpack, Vite, Rollup khuyến nghị sử dụng. Hàm import() cho phép viết code rõ ràng hơn, dễ đọc hơn và hỗ trợ tốt các tính năng nâng cao của bundler.
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
// Định nghĩa component lazy load bằng ES6 dynamic import
// Sử dụng Webpack magic comments để đặt tên chunk tùy chỉnh
const AnalyticsWorkspace = () => import(
/* webpackChunkName: "analytics-group" */
'../views/AnalyticsWorkspace.vue'
);
const DataExportModule = () => import('../components/DataExportModule.vue');
const router = new VueRouter({
routes: [
{
path: '/analytics',
name: 'Analytics',
component: AnalyticsWorkspace
},
{
path: '/export',
name: 'DataExport',
component: DataExportModule
}
]
});
export default router;
Khi sử dụng hàm mũi tên () => import(...), Vue Router sẽ nhận diện đây là một component bất đồng bộ. Khi route được kích hoạt, Vue Router sẽ chờ Promise được resolve trước khi tiến hành render component lên giao diện. Việc sử dụng các magic comments như webpackChunkName giúp kỹ sư前端 kiểm soát tốt hơn cấu trúc thư mục dist sau khi build, gom nhóm các module có liên quan vào cùng một chunk để tối ưu hóa số lượng request mạng.