Làm chủ Kiến trúc Island của Astro: Hướng dẫn thực tế cho Frontend hiệu năng cao

Programming tutorial - IT technology blog
Programming tutorial - IT technology blog

Chi phí của sự phình to JavaScript hiện đại

Chúng ta đã dành nhiều năm để gửi các gói JavaScript khổng lồ đến trình duyệt theo mặc định. Cho dù bạn sử dụng React, Vue hay Angular, quy trình tiêu chuẩn thường bao gồm việc hydrating’ (hydrat hóa) toàn bộ trang trước khi người dùng có thể nhấp vào một nút bất kỳ.

Tôi đã dành vô số đêm thức trắng vật lộn với điểm Lighthouse, tuyệt vọng tìm cách giảm 200ms khỏi Total Blocking Time (TBT) trong khi các gói vendor phình to quá 500KB. Hầu hết code đó bị lãng phí vào các header, footer tĩnh và các khối văn bản không cần đến một dòng logic nào để hoạt động.

Sáu tháng trước, tôi đã chuyển một trang web thực tế có nội dung nặng với 50.000 khách truy cập hàng tháng sang Astro. Tôi muốn xem liệu sự cường điệu về ‘Kiến trúc Island’ (Island Architecture) có thật hay không. Kết quả đến ngay lập tức và ổn định. Triết lý cốt lõi rất đơn giản: mặc định không gửi JavaScript đến client. Bạn chỉ ‘hydrate’ các component tương tác cụ thể chính xác khi chúng cần thiết. Việc chuyển đổi từ mô hình thực thi nguyên khối sang hệ thống dựa trên nhu cầu chi tiết này đã thay đổi hoàn toàn chiến lược hiệu năng của tôi.

Định nghĩa về Kiến trúc Island

Hãy coi Kiến trúc Island như một chiến lược cho sự tương tác có tính “phẫu thuật”. Thay vì biến toàn bộ trang thành một ứng dụng JavaScript nặng nề, bạn gửi một bộ khung HTML nhẹ. Nếu thiết kế của bạn yêu cầu một carousel, một modal đăng nhập hoặc một thanh tìm kiếm trực tiếp, bạn nhúng chúng dưới dạng các ‘hòn đảo’ (islands) biệt lập trong “biển” tĩnh.

Những lợi ích tôi đã theo dõi trong hai quý vừa qua bao gồm:

  • Time to Interactive (TTI) nhanh chóng: TTI của tôi giảm từ 4.8 giây xuống còn 1.2 giây trên thiết bị di động. Vì trình duyệt bỏ qua các phần tĩnh, luồng chính (main thread) luôn sẵn sàng cho thao tác của người dùng.
  • Tính linh hoạt của Framework: Bạn không bị gò bó. Hiện tại tôi đang chạy một widget thanh toán dựa trên React ngay cạnh một form liên hệ dựa trên Svelte.
  • Giảm tải lượng Payload đáng kể: Trình duyệt chỉ tải xuống JS cho các component cụ thể trên màn hình. Điều này đã cắt giảm 80% lượng JS payload ban đầu của chúng tôi.

Bắt đầu: Môi trường Astro của bạn

Thiết lập một dự án chỉ mất hai phút. Tôi khuyên bạn nên sử dụng CLI để giữ cho các dependency sạch sẽ và được ánh xạ chính xác. Mở terminal và chạy:

npm create astro@latest

Trình hướng dẫn thiết lập khá trực quan. Đối với hướng dẫn này, hãy chọn template dự án ‘Empty’. Đó là cách tốt nhất để xem cấu trúc tệp mà không bị nhiễu bởi các theme có sẵn. Khi thư mục đã sẵn sàng, hãy truy cập và cài đặt các thành phần cơ bản:

cd my-astro-project
npm install

Kiến trúc Island hiệu quả nhất khi kết hợp với một UI framework. Hãy thêm React vào. Astro cung cấp một lệnh chuyên dụng để xử lý các công việc nặng nhọc:

npx astro add react

Công cụ này tự động cập nhật astro.config.mjs và cài đặt các thư viện cần thiết. Bây giờ bạn đã sẵn sàng để xây dựng hòn đảo đầu tiên của mình.

Directive: Bí mật của Hydration có chọn lọc

Sức mạnh của Astro đến từ các directive client. Các thuộc tính này cho trình biên dịch biết chính xác khi nào cần “đánh thức” một component. Nếu không có directive, component sẽ render dưới dạng HTML thuần túy, không tương tác trên server.

