Nguồn: https://developer.mozilla.org/en-US/docs/Web/Performance/How_browsers_work
PS: Bản dịch sẽ có sai sót về lỗi chính tả hoặc hướng nhìn chủ quan của người dịch, các bạn nên đọc kỹ bài gốc để có thể nắm rõ hơn về kiến thức được chia sẽ trong bài.
Người dùng luôn thích những website load nhanh, chạy mượt khi thao tác, tương tác. Hai tiêu chí đó luôn là mục tiêu chính của các web developer trong quá trình phát triển một website hoặc một web ứng dụng.
Để có thể lĩnh hội được cách tăng performance cho một website, chúng ta trước tiên cần phải hiểu được cụ thể browser hoạt động như thế nào và đó sẽ là nội dung chính trong bài viết hôm nay.
Overview
Một trang web tải nhanh sẽ luôn cung cấp một trải nghiệm tốt cho user. Người dùng luôn mong đợi một trang web chạy nhanh, mượt mà và mỗi lần tương tác với website đều sẽ nhận được những trải nghiệm smoothy nhất. Hai vấn đề trên luôn luôn bị hạn chế bởi độ trễ và các vấn đề xoay quanh việc phần lớn các trình duyệt đều chạy đơn luồng (single-thread).
Latency (độ trễ) luôn là issue lớn nhất đối với việc đảm bảo trang web của bạn có thể load nhanh. Mục tiêu của các nhà phát triển là bằng cách nào đó phải khiến cho trang web tải nhanh nhất có thể (hoặc ít nhất thì cũng phải làm cho nó trông có vẽ tải nhanh) giúp người dùng nhận được response nhanh nhất có thể.
Latency network (độ trễ mạng) là thời gian để truyền tải các bytes giữ liệu từ nơi này đến nơi kia thông qua internet. Còn về hiệu xuất của website (Web performance) chính là nơi chúng ta phải tập trung để có thể cải thiện được hiệu năng của website một cách tốt nhất.
Phần lớn, các browser được coi là đơn luồng (single-threaded). Nghĩa là nó sẽ thực thi theo thứ tự hết task này tới task khác từ đầu đến cuối. Để tương tác có thể mượt mà nhất, các developer sẽ phải xử lý từ việc scroll trang hay việc phản hồi nhanh khi user tương tác với các component của website. Thời gian render là yếu tố chính đảm bảo luồng chính có thể hoàn thành tất cả các công việc mà nó cần thực hiện và luôn sẵn sàng xử lý các tương tác của người dùng. Hiệu suất của web có thể được cải thiện khi chúng ta hiểu được bản chất single-thread của browser, giảm thiểu workload của main thread ở thời điểm phù hợp và khả thi, luôn đảm bảo được website sẽ render suôn sẻ và luôn phản hồi ngay lập tức các tương tác của người dùng.
Navigation (điều hướng)
Navigation là bước đầu tiên để có thể load được một website, nó xãy ra mỗi khi user yêu cầu trình duyệt duyệt một web page khi họ nhập domain URL của website lên address bar của trình duyệt hoặc thực hiện một số action như click vào một link, submit một form, click button, vv ...
Một trong số mục tiêu cực kỳ quan trọng trong việc tăng performance của website đó là tìm cách giảm thời gian mà navigation dành ra để hoàn thành. Trong trường hợp lý tưởng thì vốn dĩ nó cũng không tốn quá nhiều thời gian, có điều đôi khi nó sẽ bị chậm do network latency hoặc do bandwidth (băng thông) không tốt, không ổn định.
DNS (Domain Name System) lookup
Bước đầu tiên của việc điều hướng tới một website là phải xác định được source build của trang đó nằm đâu. Nếu bạn navigate đến domain https://example.com
, nghĩa là trang HTML của nó sẽ được đặt ở server có IP address là 93.184.216.34
. Nếu đó là lần đầu tiên bạn truy cập tới trang web thì DNS lookup sẽ diễn ra.
Browser của bạn sẽ gửi một yêu cầu DNS lookup thường được cung cấp bởi một máy chủ định danh nào đó và sẽ được phản hồi lại một địa chỉ IP address. Sau yêu cầu đầu tiên này, IP address đó sẽ được lưu vào bộ nhớ đệm trong một thời gian nhất định, như vậy sẽ giúp tăng tốc các request sau này bằng cách truy xuất địa chỉ IP từ bộ nhớ đệm thay vì liên hệ tới máy chủ định danh.
DNS lookup thường sẽ diễn ra một lần cho mỗi hostname (tên máy chủ) của một page load. Tuy hiên DNS lookup sẽ hoàn thành cho từng unique hostname mà web website của bạn có bắt link tới. Nghĩa là nếu font, imgs, một số tập tin, hoặc link quảng cáo được host ở các máy chủ khác, lúc đó DNS lookup sẽ chạy để tra cứu cho từng máy chủ với các hostname khác nhau.
Điều này có thể gây ra vấn đề về hiệu suất, đặc biệt là trên mạng di động. Khi người dùng sử dụng mạng di động, mỗi lần tra cứu DNS phải đi từ điện thoại đến tháp di động để đến máy chủ DNS có thẩm quyền. Khoảng cách giữa điện thoại, tháp di động và máy chủ tên có thể làm tăng thêm độ trễ đáng kể.
TCP handshake
Khi IP Address đã được tìm thấy, browser sẽ khởi động một kết nối tới server thông qua một giao thức gọi là TCP three-way handshake. Cơ chế này được thế kế sao cho 2 thực thể đang cố gắn kết nối với nhau - ở đây là máy client và máy server - có thể xem xét các parameters của các kết nối TCP sockets trước khi truyền dữ liệu qua lại với nhau, thường là truyền dữ liệu thông qua HTTPs.
Kỹ thuật TCP's three-way handshaking thường được gọi là "SYN-SYN-ACK" hoặc chính xác hơn là SYN, SYN-ACK, ACK bởi vì sẽ có 3 message được truyền đi bởi TCP để xem xét và bắt đầu session giữa hai máy tính với nhau. Vâng, điều này có nghĩa là sẽ có thêm 3 messages nữa được gửi đi và gửi về giữa mỗi máy chủ, và lúc đó request vẫn chưa được thực hiện.
Đọc thêm bài này để hiểu kỹ hơn về quá trình handshaking của giao thức kết nối TCP.
TLS negotiation
Đối với các kết nối mang tính bảo mật được thiết lập bởi HTTPs thì lại cần một cái bắt tay khác (another "handshake"). Cái bắt tay này nói đúng hơn thì sẽ là TSL negotiation, nó dùng để xác định mật mã nào sẽ được sử dụng để mã hoá thông tin liên lạc, xác minh máy chủ và thiết lập rằng một kết nổi an toàn đã sẳng sàng trước khi dữ liệu thực sự được truyền đi. Điều này đòi hỏi thêm 5 chuyến đi khứ hồi tới máy chủ trước khi request yêu cầu content thực sự được gửi đi.
Mặc dù việc đảm bảo kết nối an toàn sẽ tốn thời gian vì phải tăng thêm thời gian tải trang nhưng bù lại dữ liệu được truyền giữa trình duyệt và máy chủ web không thể được giải mã bởi bên thứ ba.
Sau tám chuyến đi tới máy chủ, trình duyệt cuối cùng cũng có thể thực hiện được việc gửi request tới server.
Response
Sau khi việc thiết lập kết nối với server đã hoàn thành, browser sẽ thay mặt user gửi một request HTTP đầu tiên đến server. Đối với các trang web, yêu cầu này thông thường là để tải file HTML. Một khi server nhận được request, nó sẽ trả lời lại bằng một response headers tương ứng và nội dung trang HTML.
<!doctype html>
<html lang="en-US"> <head> <meta charset="UTF-8" /> <title>My simple page</title> <link rel="stylesheet" href="styles.css" /> <script src="myscript.js"></script> </head> <body> <h1 class="heading">My Page</h1> <p>A paragraph with a <a href="https://example.com/about">link</a></p> <div> <img src="myimage.jpg" alt="image description" /> </div> <script src="anotherscript.js"></script> </body>
</html>
Response cho HTTP request đầu tiên này sẽ chứa các byte dữ liệu đầu tiên mà browser sẽ nhận được. Time to First Byte (TTFB) sẽ là thời gian mà user gửi request - (click link, button, url domain, ....) cho đến khi nhận được gói HTML đầu tiên này. Đoạn nội dung đầu tiên thường chứa khoảng 14KB dữ liệu.
Trong file HTML ví dụ của chúng ta ở trên, response cho request này chắc chắn nhỏ hơn 14KB, nhưng các link chứa trong HTML vẫn sẽ chưa được browser bắn request đi cho đến khi nó bắt gặp các link này trong quá trình parsing HTML, quá trình này sẽ được mô tả dưới đây.
Congestion control / TCP slow start (kiểm soát tắt nghẽn / thuật toán Khởi động chậm của TCP)
Trong quá trình truyền dữ liệu, các gói giữ liệu TCP sẽ được chia thành các segments (các phân đoạn). Bởi vì TCP sẽ đảm bảo trình tự của các các TCP packets nên là máy server sẽ phải nhận được một xác nhận rằng đã nhận được data từ máy client dưới dạng một ACK packet sau khi nó gửi đi một số segment của TCP packets nhất định.
Nếu như server đợi một ACK packets sau mỗi lần gửi đi một segment data, điều đó có nghĩa là server sẽ phải nhận liên tục các ACK message từ máy client làm tăng thời gian truyền tải ngay cả trong trường hợp mạng tải thấp.
Ngược lại nếu server gửi quá nhiều các segement data cũng một lúc, thì trong trường hợp mạng chậm hoặc quá tải, client sẽ không thể nhận được hết các segment đó và sẽ không gửi ACK message về cho server, và server sẽ cứ phải re-sending lại các segments data đó về client.
Để cân bằng số segements được truyền, thuật toán khởi động chậm TCP được sử dụng để tăng dần lượng dữ liệu được truyền cho đến khi có thể xác định được băng thông mạng tối đa và để giảm lượng dữ liệu được truyền trong trường hợp tải mạng cao.
Số lượng segments được truyền sẽ được kiểm soát bởi giá trị của congestion window (CWND) gọi là cửa sổ tắc nghẽn. Có các giá trị khởi tạo là 1, 2, 4 hoặc 10 MSS ( 1MSS bằng 1500 byte qua giao thức Ethernet). Các giá trị đó là số bytes được gửi đi trước, và nếu nhận được đủ số bytes thì mày client sẽ phải gửi lại ACK message cho máy server.
Sau nhận được ACK message thì giá trị của CWND sẽ tăng lên gấp đôi, nghĩa server sẽ dựa theo đó để gửi nhiều lương segments của data hơn vào lần tiếp theo. Ngược lại nếu không nhận được ACK từ client, giá trị của CWND sẽ giảm đi một nữa. Do đó, cơ chế này sẽ đạt được sự cân bằng giữa việc gửi quá nhiều segments và gửi quá ít segments.
Parsing
Ngay khi browser nhận được đoạn data đầu tiên, nó đã có thể bắt đầu phân tích thông tin nhận được. Parsing là bước đầu tiên mà browser thực hiện để chuyển hoá dữ liệu mà nó nhận được thông qua network thành DOM và CSSOM, trình suất kết (the renderer) sẽ sử dụng đó để vẽ nên website và hiển thị lên màn hình.
DOM là một đại diện thể hiện markup language ở dạng internal của browser. DOM cũng có thể được hiển thị và được điều khiển thông qua các APIs của javaScript.
Ngay cả khi trang HTML trong request đầu tiền được trả về lớn hơn 14KB, trình duyệt vẫn sẽ bắt đầu phân tích cú pháp và cố gắng hiển thị trãi nghiệm dựa trên dữ liệu mà nó có. Đó là lý do vì sao việc website cần có mọi thứ mà trình duyệt cần để có thể hiển thị một trang web, hoặc ít nhất là một mẩu của trang web ngay trong 14KB đầu tiên - Ở đây CSS & HTML cần phải có đầy đủ cho lần render đầu tiên - là điều cực kỳ quan trọng trong việc tối ưu hoá hiệu suất website. Và trước khi mọi người có thể được hiển thị trên màn hình thì HTML và CSS cần phải được parsed (phân tích cú pháp).
Đọc thêm về parsing ở đây
Building the DOM tree
Chúng tôi sẽ mô tả năm bước trong lộ trình kết xuất quan trọng (the critical rendering path) của browser.
Bước đầu tiên là xử lý đánh dấu HTML và xây dựng cây DOM. Phân tích cú pháp HTML liên quan đến HTML tokenization và xây dựng cây DOM. HTML tokens bao gồm thẻ bắt đầu và thẻ kết thúc cũng như tên và giá trị thuộc tính. Nếu tài liệu được định dạng đúng thì việc phân tích cú pháp sẽ đơn giản và nhanh hơn. Trình phân tích cú pháp của browser sẽ phân tích dữ liệu đầu vào được mã hóa và gắn vào document từ đó xây dựng nên cây DOM.
Cây DOM dùng để mô tả nội dung của tài liệu. Phần tử html là phần tử đầu tiên và root node của cây DOM. Cây DOM này sẽ phản ánh mối quan hệ và thứ bậc giữa các phần tử khác nhau. Các phần tử lồng trong các phần tử khác là các node con. Số lượng node của DOM càng lớn thì thời gian xây dựng cây DOM càng lâu.
Khi trình phân tích cú pháp và tìm thấy các non-blocking resources, chẳng hạn như hình ảnh, trình duyệt lúc này sẽ gửi request đến server để tải tiếp các resources đó và tiếp tục phân tích cú pháp. Quá trình parsing có thể tiếp tục khi gặp tệp CSS, ngoại trừ các thẻ <script>
— đặc biệt là những thẻ không có thuộc tính async hoặc defer — chặn hiển thị và tạm dừng quá trình phân tích cú pháp HTML. Mặc dù trình quét tải trước (preload scanner) của trình duyệt đã đẩy nhanh quá trình này nhưng quá nhiều tập lệnh vẫn có thể là một nút thắt cổ chai đáng kể.
Preload scanner - trình quét tải trước
Trong khi trình duyệt xây dựng cây DOM ở luồng chính (main thread), preload scanner sẽ phân tích nội dung có sẵn và bắn request lên server yêu cầu tải các tài nguyên có mức độ ưu tiên cao như CSS, JavaScript và các file font. Nhờ có trình quét tải trước này mà chúng ta không cần phải đợi cho tới khi parser của browser tìm tới các thẻ tham chiếu tới các tài nguyên bên ngoài thì mới bắt đầu gửi request để tải các tài nguyên này. Nó sẽ truy suất các resource ở background để khi trình HTML parser phân tích tới các nội dung này thì chúng đã được tải sẳn về. Các thuật toán tối ưu hoá của preload scanner sẽ giúp website giảm thiểu được sự tắt nghẽn.
<link rel="stylesheet" href="styles.css" />
<script src="myscript.js" async></script>
<img src="myimage.jpg" alt="image description" />
<script src="anotherscript.js" async></script>
Trong ví dụ này, trong khi luồng chính đang phân tích cú pháp HTML và CSS, trình quét tải trước sẽ tìm thấy các tập lệnh và hình ảnh, đồng thời bắt đầu tải chúng xuống. Để đảm bảo tập lệnh không chặn quá trình, hãy thêm thuộc tính async hoặc thuộc tính defer nếu thứ tự thực thi và phân tích cú pháp JavaScript là quan trọng.
Việc chờ lấy CSS sẽ không chặn việc tải xuống hoặc phân tích cú pháp HTML nhưng nó chặn JavaScript vì JavaScript thường được sử dụng để truy vấn tác động của thuộc tính CSS lên các phần tử.
Building the CSSOM tree
Bước thứ hai trong critical rendering path chính là xử lý CSS và xây dựng cây CSSOM. CSS object model cũng tương tự như cây DOM cũng đều theo dạng cây và đều là các cấu trúc dữ liệu độc lập. Trình duyệt chuyển đổi các CSS rules thành một map of styles mà nó có thể hiểu và làm việc được trên đó. Trình duyệt duyệt qua từng bộ rule trong CSS, tạo ra một cây gồm các node có các mối quan hệ cha, con và anh chị em dựa trên các CSS selectors.
Giống như HTML, trình duyệt cần chuyển đổi các CSS rules nhận được thành thứ mà nó có thể hoạt động được. Cho nên nó sẽ phải lặp lại quá trình chuyển đổi HTML thành object nhưng dành riêng cho CSS. The CSSOM tree bao gồm luôn các style của user agent style sheet - là một tập các kiểu định dạng mặc định được áp dụng cho mọi trang web, mỗi browser sẽ có style sheet định dạng khác nhau. Trình duyệt bắt đầu với các css general rules được apply trên một số node, sau đó nó sẽ apply tiếp các rules khác theo dạng đệ quy. Nói theo một cách khác thì nó sẽ sắp sếp các css rules theo từng tần, từng lớp chồng lên nhau.
Việc xây dựng CSSOM diễn ra rất nhanh và không được hiển thị bằng một màu duy nhất trong các developer tools hiện tại. Thay vào đó, "Recalculate Style" trong các developer tools sẽ hiển thị tổng thời gian cần thiết để phân tích CSS, xây dựng cây CSSOM và tính toán đệ quy các computed styles css. Về mặt tối ưu hóa hiệu suất web, đây sẽ là mục tiêu dễ đạt được hơn vì tổng thời gian để tạo CSSOM thường ít hơn thời gian cần thiết cho một lần tra cứu DNS.
Other processes
JavaScript compilation
Trong khi CSS đang được phân tích cú pháp và CSSOM được tạo thì các nội dung khác, bao gồm cả tệp JavaScript, đang được tải xuống (nhờ trình quét tải trước). JavaScript sẽ được browser engine phân tích cú pháp, biên dịch và giải thích. Các tập lệnh được phân tích thành cây cú pháp trừu tượng (abstract syntax trees). Một số browser engines lấy abstract syntax trees này và chuyển chúng vào trình biên dịch sau đó xuất ra bytecode. Quá trình này được gọi là biên dịch JavaScript. Hầu hết code được diễn giải trên luồng chính nhưng vẫn có những trường hợp ngoại lệ chẳn hạn như các mã chạy trong web workers.
Building the accessibility tree
Browser cũng xây dựng accessibility tree để một số thiết bị hỗ trợ có thể sử dụng để phân tích và diễn giải nội dung trên website. Accessibility object model (AOM) giống như một phiên bản ngữ nghĩa của DOM (semantic version của DOM). Trình duyệt sẽ cập nhật accessibility tree sau khi DOM được cập nhật. Bản thân các công nghệ hỗ trợ không thể sửa đổi cây trợ năng này.
Nếu cây AOM chưa được build xong, trình đọc màn hình (sceen reader) sẽ không thể truy cập được nội dung của website.
Render
Các bước kết suất (rendering) sẽ bao gồm style, layout, paint hoặc trong một số trường hợp là compositing (tổng hợp tất cả). Cây CSSOM và DOM được tạo trong bước phân tích cú pháp được kết hợp thành cây kết xuất, sau đó được sử dụng để tính toán bố cục của mọi phần tử hiển thị, sau đó được vẽ lên màn hình. Trong một số trường hợp thì content có thể được vẽ riêng ra một lớp và sau đó được render riêng một cách tổng hợp (paint, layout, style) sau đó mới đắp lên layout, nhằm cải thiện hiệu suất bằng cách vẽ từng phần lên GPU thay vì là CPU để giải phóng được main thread.
Style
Bước thứ ba trong critical rendering path là kết hợp DOM và CSSOM thành cây kết xuất. Cây style đã được tính toán, hoặc cây render sẽ bắt đầu thực hiện từ node root của cây DOM, sau đó duyệt qua từng node đang hiển thị của DOM. Một số phần tử sẽ không được hiển thị, như phần tử <head>
và các phần tử con của nó, hay một số node có style display: none vi dụ script { display: none; }
có trong user agent stylesheets sẽ không được bao gồm trong render tree và sẽ không được hiển thị lên màn hình sau khi browser hoàn thành việc render. Các nodes có visibility: hidden
thì sẽ được bao gồm trong render tree, nghĩa là chúng vẫn chiếm space nhưng sẽ không được hiển thị ở output. Cho tới step hiện tại thì chúng ta vẫn chưa có chị thị nào về việc overwrite user agent default style nên đoạn code ví dụ script { display: none; }
sẽ vấn chưa được được bao gồm trong cây render.
Mỗi visible node trên DOM đều sẽ có CSSOM rules riêng được áp dụng cho nó. Render tree sẽ chứa tất cả các visible node có content và cả computed style của chính nó - nó sẽ map style cho tất cả các nodes trên DOM dựa theo cơ chế thác nước (cascade) của css sau đó tính toán style và apply cho từng nodes.
Layout
Bước thứ tư trong critical rendering path là chạy bố cục trên cây kết xuất để tính toán hình dạng của từng node. Layout là quá trình xác định kích thước và vị trí của tất cả các node trong render tree, cộng với việc xác định kích thước và vị trí của từng đối tượng trên trang. Cơ chế reflow dùng để xác định size và position bất kỳ phần nào trong page hoặc trên toàn toàn bộ page để tiếp tục render.
Sau khi render tree được xây dựng xong, quá trình bố trí layout sẽ bắt đầu. Render tree sẽ xác định node nào sẽ được display (kể cả các node có kiểu invisible) bao gồm các computed styles của chúng, nhưng lúc này render tree vẫn sẽ chưa xác định được kính thước hoặc vị trí của mỗi node. Để xác định kích thước và vị trí chính xác của từng đối tượng, trình duyệt bắt đầu từ node root của render tree và duyệt qua nó.
Trên một web page, hầu hết các đối tượng đều sẽ là các box. Bởi vì website được hiển thị trên nhiều thiết bị khác nhau, hoặc các kích thước màn hình desktop cũng rất khác nhau nên cácn dạng kích thước khung hình render sẽ là không giới hạn. Lúc này, khi xem xét kích thước khung nhìn, trình duyệt sẽ xác định kích thước của tất cả các box sẽ xuất hiện trên màn hình. Lấy kích thước của khung nhìn làm cơ sở, layout sẽ bắt đầu được sắp xếp từ body sau đó tiếp tục sắp xếp tiếp các phân tử con cùng với các thuộc tính riêng của chúng, lúc đó những phần tử này có thể cung cấp không gian, giữ chổ cho một số phần tử thay thế mà nó chưa xác định được kích thước, ví dụ như image.
Kích thước và vị trí lúc đầu của mỗi node được gọi là layout. Những lần tính toán lại sau đó sẽ được gọi là reflow. Có thể ví dụ về việc render layout trước sau đó mới có hình ảnh trả về, trước đó chúng ta không thể xác định được size ảnh nên sẽ có bước reflow sau khi lấy được hình ảnh và xác định được kích thước của nó.
Paint
Bước cuối cùng trong critical rendering path là vẽ các nodes riêng lẻ lên màn hình. Lần paint đầu tiên có ý nghĩa rất quan trọng. Trong giai đoạn vẽ hay còn gọi là tạo điểm ảnh. Trình duyệt sẽ chuyển đổi từng box đã được apply style có trong layout thành các pixel thực tế để hiển thị trên màn hình. Nó sẽ bao gồm content, màu sắc, đường viền, shadow hoặc một số element như button hay images. Trình duyệt sẽ cần phải thực hiện giai đoạn này một các cực kỳ nhanh chóng.
Để đảm bảo scrolling và animation được hiển thị mượt mà, mọi thứ lúc này sẽ diễn ra ở main thread, bảo gồm cả calculating style, reflow và cả paint, toàn bộ sẽ tốn ít hơn 16.67ms để hoàn thành. Với độ phân giải 2048 x 1536, iPad có hơn 3.145.000 pixel được vẽ lên màn hình. Thực sự là một lượng rất lớn các pixel phải được vẽ lên một cách nhanh chóng. Để đảm bảo việc sơn lại có thể được thực hiện nhanh hơn lần sơn ban đầu, bản vẽ trên màn hình thường được chia thành nhiều lớp, lúc này chúng ta sẽ cần tới compositing (tổng hợp lại).
Quá trình painting có thể chưa các element trong layout tree thành nhiều lớp layers. Đưa các contents vào các layer này và render nó trên GPU thay vì CPU giúp cãi thiện hiểu suất re-paint. Một số thuộc tính đặc biệt có thể được tách lớp layer như <video>
, <canvas>
, các element có các thuộc tính opacity
, 3D transform
, will-change
và một số thuộc tính khác. Các nodes sẽ được paint ngay trên layer của nó, bao gồm cả các nodes con trừ một sô nodes con được tách thêm layer riêng.
Các lớp layers giúp cải thiện hiệu suất nhưng lại tốn kém khi quản lý bộ nhớ, do đó không nên lạm dụng nó như một phần của chiến lược tối ưu hóa hiệu suất web.
Compositing
Khi các phần của document được vẽ thành các lớp khác nhau, chồng lên nhau, việc tổng hợp là cần thiết để đảm bảo chúng được vẽ lên màn hình theo đúng thứ tự và nội dung được hiển thị chính xác.
Khi các tài nguyên assets (hình, font, icon, ...) tiếp tục được load lên trang, reflows sẽ diễn ra (đã có đề cập ở trên). Reflow sẽ kích hoạt quá trình repaint và re-composite. Nếu kích thước hình ảnh đã được xác định trước thì sẽ không cần reflow.
Interactivity
Sau khi luồn chính đã hoàn thành việc painting, tưởng rằng mọi việc có thể kết thúc tại đây. Trong một vài trường hợp thì lại không nhất thiết như vậy. Nếu lúc đó cần phải load javaScript, và thẻ script có gắn defer
nghĩa là các script sẽ phải đợi sau khi onload event được thực hiện, luồn chính lúc này sẽ busy không thể tương tác được, không thể scroll, touch, vv ...
Time to Interactive (TTI) là khoản thời gian kể từ khi request được gửi tới server + DNS lookup + TCP connect cho tới khi trang có thể tương tác được. Đâu đó được tính ra tốn khoản 50ms. Main thread trong lúc đó sẽ phải chạy các thao tác như parsingm compiling, excuting javaScript, thì lúc đó browser sẽ không thể phản hồi kịp các tương tác khác của người dùng. (Lúc đó nên đặt loading hoặc skeleton để hỗ trợ mặt UX)
Trong ví dụ ở trên của chúng ta, có thể hình ảnh được tải nhanh nhưng file anotherscript.js
có dung lượng lên đến 2 MB và nhỡ kết nối internet lúc đó chậm. Lúc này, người dùng sẽ nhìn thấy trang load rất nhanh nhưng lúc scroll sẽ bị giật cho đến khi file 2M kia được tải xuống, parsed và excuted xong. Đó đương nhiên không thể là một trải nghiệm người dùng tốt. Nói chung chúng ta phải tránh chiếm giữ main thread như trường hợp trên, trong ví dụ minh hoạ WebPageTest này như sau:
Trong ví dụ này, quá trình thực thi JavaScript mất hơn 1,5 giây và luồng chính đã bị chiếm dụng hoàn toàn trong suốt thời gian đó, không phản hồi với các sự kiện nhấp chuột hoặc các lần nhấn vào màn hình.