Hầu hết các trang web và ứng dụng đều có nhiều phần khác nhau. Thay vì gửi tất cả JavaScript tạo nên ứng dụng ngay khi trang đầu tiên được tải, việc chia JavaScript thành nhiều đoạn sẽ cải thiện hiệu suất trang.
Lớp học lập trình này hướng dẫn cách sử dụng tính năng phân chia mã để cải thiện hiệu suất của một ứng dụng đơn giản sắp xếp 3 số.
Đo lường
Như thường lệ, điều quan trọng là trước tiên bạn phải đo lường hiệu suất của một trang web trước khi cố gắng thêm bất kỳ hoạt động tối ưu hoá nào.
- Để xem trước trang web, hãy nhấn vào Xem ứng dụng, rồi nhấn vào Toàn màn hình
.
- Nhấn tổ hợp phím `Control+Shift+J` (hoặc `Command+Option+J` trên máy Mac) để mở DevTools.
- Nhấp vào thẻ Mạng.
- Chọn hộp đánh dấu Tắt bộ nhớ đệm.
- Tải lại ứng dụng.
71,2 KB JavaScript chỉ để sắp xếp một vài con số trong một ứng dụng đơn giản. What gives?
Trong mã nguồn (src/index.js
), thư viện lodash
được nhập và dùng trong ứng dụng này. Lodash cung cấp nhiều hàm tiện ích hữu ích, nhưng ở đây chỉ dùng một phương thức duy nhất từ gói.
Cài đặt và nhập toàn bộ các phần phụ thuộc của bên thứ ba trong khi chỉ sử dụng một phần nhỏ là một lỗi thường gặp.
Tối ưu hoá
Có một số cách để giảm kích thước gói:
- Viết một phương thức sắp xếp tuỳ chỉnh thay vì nhập thư viện của bên thứ ba
- Sử dụng phương thức
Array.prototype.sort()
tích hợp để sắp xếp theo số - Chỉ nhập phương thức
sortBy
từlodash
chứ không nhập toàn bộ thư viện - Tải mã để sắp xếp chỉ khi người dùng nhấp vào nút
Lựa chọn 1 và 2 là những phương pháp hoàn toàn phù hợp để giảm kích thước gói (và có lẽ sẽ hợp lý nhất đối với một ứng dụng thực). Tuy nhiên, những phương thức đó không được dùng trong hướng dẫn này để phục vụ mục đích giảng dạy 😈.
Cả lựa chọn 3 và 4 đều giúp cải thiện hiệu suất của ứng dụng này. Một vài phần tiếp theo của lớp học lập trình này sẽ đề cập đến các bước này. Giống như mọi hướng dẫn lập trình, bạn nên luôn cố gắng tự viết mã thay vì sao chép và dán.
Chỉ nhập những gì bạn cần
Bạn cần sửa đổi một số tệp để chỉ nhập phương thức duy nhất từ lodash
.
Để bắt đầu, hãy thay thế phần phụ thuộc này trong package.json
:
"lodash": "^4.7.0",
bằng:
"lodash.sortby": "^4.7.0",
Bây giờ, trong src/index.js
, hãy nhập mô-đun cụ thể này:
import "./style.css";
import _ from "lodash";
import sortBy from "lodash.sortby";
Và cập nhật cách sắp xếp các giá trị::
form.addEventListener("submit", e => {
e.preventDefault();
const values = [input1.valueAsNumber, input2.valueAsNumber, input3.valueAsNumber];
const sortedValues = _.sortBy(values);
const sortedValues = sortBy(values);
results.innerHTML = `
<h2>
${sortedValues}
</h2>
`
});
Tải lại ứng dụng, mở Công cụ cho nhà phát triển và xem lại bảng điều khiển Mạng.
Đối với ứng dụng này, kích thước gói đã giảm hơn 4 lần mà không tốn nhiều công sức, nhưng vẫn còn nhiều điểm cần cải thiện.
Phân chia mã
webpack là một trong những trình kết hợp mô-đun mã nguồn mở phổ biến nhất hiện nay. Nói tóm lại, Webpack gói tất cả các mô-đun JavaScript (cũng như các tài sản khác) tạo nên một ứng dụng web thành các tệp tĩnh mà trình duyệt có thể đọc được.
Gói đơn được dùng trong ứng dụng này có thể được chia thành 2 khối riêng biệt:
- Một người chịu trách nhiệm về mã tạo nên tuyến đường ban đầu của chúng ta
- Một khối thứ cấp chứa mã sắp xếp của chúng ta
Khi sử dụng dynamic imports, bạn có thể lazy loaded một đoạn thứ cấp hoặc tải theo yêu cầu. Trong ứng dụng này, mã tạo nên đoạn chỉ có thể được tải khi người dùng nhấn nút.
Bắt đầu bằng cách xoá dữ liệu nhập cấp cao nhất cho phương thức sắp xếp trong src/index.js
:
import sortBy from "lodash.sortby";
Và nhập nó trong trình nghe sự kiện sẽ kích hoạt khi người dùng nhấn nút:
form.addEventListener("submit", e => {
e.preventDefault();
import('lodash.sortby')
.then(module => module.default)
.then(sortInput())
.catch(err => { alert(err) });
});
Tính năng import()
là một phần của đề xuất (hiện ở giai đoạn 3 của quy trình TC39) để thêm khả năng nhập mô-đun một cách linh động. webpack đã hỗ trợ tính năng này và tuân theo cùng một cú pháp do đề xuất đưa ra.
import()
trả về một promise và khi promise này phân giải, mô-đun đã chọn sẽ được cung cấp và được tách thành một đoạn riêng biệt. Sau khi mô-đun được trả về, module.default
sẽ được dùng để tham chiếu đến chế độ xuất mặc định do lodash cung cấp. Lệnh hứa được liên kết với một .then
khác gọi phương thức sortInput
để sắp xếp 3 giá trị đầu vào. Ở cuối chuỗi promise, .catch()
được dùng để xử lý các trường hợp lời hứa bị từ chối do lỗi.
Việc cuối cùng cần làm là viết phương thức sortInput
ở cuối tệp. Đây phải là một hàm trả về một hàm nhận phương thức đã nhập từ lodash.sortBy
. Sau đó, hàm lồng nhau có thể sắp xếp 3 giá trị đầu vào và cập nhật DOM.
const sortInput = () => {
return (sortBy) => {
const values = [
input1.valueAsNumber,
input2.valueAsNumber,
input3.valueAsNumber
];
const sortedValues = sortBy(values);
results.innerHTML = `
<h2>
${sortedValues}
</h2>
`
};
}
Giám Sát
Tải lại ứng dụng lần cuối và chú ý đến bảng điều khiển Mạng một lần nữa. Chỉ một gói ban đầu nhỏ được tải xuống ngay khi ứng dụng tải.
Sau khi người dùng nhấn nút để sắp xếp các số đầu vào, đoạn chứa mã sắp xếp sẽ được tìm nạp và thực thi.
Hãy chú ý cách các số vẫn được sắp xếp!
Kết luận
Phân tách mã và tải chậm có thể là những kỹ thuật cực kỳ hữu ích để giảm kích thước gói ban đầu của ứng dụng. Điều này có thể trực tiếp dẫn đến thời gian tải trang nhanh hơn nhiều. Tuy nhiên, có một số điều quan trọng cần cân nhắc trước khi đưa phương pháp tối ưu hoá này vào ứng dụng của bạn.
Giao diện người dùng tải từng phần
Khi tải từng phần các mô-đun mã cụ thể, bạn cần cân nhắc trải nghiệm của người dùng có kết nối mạng yếu hơn. Việc chia tách và tải một đoạn mã rất lớn khi người dùng gửi một hành động có thể khiến ứng dụng có vẻ như đã ngừng hoạt động, vì vậy, hãy cân nhắc việc hiển thị một chỉ báo tải nào đó.
Tải từng phần các mô-đun nút của bên thứ ba
Không phải lúc nào bạn cũng nên tải các phần phụ thuộc của bên thứ ba một cách trì hoãn trong ứng dụng của mình và điều này còn tuỳ thuộc vào vị trí bạn sử dụng chúng. Thông thường, các phần phụ thuộc của bên thứ ba được chia thành một gói vendor
riêng biệt có thể được lưu vào bộ nhớ đệm vì chúng không cập nhật thường xuyên. Đọc thêm về cách SplitChunksPlugin có thể giúp bạn thực hiện việc này.
Tải từng phần bằng một khung JavaScript
Nhiều khung và thư viện phổ biến sử dụng webpack cung cấp các lớp trừu tượng để giúp việc tải chậm dễ dàng hơn so với việc sử dụng tính năng nhập động ở giữa ứng dụng của bạn.
Mặc dù việc tìm hiểu cách hoạt động của tính năng nhập động là hữu ích, nhưng bạn luôn phải sử dụng phương thức do khung/thư viện của bạn đề xuất để tải các mô-đun cụ thể một cách trì hoãn.
Tải trước và tìm nạp trước
Nếu có thể, hãy tận dụng các gợi ý của trình duyệt, chẳng hạn như <link rel="preload">
hoặc <link rel="prefetch">
để cố gắng tải các mô-đun quan trọng sớm hơn nữa. webpack hỗ trợ cả hai gợi ý này thông qua việc sử dụng các chú thích đặc biệt trong câu lệnh nhập. Điều này được giải thích chi tiết hơn trong hướng dẫn Tải trước các khối quan trọng.
Tải từng phần nhiều hơn mã
Hình ảnh có thể chiếm một phần đáng kể trong ứng dụng. Việc tải từng phần những thành phần nằm bên dưới màn hình đầu tiên hoặc bên ngoài khung hiển thị của thiết bị có thể giúp tăng tốc độ của một trang web. Đọc thêm về vấn đề này trong hướng dẫn Lazysizes.