Bấm "Tải lên", công cụ dành cho nhà phát triển hiện ra một dòng chữ đỏ: Kích thước gói chính vượt quá giới hạn 2MB. Phiên bản không thể tải lên, yêu cầu vẫn đang chờ đợi.
Bài viết này không nói về tổng quan nguyên lý, chỉ liệt kê các phương pháp đưa kích thước gói chính trở lại dưới 2MB theo thứ tự "tiết kiệm nhiều, sửa ít".
Đầu tiên hãy làm rõ: 2MB giới hạn ở đâu
WeChat có hai giới hạn cứng đối với kích thước gói mã của Mini Program:
| Giới hạn | Giới hạn |
|---|---|
| Gói chính (bao gồm app.js, trang tabBar, tài nguyên chung v.v.) | 2MB |
| Mỗi gói phụ | 2MB |
| Tổng kích thước tất cả các gói của Mini Program | 20MB |
Lưu ý rằng "kích thước" ở đây là kích thước gói mã sau khi tải lên, không chỉ là JS. Tất cả các tệp sẽ được đóng gói vào trong dự án đều được tính: .js、.wxml、.wxss、.、phông chữ、âm thanh, và ——thường chiếm phần lớn——hình ảnh.
Đây là giới hạn cứng "nếu không ép được thì không thể phát hành phiên bản", không phải "tối ưu hóa một chút thì tốt hơn". Vì vậy bước đầu tiên không phải là hành động, mà là trước tiên phải nhìn rõ tiền đã bị lãng phí ở đâu.
Bước 0: Xem rõ trong gói chính đang có thứ gì chiếm chỗ
Góc trên bên phải của công cụ dành cho nhà phát triển WeChat, "Chi tiết → Thông tin cơ bản" có thể thấy tổng kích thước gói mã cục bộ; chi tiết hơn ở thanh công cụ "Phân tích phụ thuộc mã", có thể liệt kê kích thước theo tệp và đánh dấu những tệp nào không được bất kỳ trang nào tham chiếu.
Hầu hết các dự án sau khi nhìn vào bảng này đều phát hiện cùng một sự việc: tài nguyên tĩnh như hình ảnh、phông chữ lớn hơn nhiều so với mã nghiệp vụ. JS viết vài chục nghìn dòng cũng chỉ vài trăm KB, một tấm banner chưa xử lý có thể hơn trăm KB, một thư mục images/ dễ dàng ăn mất một nửa gói chính.
Dưới đây được sắp xếp theo lợi ích từ lớn đến nhỏ.
Phương pháp 1: Xóa các tệp hoàn toàn không cần thiết
Cách tiết kiệm công sức nhất, cũng thường bị bỏ qua nhất.
- Tệp được đánh dấu là không được tham chiếu trong "Phân tích phụ thuộc mã": Các biểu tượng cũ do các phiên bản trước đó thay đổi、trang bị hỏng、hình ảnh thử nghiệm, hãy xóa trực tiếp.
- Các tệp không nên vào gói: bản thiết kế、README、
.psd、nguyên liệu gốc、dữ liệu giả lập (Mock). Nếu có thể hãy chuyển ra khỏi thư mục dự án; nếu không thể, hãy sử dụngpackOptions.ignoretrongproject.config.để loại bỏ:
{
"packOptions": {
"ignore": [
{ "type": "folder", "value": "design" },
{ "type": "suffix", "value": ".psd" }
]
}
}
- Nhập toàn bộ thư viện thành phần: Chỉ dùng ba thành phần nhưng lại đưa toàn bộ thư viện UI vào gói chính, đây là dạng "béo bất đắc dĩ" thường gặp. Hãy chuyển sang nhập theo nhu cầu (on-demand), chỉ giữ lại các thư mục thành phần được sử dụng.
Phương pháp 2: Gói phụ – Di chuyển các trang không phải màn hình đầu tiên ra khỏi gói chính
Đây là giải pháp chính thức đúng. Gói chính chỉ giữ trang khởi động、trang tabBar và mã chung thực sự cần thiết, các trang còn lại được chia theo nghiệp vụ vào các gói phụ:
{
"pages": ["pages/index/index", "pages/mine/mine"],
"subPackages": [
{ "root": "packageOrder", "pages": ["list/list", "detail/detail"] },
{ "root": "packageActivity", "pages": ["index/index"] }
],
"preloadRule": {
"pages/index/index": { "network": "all", "packages": ["packageOrder"] }
}
}
Một số điểm chính:
- Tài nguyên đi theo trang: Các trang gói phụ sử dụng hình ảnh、thành phần, cần đưa vào thư mục gói phụ. Nếu đặt ở thư mục chung
images/trong gói chính, thì dù gói phụ được chia thế nào, kích thước vẫn được tính trên gói chính. preloadRuleTải trước gói phụ: Khi vào trang chủ, hãy kéo xuống gói phụ có khả năng cao sẽ được dùng tiếp theo, người dùng sẽ không cảm nhận được độ trễ khi nhấp vào.- Gói phụ độc lập (
"independent": true): Phù hợp với trang sự kiện、trang đích (landing page) có thể mở riêng biệt không phụ thuộc vào gói chính. - Gói phụ bất đồng bộ: Khi tham chiếu thành phần hoặc JS qua các gói phụ, có thể sử dụng thành phần giữ chỗ và
require.async, tránh việc vì "chia sẻ" mà lại kéo mã lên gói chính.
Chi phí của gói phụ là phải thay đổi cấu trúc thư mục và đường dẫn nhảy. Việc chia tách dự án cũ không hề nhẹ, đây cũng là lý do tại sao đáng để làm bước tiếp theo trước ——thường sau khi làm xong bước này, gói chính đã quay lại dưới 2MB rồi.
Phương pháp 3: Nén hình ảnh xuống (thay đổi ít nhất、hiệu quả trực tiếp nhất)
Hình ảnh là phần dễ dàng "béo phì vô tội vạ" nhất trong gói chính. Các file PNG do nhà thiết kế xuất ra có chứa dữ liệu thừa, JPG sử dụng tham số chất lượng hơi cao, những byte này người dùng không nhìn ra, nhưng mỗi byte đều được tính vào 2MB.
Những hình ảnh nào phải giữ lại trong gói
Không phải tất cả hình ảnh đều có thể chuyển sang CDN:
- Biểu tượng tabBar:
iconPath/selectedIconPathchỉ có thể là đường dẫn cục bộ, không hỗ trợ hình ảnh mạng. - Trang khởi động、Logo màn hình đầu tiên、hình ảnh cố định khi yếu mạng hoặc ngoại tuyến: Phải có thể hiển thị trong mọi trường hợp.
- Các biểu tượng nhỏ xuất hiện thường xuyên: Mỗi lần phải đi qua yêu cầu mạng không hề đáng.
Những hình ảnh này chỉ có thể giữ lại trong gói, nên một cách duy nhất là làm chúng nhỏ lại.
Tránh một cái bẫy khác: background-image trong wxss
Trong .wxss sử dụng background-image để tham chiếu hình ảnh cục bộ, trên thiết bị thật thì không hiệu lực, cách phổ biến là chuyển thành base64 nội tuyến. Nhưng mã hóa base64 sẽ làm kích thước nở ra khoảng một phần ba, hơn nữa nó nằm trong tệp style, trong phân tích phụ thuộc không quá nổi bật. Nếu có thể đổi sang thành <image> thành phần hoặc hình ảnh mạng, đừng nội tuyến; nếu bắt buộc phải nội tuyến, hãy nén hình ảnh gốc trước khi chuyển.
Sử dụng ImgZilla để nén toàn bộ thư mục tài nguyên tại chỗ
Điều đáng sợ nhất khi làm Mini Program, là nén xong hình ảnh还要 quay lại sửa đường dẫn ——trong wxml、wxss、cấu hình JS、cấu hình tabBar, khắp nơi là /images/xxx.png. Nhiều công cụ nén sẽ lưu lại thành xxx-min.png, hoặc yêu cầu bạn xuất ra thư mục khác, nén xong还得 phải thay thủ công, rồi kiểm tra lại xem có bỏ sót gì không.
ImgZilla là một công cụ nén hình ảnh macOS, cách làm của nó là nén tại chỗ:
- Tên tệp、đường dẫn、định dạng đều không đổi. Sau khi nén
icon-home.pngvẫn làicon-home.png, không cần sửa bất kỳ câu lệnh tham chiếu nào trong mã. - Chỉ cần kéo toàn bộ thư mục vào. Nó sẽ quét đệ quy tất cả thư mục con, và tự động bỏ qua các tệp ẩn và
node_modules. Kéoimages/、static/thậm chí toàn bộ thư mục dự án vào, nén xong trực tiếp xem sự thay đổi kích thước gói trong công cụ dành cho nhà phát triển. - Hình ảnh gốc được mặc định đưa vào thùng rác, nếu thấy tấm nào không vừa mắt, hãy nhấp chuột phải "Put back" để khôi phục. Nếu dự án trong Git, còn có thêm một lớp bảo hiểm.
- Chạy hoàn toàn trên máy tính cục bộ, không tải lên mạng. Tài liệu dự án công ty sẽ không rời khỏi máy tính của bạn, và cũng không có giới hạn số lượng mỗi lần、kích thước mỗi tấm như các trang nén trực tuyến.
Về chất lượng, cách xử lý cho các định dạng khác nhau khác nhau, hãy làm rõ ở đây:
- PNG: Sử dụng oxipng để nén không mất chất lượng (lossless), không một pixel nào thay đổi. Trong Mini Program có nhiều biểu tượng、hình cắt (sprites) đều là PNG, phần này có thể yên tâm nén.
- SVG: Làm sạch các thẻ thừa, cũng là lossless.
- JPG / WebP / GIF v.v.: Thuộc loại mã hóa mất chất lượng, các tham số đã được tinh chỉnh cho từng định dạng trong vùng không mất chất lượng theo mắt——mắt người rất khó phân biệt sự khác biệt. Không cần tin tưởng một cách mù quáng, cửa sổ so sánh tích hợp (
⌘D) có thể chia đôi màn hình、phóng to đến từng pixel thật để so sánh từng tấm.
Hai điểm khác cũng đáng biết:
- Nó sẽ không ép nén cứng nếu hình ảnh đã được nén tối đa: Các tệp có tỷ lệ nén dưới 0,4% sẽ được đánh dấu là "đã tối thiểu", giữ nguyên, không làm mờ ảnh chỉ để con số đẹp hơn. Vì vậy nếu hình ảnh của bạn đã được tối ưu hóa nghiêm túc trước đó, bước này có thể không tiết kiệm nhiều ——điều này rất bình thường, cũng có nghĩa là nên chuyển sang gói phụ.
- Nó không chuyển đổi định dạng、không thay đổi độ phân giải. Sau khi nén PNG vẫn là PNG, kích thước không đổi. Nếu bạn muốn chuyển đổi sang WebP、hoặc giảm 3 lần xuống 2 lần, thì là chuyện khác, cần xử lý riêng.
Có thể tiết kiệm được bao nhiêu, phụ thuộc vào việc hình ảnh được xuất ra như thế nào, chúng tôi không đưa ra một tỷ lệ chung. Là tham chiếu, chúng tôi đã thực hiện hai lần kiểm chứng công khai: một nhóm JPG đã được nén một lần khi xuất kho web, nén thêm lần nữa vẫn tiết kiệm được 46,8%; một nhóm JPG từ máy ảnh trực tiếp nén được 76,8% (chi tiết trong loạt bài "Thực tế ImgZilla"). Các hình ảnh trong Mini Program thường được xuất trực tiếp từ công cụ thiết kế, thường chưa được nén nghiêm túc, vì vậy đáng để chạy qua một lần xem.
ImgZilla hiện tại chỉ có phiên bản macOS (macOS 12.3 trở lên), có thể tải từ Mac App Store, miễn phí nén 10 tấm mỗi ngày. Các bạn học sinh Windows, tư duy trong phần này vẫn áp dụng được, chỉ cần thay bằng một công cụ có thể "giữ nguyên tên tệp" tương tự.
Phương pháp 4: Di chuyển các hình ảnh lớn sang CDN
Sau khi nén mà vẫn rất lớn ——như banner sự kiện、hình ảnh dài trang chi tiết、hình ảnh lớn sản phẩm——thường không phù hợp để đặt trong gói. Tải lên đối tượng lưu trữ hoặc CDN, đổi thành địa chỉ mạng trong mã, gói chính sẽ nhẹ ngay lập tức.
Nhưng điều này không miễn phí:
- Tải lần đầu phải đi qua mạng, trong tình trạng mạng yếu sẽ có khoảng trống trắng, nên nên phối hợp với hình ảnh giữ chỗ hoặc màn hình xương (skeleton screen).
- Phải duy trì một quy trình tải lên、lưu vào bộ nhớ đệm、cập nhật.
- CDN tính phí theo lưu lượng. Mỗi lần hình ảnh được tải lên đều tốn tiền, phí lưu lượng tương đương với "kích thước hình ảnh × số lần truy cập". Vì vậy trước khi chuyển sang CDN, cũng đáng để nén hình ảnh trước ——nén một lần, sau đó mỗi lần truy cập đều tiết kiệm lưu lượng.
Phương pháp 5: Đóng gói và nhập theo nhu cầu ở mức mã
Sau khi xử lý hình ảnh và cấu trúc, không gian còn lại có thể vớt từ trong mã:
- Trong công cụ dành cho nhà phát triển, "Chi tiết → Cài đặt cục bộ" hãy đánh dấu nén script、style、WXML khi tải lên.
- Trong
app.hãy bật"lazyCodeLoading": "requiredComponents"(tải theo nhu cầu). Nó chủ yếu cải thiện thời gian khởi động hơn là kích thước gói, nhưng cũng hãy bật cùng lúc khi đang tối ưu hóa hiệu suất. - Kiểm tra
miniprogram_npm: Các gói npm được xây dựng có tình trạng nhập toàn bộ mà chỉ dùng một vài hàm không. - Các dữ liệu tĩnh lớn viết cứng trong JS (danh sách thành phố、bảng cấu hình) hãy cân nhắc chuyển sang giao diện API.
Bảng tóm tắt
| Phương pháp | Có tiết kiệm được bao nhiêu | Cần sửa bao nhiêu | Thứ tự đề xuất |
|---|---|---|---|
| Xóa tệp không cần thiết | Tùy vào gánh nặng lịch sử dự án | Rất ít | 1 |
| Nén hình ảnh tại chỗ | Hình ảnh càng nhiều、xuất ra càng tự ý, càng tiết kiệm được nhiều | Gần như bằng không, đường dẫn không đổi | 2 |
| Gói phụ | Có thể rất nhiều | Phải thay đổi thư mục và định tuyến | 3 |
| Chuyển hình ảnh lớn sang CDN | Rất nhiều | Phải thay đổi tham chiếu、duy trì quy trình tải lên | 4 |
| Nén mã và nhập theo nhu cầu | Ít hơn | Tùy tình huống | 5 |
Logic của thứ tự rất đơn giản: Làm những việc ít thay đổi trước, làm những việc thay đổi lớn sau. Xóa tệp và nén hình ảnh không hề chạm vào mã nghiệp vụ, làm xong xem gói chính còn thiếu bao nhiêu ——nếu đã quay lại dưới 2MB, phiên bản hôm nay có thể phát hành; nếu chưa đủ, hãy mới động vào gói phụ và CDN, trong đầu cũng đã có định hướng rồi.
Việc vượt giới hạn gói chính thường xảy ra vào lúc căng thẳng nhất trước khi phát hành. Hãy đưa việc nén hình ảnh vào quy trình hàng ngày ——trước khi thêm hình ảnh mới hãy nén một lần——lần sau sẽ không phải lo lắng trước ngày hết hạn nhìn vào dòng chữ đỏ.
