Bài viết này phân tích về những kĩ thuật tăng hiệu suất của 1 trang web, vì thế hy vọng bạn sẽ không phiền nếu trang web được nói đến vẫn chưa hoàn thiện lắm.
Nhưng bạn cần có 1 cái gì đó để click vào và đánh giá xem ý kiến của tôi có giá trị hay không. Bạn có thể tham khảo tại đây: https://knowitall-9a92e.firebaseapp.com/
Hy vọng trang web sẽ mở nhanh để tôi có được chút danh tiếng ban đầu.
Nếu bạn đang tự hỏi tại sao tôi lại đang viết về trang web này khi nó vẫn chưa hoàn thiện.. là vì những góp ý. Hiệu suất là tập hợp chuỗi mẹo và các cách suy nghĩ về mọi việc. Vì vậy tôi sẽ rất vui nếu bạn có thứ gì đó hay ho để góp ý cho website bằng cách truy cập source tại đây.
Khi chế độ Strict Mode được bật, nếu tham số truyền vào là string, trong khi chúng
Hãy nói về tốc độ
Bạn có thể tham khảo biểu đồ sau.
Liệu bạn có muốn website được cách điệu 1 cách không cần thiết?
Đây có phải là cuộc chiến công bằng?
Không. Những trang web trên có chức năng hoàn toàn khác biệt, nên sẽ rất vô lý nếu chúng tải cùng 1 thời gian. Nhưng những trang web trên đã rất nỗ lực để cải thiện hiệu suất và đạt mục tiêu tạo được 1 trang web tải nhanh hơn trang homepage của Google.
Tại sao trông chúng lại không ấn tượng
Nếu bạn vô tình cảm thấy ấn tượng, tôi khuyên bạn nên bình tĩnh 1 chút. Tôi không phải read từ 1 database để sản xuất content hay bắt bạn đăng nhập hay tải 7 loại ads từ bên thứ 3 – mỗi cái thực hiện 40 redirects trước khi chúng tải được 1 file flash. Tôi thậm chí còn không dùng đến bất kì hình ảnh nào nữa cơ. Và page count = 1.
Tại sao chúng lại gây ấn tượng
Trước khi trang web được tải và sẵn sàng chạy, tôi đang tải về và parse 1 file JSON 75.000 line. Nếu được expanded, sẽ cho ra kết quả là 9.986 hàng.
Và tôi đang xài 1 thư viện. Các thư viện đều chậm (bất kể tốc độ nhanh chóng mà chúng đang có).
Dưới đây là 10 bài học mà tôi có được trong suốt hành trình đã qua. 7 trong số chúng khá hữu hiệu ở thời điểm cách đây 2 tháng, 3 cái còn lại thì khá… rác rưởi.
Tất nhiên, tôi sẽ không nói bạn đâu là 3 cái đó.
#1 Đừng cố gắng tạo 1 trang web chậm
Tôi nghe được điều này từ 1 người không phải là lập trình viên web. “Tôi đang lướt trang mercedes.com và nó quá chậm, làm thế nào mà họ có thể tạo được 1 trang chậm như thế”
Tôi cho rằng việc tạo nên 1 trang web chậm thì chẳng khó khăn gì, tất cả những gì bạn phải làm là cố gắng không phải tạo 1 trang web nhanh.
Đây là tin tức tốt, vì nếu tất cả những gì bạn làm là cố gắng tạo trang web nhanh, bạn sẽ tự động có được 1 trang web nhanh hơn.
Đối với trang web này, trong mỗi bước, tôi lại dành ra vài thời điểm để nghĩ về mức độ ảnh hướng đến hiệu suất trong những gì mình đang làm. Với mỗi thư viện mà tôi đã sử dụng trong ứng dụng này, tôi đo đạc lại 3 chỉ số này, trước và sau:
- first meaningful paint
- thời gian interactive
- expanding 1 DOM node
Nếu 1 thư viện có ảnh hưởng tiêu cực, hãy bỏ nó ra. Ví dụ, tôi đã từng dùng lodash deepClone
trong đối tượng JS có 75.000 prop. Sau khi chuyển sang Immutable.js
thì mọi chuyện đã hoàn toàn khác.
Tôi đang sử dụng React, và thỉnh thoảng dùng đến thư viện classnames
. Sau đó tôi ước lượng lại bằng số liệu lần nữa và… không có gì khác biệt.
Mỗi lần bạn nhập 1 thư viện mới hoặc tạo nên 1 sự thay đổi lớn để nâng cao hiệu suất, bạn chỉ tốn có 5 phút mà thôi..
#2 Do mobile first
Có 2 chiến lược “mobile first” ở đây.
Chiến lược đầu tiên (tôi vẫn làm mãi cho đến dự án này), tôi đã ngồi tại monitor 27″ với 1 style website imax trước mặt, 1 fan-cooled CPU “quái vật” và hàng tá RAM thực hiện mọi mệnh lệnh mà tôi đặt ra.
Sau đó, tôi viết CSS media queries với min-width
và nói với bạn bè của mình rằng, tôi đang thực hiện chiến lược “mobile first”
Đối với dự án này, tôi thực sự đã làm mobile first. Đó là, phát triển trang web chạy trên thiết bị di động. Tôi làm điều này trước, và khi đã hài lòng về UI và hiệu suất, tôi tiến hành nó trên máy tính lớn.
Bạn sẽ bất ngờ khi nhận ra không hề khó để có 1 trang web nhanh chạy trên 1 machine nhanh!
(Câu chuyện không đơn giản như thế, tôi đã bắt đầu dự án với những thói quen cũ không tốt, sau đó nửa đường thì dần trưởng thành, học hỏi được nhiều hơn và đã mặc định là bắt đầu lập trình trên mobile)
Bây giờ, việc trang web chỉ chạy trên 1 chiếc điện thoại là 1 giấc mơ, nhưng khi so sánh các chỉ số về hiệu suất sau nhiều ngày và nhiều tuần, bạn muốn 1 benchmark nhất quán. Nếu bạn xem video này, bạn sẽ biết rằng testing trên 1 thiết bị mobile thực tế không hề chuẩn mực chút nào. Bạn cũng sẽ biết tại sao tôi lại nói 1 cái CPU – được – quạt – mát sẽ đóng vai trò rất quan trọng.
Khi bạn thực hiện benchmarking, bạn nên sử dụng Chrome DevTools and throttle CPU và network của bạn. Tôi sử dụng CPU giảm tốc 10x lần và để network ở chế độ “Good 3G”. Tôi biết nó có thể không chậm bằng chiếc điện thoại trung bình nhưng tôi không muốn bị làm phiền với các vấn đề về tốc độ.
Bởi vì không dừng lại ở 1 cái gật đầu và lời đồng tình rằng đây là ý tưởng tốt mà nó còn phải là 1 ý tưởng thực sự có giá trị.
Đây là 1 thứ khiến tôi khá ngạc nhiên: chúng thực sự rất ồn.
Đây là thứ khác khiến tôi ngạc nhiên: tôi có 1 CPU i7 lớn mà tôi sử dụng để làm hầu hết mọi việc và 1 Pixel XL hoàn toàn mới: chiếc điện thoại nhanh nhất trên thế giới. Theo bạn thì hiệu suất điện thoại của tôi sẽ ra sao? 80% desktop? 60%? Hay có ai đoán là trên 10% không?
Sai rồi! Chỉ có 10% mà thôi. Nếu tôi làm chậm i7 lại khoảng 10 lần, nó cũng bằng tốc độ với chiếc điện thoại 1400USD trước mặt tôi.
Đó là sự khác biệt giữa cái click 20ms và click 200ms. Hoặc 1 khung hình tốn 16ms để render với 160ms.
#3 Trở thành 1 kẻ rành về benchmark
Một khi đã nhận được nhận xét tốt trên 1 trang benchmarking, tôi lại càng muốn tiến hành với tất cả các trang benchmarking để được nghe lời khen nhiều hơn.
Khi tôi chạy lighthouse trên trang web, tôi chỉ nhận được 97 điểm. 3 điểm còn lại của tôi đã mất đi đâu rồi!!!
Như tôi đã thấy, tôi được nhận xét là tôi có 1 input latency 285ms. Nếu nó thực sự đúng, thì sẽ là chuyện khá kinh hãi. Nhưng tôi biết nó chỉ 20ms thôi.
Vậy thì nhân viên của lighthouse chẳng qua là những kẻ ngu ngốc.
Vì vậy, tôi ngập ngừng thừa nhận với bản thân rằng có lẽ tôi nên nghiên cứu điều này, dù thực tế cho thấy tôi đã hoàn toàn đúng và 1 thuật toán được viết bởi Google đã sai rõ ràng.
Nó nhắc tôi phải bắt đầu lại toàn bộ quá trình giảm tốc CPU, và chắc chắn, thời gian phản hồi mà tôi nghĩ là nhất thời đã chậm đến 200+ ms.
Vì vậy tôi đã làm vài profiling và hầu hết thời gian là dành cho React. Tôi đã thực hiện hầu hết các practices tốt nhất liên quan đến hiệu suất React, và không có “updates nào bị lãng phí”.
Tôi thậm chí còn memoize (memoization là một kỹ thuật tối ưu, nhằm tăng tốc chương trình bằng cách lưu trữ kết quả của các câu gọi function và trả về các kết quả này khi function được gọi với cùng input đã gọi) các components có số lượng phần tử thấp (low-cardinality)
Bạn cũng nên biết tôi là 1 fan bự của React. Nhưng hiệu suất đã đánh bật lòng trung thành đó.
Đầu tiên, tôi đã thử preact-compat
. Nó tốn mất 15 phút để convert codebase cả tôi. Một sự tiến bộ vượt bậc.
Tôi kể chuyện này với 1 người bạn từng viết Preact và anh ấy nói tôi nên thử đầy đủ Preact và wowsers vì nó thậm chí còn nhanh hơn.
Dưới đây là biểu đồ tôi có được.
Có người còn bảo tôi hãy thử Inferno nên tôi đã chuyển ứng dụng của mình sang inferno
để siết chặt thêm về hiệu suất.
Đây chính là kết quả thu được.
Đúng vậy tôi đã thử, Inferno nhanh nhưng không nhanh bằng Preact nên tôi rút lại thay đổi đó.
Tôi đã học được bài học ở đây rằng đừng ngần ngại gạch bỏ công việc. Nhưng bạn cũng nên rụt rè 1 chút. Không ai lại thích 1 kẻ hướng ngoại nói “nhiều” đâu.
Và đây…
Tiếp theo, tôi test trên yslow. Tôi gần như đã đạt được những điểm số cao nhất, ngoại trừ 1 con D (!) vì có quá nhiều DOM nodes. Điều buồn cười là vì tôi biết số lượng DOM nodes mà tôi cần và không ai khác có thể nói tôi nên làm cái gì vì tôi chính là người quản lý của chính tôi.
Có vẻ như những nhân viên của yslow đã sai.
Vì vậy tôi đã khá ngần ngại khi nghĩ đến việc tôi có thể giảm số lượng DOM nodes mà mình đã render. Vì thế, tôi đã thay đổi các nhánh trong tree của mình đã được expand mặc định.
Đây là biểu đồ dành cho bạn!
Cảm ơn các nhân viên của yslow, có vẻ như gợi ý ở trên khá tốt đấy.
#4 Render Client Side rất tốn kém
Client Side Rendering (CSR) cũng có những lợi điểm nhưng đối với trang web này thì lại vô dụng.
Tôi không có bất cứ phần nào dành riêng cho người dùng trên trang của mình nên tôi gửi cùng 1 HTML đến tất cả mọi người. Và tôi cũng có 1 nhiệm vụ processing khá năng thực hiện về phần client, khiến CSR ngày càng tệ hơn. CSR không phải là cách nhanh nhất để các pixels của tôi tiếp cận được ánh nhìn của bạn.
Đây là lời khuyên của tôi nếu bạn đang xây dựng 1 trang web cho 1 công ty có – và mong muốn duy trì dòng doanh thu:
- Hãy xem bảng phân tích của bạn và xem thử phần trăm traffic đến từ Bing là bao nhiêu (đối với tổ chức của tôi là 1,6%)
- Đúng vậy là Bing vì chúng không xài JS khi index và vì vậy không index 1 trang web CSR nào
- Giúp thu nhập hằng năm của nhân viên bạn tăng 1,6%
- Hãy hỏi công ty nếu họ hạnh phúc với số tiền được đưa vào cuộc cạnh tranh bởi vì bạn sẽ không thu được các kết quả tìm kiếm Bing nữa đâu
Có lẽ bạn đã hiểu. Tôi đã đi lạc đề.
Chỉ cần gửi HTML đã render từ server của bạn.
#5 Đừng server-render HTML
Đúng là nghe có vẻ nghịch lý. Làm thế nào bạn serve HTML mà không server-render HTML? Bạn làm đúng những gì mà mọi người có thể đã làm vào những năm 90…
React (và họ hàng nhanh hơn của nó) cần có vài tá milliseconds để render 1 trang HTML khiêm tốn. (Ai đó có số liệu về thời gian PHP hoặc JSP không? Tôi rất muốn so sánh). Điều đó đồng nghĩa là 1 single core chỉ phục vụ được khoảng 50 người trên mỗi giây, các requests thêm sẽ phải xếp hàng chờ đợi ngay ngắn và điều này không tốt chút nào.
Đối với trang web nhỏ của mình, tôi đang gửi cùng HTML vào mỗi response. Nếu trang của bạn tương tự thì bạn không cần 1 web server để render HTML và gửi đi các files CSSs và JS theo yêu cầu. Bạn có thể xuất HTML của mình vào thời điểm build time và ra mắt toàn bộ từ static hosting (hoặc cache it trên 1 CDN tốt). Github và Firebase và khả năng là những người khác sẽ cho bạn static hosting vì chúng rất thân thiện.
Ý tưởng này không áp dụng được nhiều nên bạn có thể bỏ qua phần này. Nhưng nếu bạn có bất kì trang nào có thể được render vào build time (như trang chủ của Linkedin, Paypal, Github) và tận dụng nó.
Tôi nghĩ ra ý tưởng này khi đang xem vài blog, nhận ra tôi chỉ tốn mất 96 milliseconds để nhấn vào link dẫn đến blog đã nói (nhưng nhìn chung tôi vẫn cho rằng trang của tôi là trang nhanh nhất trên thế giới – và thực tế đôi khi lại không công nhận điều đó)
Tôi đã phát hiện blog được hosted như 1 static site với Firebase. Nhưng điều đó đồng nghĩa là tôi phải generate HTML vào build time. .
Giá như React có thể output DOM đã generated như 1 string. Như thế tôi có thể chỉ cần lưu string đó vào 1 file HTML như 1 phần của build script.
Vốn dĩ React đã có 1 method có tên là renderToString
(mặc dù bạn nên sử dụng renderToStaticMarkup
).
(Đây là cơ hội tốt để chạy HTML qua 1 minifier trước khi lưu vào đĩa).
Mời bạn đọc tiếp phần 2
Nguồn: Techtalk via Hackernoon (còn tiếp)