Webpack 5: Webpack code splitting - tree shaking

Code Splitting là một trong những tính năng hấp dẫn của Webpack. Tính năng này cho phép bạn chia code của mình thành nhiều gói khác nhau, phân tách đoạn code hoặc thư viện giống nhau ra một file riêng. Nó có thể được sử dụng để tách ra các gói nhỏ hơn và kiểm soát mức độ ưu tiên tải tài nguyên, nếu được sử dụng đúng cách, có thể có tác động lớn đến thời gian tải của trang web.
1. Chuẩn bị file
Thêm thư viện lodash để tạo ra một thư viện chung, các file nào cần thì gọi vào
Link thư viện: https://www.npmjs.com/package/lodash
npm install lodash@4.17.21
const path = require('path')
module.exports = {
entry: {
home: './src/home.js',
about: './src/about.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
}
import _ from 'lodash'
console.log('home.js')
import _ from 'lodash'
console.log('about.js')
Ta thấy cả hai file home.js và about.js trên đều gọi thư viện lodash để tạo ra sự trùng lặp.
2. Webpack Code Splitting
Với setup và cấu hình ở trên khi chưa có Code Splitting khi ta chạy npm run dev thì được thông số như sau:

Ta có hai file output là about.js và home.js có dung lượng đều là 72.4 KiB
Giờ ta thêm Code Splitting vào trong file webpack.config.js để phân tách code ra nhé
const path = require('path')
module.exports = {
entry: {
home: './src/home.js',
about: './src/about.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
optimization: {
splitChunks: {
chunks: 'all'
}
}
}
Chạy lại npm run dev và xem sự khác biệt

Lúc này ta thấy có ba file output là home.js about.js và vendors~about~home.js
Hai file output about.js và home.js lúc trước là 72.4 KiB giờ chỉ còn tầm 1.5KiB
Xuất hiện file output vendors~about~home.js điều này có nghĩa webpack đã tối ưu source code, thu gom những thư viện giống nhau ở đây là lodash, tên thì được remix giữa vendor (nhà cung cấp) với tên hai file
Lưu ý là khi chạy qua Code Splitting thì code đã bị tách ra các file khác nhau, trong file dist/index.html muốn chạy được thì phải gọi cả file vendors vào cùng
<script src="vendors~about~home.js"></script>
<script src="home.js"></script>
3. Webpack Tree Shaking là gì?
Nếu như Code Splitting giúp chúng ta chia nhỏ code thành nhiều gói (bundle) khác nhau để tối ưu hóa việc tải tài nguyên, thì Tree Shaking lại là một "cặp bài trùng" giúp tối ưu hóa dung lượng bên trong từng gói đó.
Hãy tưởng tượng source code của bạn giống như một cái cây. Những đoạn code bạn thực sự chạy là các cành lá xanh tươi, còn những đoạn code bạn viết ra (hoặc import từ thư viện) nhưng không bao giờ gọi tới chính là những chiếc lá héo úa. Tree Shaking (Rung cây) chính là hành động Webpack "lắc" mạnh cái cây đó để các chiếc lá chết rụng xuống, giúp bundle size sau khi build trở nên nhẹ nhất có thể.
Lưu ý quan trọng: Tree Shaking dựa vào cú pháp ES6 Module (
importvàexport). Nó sẽ không hoạt động hoặc hoạt động không hiệu quả với cú pháp CommonJS (requirevàmodule.exports).
4. Chuẩn bị file thử nghiệm Tree Shaking
Để thấy rõ cách Tree Shaking hoạt động, chúng ta hãy tạo thêm một file tính toán và gọi thử nghiệm nó.
Tạo file src/math.js định nghĩa 2 hàm: square (bình phương) và cube (lập phương).
export function square(x) {
return x * x
}
export function cube(x) {
return x * x * x
}
Sửa lại file src/home.js để import file math.js này vào, nhưng chỉ gọi duy nhất hàm cube:
import _ from 'lodash'
import { cube } from './math.js'
console.log('home.js')
console.log(cube(5))
5. Thử nghiệm Tree Shaking trên môi trường Production
Mặc định, Webpack 5 đã tự động kích hoạt tính năng Tree Shaking khi bạn chạy ở chế độ Production mode: 'production'
Hãy thử chạy lệnh build production của bạn npm run build hoặc npx webpack --mode=production
Sau khi build xong, bạn hãy vào thư mục dist, mở file home.js đã bị minify lên và tìm kiếm thử đoạn code của hàm square (tìm kiếm đoạn xử lý x * x). Bạn sẽ thấy: Hàm square đã biến mất hoàn toàn! Webpack đã tự động nhận diện đoạn code thừa đó và "rung" nó rụng khỏi file final bundle.
6. Xem cách Tree Shaking hoạt động ở Development Mode
Nếu bạn chạy ở mode: 'development', Webpack sẽ không xóa code thừa vì nó cần giữ nguyên cấu trúc code để bạn dễ dàng debug. Tuy nhiên, chúng ta vẫn có thể cấu hình để Webpack "đánh dấu" những đoạn code thừa này.
Sửa lại file webpack.config.js, thêm thuộc tính optimization.usedExports
const path = require('path');
module.exports = {
entry: {
home: './src/home.js',
about: './src/about.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
mode: 'development', // Thử nghiệm ở chế độ development
optimization: {
splitChunks: {
chunks: 'all'
},
usedExports: true // Bật tính năng đánh dấu các export được sử dụng
}
};
Bây giờ, hãy chạy lệnh npm run dev và mở file dist/home.js ra xem. Bạn sẽ tìm thấy một đoạn comment rất thú vị của Webpack nằm ngay phía trên hàm square:
/* unused harmony export square */
function square(x) {
return x * x;
}
Đoạn comment unused harmony export này chính là bằng chứng cho thấy Webpack đã phát hiện ra hàm square không được dùng tới. Khi bạn chuyển sang mode production, các công cụ nén code (như TerserPlugin) sẽ nhìn vào các đánh dấu này để xóa bỏ code thừa một cách an toàn.
7. Cấu hình "Sống còn": Side Effects
Có một trường hợp nguy hiểm: Nếu một file code không export gì cả, hoặc có export nhưng bạn không dùng, nhưng bản thân file đó khi import vào lại chạy một đoạn code làm thay đổi biến toàn cục (Global), polyfill hệ thống, hoặc import file CSS (import './style.css'). Những hiện tượng này gọi là Side Effects (Hiệu ứng phụ).
Nếu Webpack thấy bạn không dùng file đó và lỡ tay "Tree Shaking" xóa luôn file, ứng dụng của bạn sẽ bị lỗi lập tức!
Để giải quyết vấn đề này, chúng ta cần khai báo cho Webpack biết file nào an toàn để xóa, file nào có chứa side effects trong file package.json
Trường hợp 1: Dự án của bạn hoàn toàn "sạch", không có file nào gây ra side effect bậy bạ, có thể tự tin cho Webpack xóa hết code thừa:
{
"name": "webpack-tutorial",
"version": "1.0.0",
"sideEffects": false
}
Trường hợp 2: Dự án của bạn có chứa các file CSS hoặc các file polyfill cần được giữ lại, không được phép xóa bừa bãi:
{
"name": "webpack-tutorial",
"version": "1.0.0",
"sideEffects": [
"*.css",
"*.scss",
"./src/polyfills.js"
]
}
Tổng kết
Qua bài viết này, chúng ta đã nắm giữ 2 vũ khí tối thượng của Webpack 5 để tối ưu hóa dung lượng dự án:
Code Splitting: Gom nhóm và chia tách các thư viện trùng lặp (như lodash) ra một file riêng (vendors).
Tree Shaking: Quét sạch các hàm, các đoạn code thừa không dùng tới trong từng file module để bundle luôn nhẹ nhất.
Bài viết đến đây là hết, hi vọng với bài viết này các bạn đã thêm được nhiều kiến thức bổ ích. Hẹn gặp lại các bạn ở bài viết tiếp theo!
- Tham khảo thêm tại: https://webpack.js.org/guides/code-splitting/
- Tham khảo thêm tại: https://webpack.js.org/guides/tree-shaking/
- Source code Github: https://github.com/kentrung/webpack-tutorial
- Series Webpack: https://viblo.asia/s/webpack-tu-a-den-a-cung-kentrung-pmleB8Am5rd
All rights reserved