1. Mặc định tĩnh

Việc import và sử dụng một component bình thường sẽ dẫn đến không có JS phía client:

--- 
import MyNavbar from '../components/MyNavbar.jsx';
---
<MyNavbar /> 

Điều này hoàn hảo cho các layout không bao giờ thay đổi sau khi render.

2. client:load

Sử dụng cái này cho các thành phần ưu tiên cao. Nếu người dùng cần tương tác với nút chuyển đổi điều hướng hoặc thanh tìm kiếm ngay khi trang xuất hiện, đây là directive bạn cần.

<MyNavbar client:load />

3. client:visible

Đây là directive có tác động lớn nhất đến hiệu năng. Nó sử dụng Intersection Observer API để trì hoãn việc tải JavaScript của component cho đến khi nó đi vào viewport của người dùng. Tôi sử dụng cái này cho các bộ sưu tập ảnh nặng hoặc các biểu đồ giàu dữ liệu nằm ở footer.

<HeavyChart client:visible />

4. client:idle

Directive này chỉ kích hoạt hydration sau khi trình duyệt đã hoàn tất việc tải ban đầu và rơi vào trạng thái ‘nhàn rỗi’ (idle) thông qua requestIdleCallback. Đây là lựa chọn lý tưởng cho các tính năng ưu tiên thấp như popup bản tin hoặc widget chat.

<NewsletterSignup client:idle />

Giao tiếp giữa các Island

Một trở ngại phổ biến là chia sẻ state giữa các hòn đảo biệt lập này. Vì mỗi hòn đảo là một root độc lập, bạn không thể sử dụng React Context provider tiêu chuẩn để bao bọc toàn bộ ứng dụng. Tôi đã giải quyết vấn đề này bằng cách sử dụng nanostores. Chúng rất nhỏ (dưới 1KB), không phụ thuộc vào framework và cho phép một island React nói chuyện với một island Svelte mà không gây ảnh hưởng đến hiệu năng.

// tệp store.js
import { atom } from 'nanostores';
export const isCartOpen = atom(false);

Đo lường tác động thực tế

Bạn không nên chỉ tin vào những cải thiện hiệu năng một cách mù quáng. Trong sáu tháng làm việc với Astro, tôi đã thiết lập một quy trình xác minh nghiêm ngặt để đảm bảo chúng tôi không vô tình gửi đi các gói bundle bị “rò rỉ”.

Kiểm tra tab Network

Mở DevTools và lọc theo ‘JS’. Nếu bạn có một component được đặt là client:visible, script đó sẽ không xuất hiện trong danh sách cho đến khi bạn cuộn xuống. Nếu nó kích hoạt ngay khi tải trang, có thể bạn đang gặp vấn đề về margin CSS hoặc viết sai chính tả trong directive.

Core Web Vitals

Hãy tập trung sự chú ý vào **Total Blocking Time (TBT)**. Trong quá trình di chuyển, chúng tôi thấy TBT giảm khoảng 70%. Luồng chính không còn bị tắc nghẽn bởi một sự kiện hydration khổng lồ. Để xem tiến trình của riêng bạn, hãy chạy bản build production tại local:

npm run build
npm run preview

Kết quả build của Astro cho bạn một bảng phân tích rõ ràng. Bạn sẽ nhận thấy các tệp HTML lớn hơn một chút vì chúng chứa nội dung đã được render sẵn. Tuy nhiên, tổng lượng JavaScript gửi đến người dùng chỉ là một phần nhỏ so với những gì một Single Page Application (SPA) tiêu chuẩn yêu cầu.

Bảo trì dài hạn

Chi phí bảo trì thấp một cách đáng ngạc nhiên. Vì các island là biệt lập, bạn có thể cập nhật một component React mà không lo lắng về các tác dụng phụ trong một component Svelte ở nơi khác. Sự cô lập này tự nhiên buộc bạn phải đi theo một kiến trúc mô-đun, giúp việc debug dễ dàng hơn đáng kể khi dự án mở rộng.

Nếu bạn đã mệt mỏi với thời gian tải chậm và các gói bundle phình to, Kiến trúc Island của Astro là con đường hợp lý nhất phía trước. Nó cho phép bạn giữ trải nghiệm nhà phát triển hiện đại mà bạn yêu thích trong khi vẫn mang lại tốc độ của một trang web tĩnh.

Share: