Thỉnh thoảng, tôi nghĩ mình là người dễ bị xao nhãng.
Tôi đang làm gì ở đây?
Ah đúng rồi, 1 bài blog về tốc độ website.
Mỗi lần ngồi suy nghĩ liệu inline CSS có đáng hay không thì tôi lại đi đến kết luận rằng.. tùy tình hình.
Nếu bạn là trang facebook.com và 99,9% lượt page views là return visitors, thì hãy có 1 file CSS riêng và lưu trữ nó. Nếu nhưng trang của bạn không cần nhiều lượt repeat visit thì bạn có thể muốn inline CSS và lưu 1 network request.
Nếu bạn muốn “tự tạo đau khổ” cho mình thì có thể thử để inline ở vào nửa trên của giao diện và phần còn lại sẽ tải trong 1 file CSS riêng.
Quy tắc riêng của tôi là: nếu bạn có thể tích hợp CSS vào HTML và dung lượng ở mức 14KB thì hãy làm đi (Và tại sao lại là con số 14 thì Tìm hiểu tại đây)
CSS + HTML (đã tinh giảm) của tôi là 3.5 KB – nhờ vậy mọi thứ đều trở nhanh nhẹ hơn
Chúng tôi nghĩ <scripts>
nên ở phần cuối của <body>
. Khi tôi nhận ra mọi người nói <scripts>
là footer, header và body. Thì tôi đã được khai sáng.
[5 phút sau…]
Thỉnh thoảng, tôi nghĩ mình là người dễ bị xao nhãng.
Tôi đang làm gì ở đây?
Ah đúng rồi, 1 bài blog về tốc độ website.
Một mô hình thường thấy trong React với SSR (Server Side Rendering) là:
window.APP_DATA = data;
window.APP_DATA
và kết hợp ứng dụng với nóCách thức này có chút lãng phí nhưng thực sự không ảnh hưởng nhiều vì HTML sẽ được render trong cùng 1 thời gian, chỉ có JavaScript bị delay 1 chút. Nếu trang web của bạn chạy mượt trước khi Javascript tải thì chả ảnh hưởng gì.
Dưới đây là những gì tôi muốn nó diễn ra:
Tôi có thể thực hiện tất cả các thao tác trong JavaScript để quy trình này chạy tốt nhưng tôi có 1 giải pháp tốt hơn. Đúng là nó hiệu quả đấy nhưng tôi lại có cảm giác khó chịu là mình đang làm cái gì đó khá ngu ngốc.
<head>
tôi có 1 <link rel="preload" … >
dành cho cả JSON và JS (tôi cũng có prefetch
dành cho các browsers chưa hỗ trợ preload)<script>
fetch()
sẽ đẩy file JSON ra và .then()
sẽ khởi động render cho ứng dụng ReactKết quả tương tự như sau:
Khi asset bất kì gặp vấn đề, các calls tải asset khác sẽ không mất khỏi network. Các network calls sẽ trông như thế này:
Vì vậy, nếu tôi không lầm, tôi không thấy có lý do mà mình không preload mọi thứ ở đầu trang.
Câu chuyện bên lề: tôi đã muốn mã hóa tên file JSON để có thể lưu trong cache mãi mãi. Tôi đã tự phá luật và tiến tới npm luôn. Tôi loay hoay 1 hồi trước khi nhận ra thư viện crypto
được build ngay vào Node có thể qua mặt được mà không cần tốn quá nhiều sức.
1
|
const fileHash = crypto.createHash(‘md5’).update(fileContents).digest(‘hex’);
|
Những users đang xài Chrome, Edge và Firefox là những người tốt bụng. Tôi đã từng có các production sites gửi đi những polyfills 30KB hàng triệu lần đến các users này và khiến tôi cảm thấy “lầy lụa” không chịu được.
Với dự án này, tôi chỉ tạo 1 file polyfill riêng và tải nó nếu cần. Trong HTML của tôi, tôi có 1 thứ như thế này:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
var scripts = [‘app.a700a9a3e91a84de5dc0.js’]; // script for all users
var newBrowser = (
‘fetch’ in window &&
‘Promise’ in window &&
‘assign’ in Object &&
‘keys’ in Object
);
if (!newBrowser) {
scripts.unshift(‘polyfills.a700a9a3e91a84de5dc0.js’); // script for the less fortunate
}
scripts.forEach(function(src) {
var scriptEl = document.createElement(‘script’);
scriptEl.src = src;
scriptEl.async = false;
document.head.appendChild(scriptEl);
});
|
Để generate 2 gói này với webpack rất đơn giản.
1
2
3
4
5
6
7
8
9
|
config.entry = {
app: [
path.resolve(__dirname, `../app/client/client.jsx`),
],
polyfills: [
`babel–polyfill`,
`whatwg–fetch`,
],
};
|
Nhờ đó, kích cỡ build của tôi giảm từ 90KB xuống 60KB.
Bạn có nhận ra mãi cho đến bây giờ tôi vẫn chưa đề cập đến kích cỡ tải? Là vì kích thước file không có liên quan.
Nếu bạn đang tính toán “thời gian tương tác” của trang web thì bạn đã tính đến kích cỡ file; cả thời gian tải và thời gian parse. Nếu bạn nghe ai đó nói là họ đã giảm file CSS đi 5KB thì hãy hỏi con số đó theo milliseconds.
Dưới đây là biểu đồ về kích thước file của ứng dụng mà tôi đã lập trình và React với tất cả các polyfills trước và sau khi có Preact.
Nếu bạn muốn làm rõ và điều chỉnh các polyfills cho mỗi hệ điều hành, polyfill.io đã làm điều đó. Dưới đây là 1 số lý do sẽ khiến bạn “mê mệt” polyfill:
Vì thế tôi chỉ đơn giản phục vụ cái package nhỏ nhất cho những ai đang sử dụng các hệ điều hành tốt và tất cả những người khác có thể đạt được mức 30KB.
(Tôi nhận ra mình khá xấu xa khi loại Safari ra – Safari 10 có hỗ trợ JavaScript – nhưng không có fetch
tôi sợ chúng sẽ không tạo danh sách các browsers hiện đại được)
Tôi đã trì hoãn nghiên cứu các service workers trong 1 thời gian dài. Khi tôi quyết định tạo trang web nhanh nhất hành tinh này, tôi nghĩ lý do đến từ thời gian.
5 tiếng sau, tôi hoàn thành. Và 4 giờ, 35 phút là để phạm lỗi lầm.
Dưới đây là cách thức vận hành:
index.html
của tôi)sw-precache
của Google để tạo file service worker sẽ cache mỗi file trong danh mục đó và cho phép trang của tôi làm việc offline đượcsw-precache
đã tạoCó 16 dòng code bắt buộc phải có.
13 dòng trong build script (tất cả các code của tôi nằm trong /public
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
swPrecache.write(path.resolve(__dirname, `../public/service–worker.js`), {
cacheId: `know–it–all`,
filename: `service–worker.js`,
stripPrefix: `public/`,
staticFileGlobs: [
`public/app.*.js`, // don’t include the polyfills version
`public/*.{html,ico,json,png}`,
],
dontCacheBustUrlsMatching: [
/\.(js|json)$/, // I’m cache busting js and json files myself
],
skipWaiting: true,
}, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
|
(Tôi không tải polyfill lên cache vì các browsers cần polyfills không có service workers)
Sau đó trong 3 dòng của client – nơi tôi cần tải service worker:
1
2
3
|
if (`serviceWorker` in navigator) {
navigator.serviceWorker.register(`service–worker.js`);
}
|
Một khi bạn đã tải trang 1 lần, nó sẽ vận hành mà không cần network. Nếu có sẵn phiên bản mới, nó sẽ cài đặt trong background nếu bạn đang online và khi refresh bạn sẽ có phiên bản mới.
Tương lai nằm ở đây. Hãy sử dụng service workers. HÃY DÙNG NÓ.
Không may là 50% số người đọc được điều này trên Safari/iOS vào thời điểm này lại không có service workers. Apple chắc chắn đang nghiên cứu nhưng trái lại, nếu bạn muốn sở hữu Internet nhanh thì hãy mua 1 chiếc Android.
Web fonts là “nỗi đau”, là những thứ tốn thời gian. Nhưng nếu có được web fonts đẹp thì cũng tốt.
Tôi nhận ra 4 phần sau đây:
Vậy tại sao lại không dùng chúng? Tôi đã chọn Calibri Light, Roboto và Helvetica Neue. Nếu bạn cần 1 font giống nhau, tùy chỉnh trên tất cả các thiết bị thì mọi thứ đã đi quá xa rồi và bạn không còn hy vọng nào đâu.
Vì vậy, đây là lý do tôi nghĩ mỗi website đơn nên có nền tảng typography.
Text đẹp, không request network.
1
2
3
4
5
6
7
|
body {
color: #212121;
font–family: “Helvetica Neue”, “Calibri Light”, Roboto, sans–serif;
–webkit–font–smoothing: antialiased;
–moz–osx–font–smoothing: grayscale;
letter–spacing: 0.02em;
}
|
Chính sửa: Ban đầu tôi có text-rendering: optimizeLegibility
. Một ai đó đã nói trong comment rằng đây là 1 vấn đề liên quan đến hiệu suất.
Sau đó, tuy có chút ngần ngại nhưng tôi đã chạy vài trials và xem thử có khác biệt nào không.
Cảm ơn, Jacob Groß!
app.js của tôi vào khoảng 28KB và tôi đã tự hỏi nó được tạo ra như thế nào. Sau khi fiddle, tôi nhận ra ImmutableJS chiếm 19KB. Một thư viện nhỏ mà đã chiếm hơn 2/3 toàn bộ kích cỡ app!
Tôi chỉ sử dụng 1 tập hợp rất nhỏ các tính năng của ImmutableJS và nhận ra tôi có thể tự mình sao chép lại. Sau đó vài giờ, website có thể hoạt động mà không cần ImmutableJS và độ tăng trưởng về hiệu suất tốt hơn bất cứ thay đổi nào mà tôi từng thực hiện: giảm 60% thời gian tải.
Lý do không phải vì ImmutableJS “chậm”. Nó có thể chiếm 19KB của Javascript bất kì. Vấn đề đến từ thời gian để parse.
Tôi đã xử lý thành công 14% thời gian “first view” (và dưới 400 đối với “repeat view”) bằng cách loại immutable và không chuyển đổi file big data client-side đó.
Xây dựng 1 trang web nhanh cũng như nuôi thú cưng, đòi hỏi sự kiên nhẫn và nhất quán (cả về thời gian và từ những người liên quan). Bạn có thể giữ mọi thứ rất sạch sẽ, gọn gàng nhưng nếu bạn cẩu thả, sử dụng 1 thư viện 11KB để format date và để đống phân thú cưng đó trên giường, bạn sẽ gặp nhiều khó khăn để dọn dẹp sạch sẽ.
Nguồn: Techtalk via Hackernoon