Thế giới phát triển web đã dành phần lớn một thập kỷ qua để tự thuyết phục mình rằng trình duyệt nên là nơi gánh vác phần việc nặng nhọc nhất. Chúng ta bắt đầu với các tài liệu và biểu mẫu, sau đó dần dần chuyển mọi hoạt động có thể tưởng tượng được sang phía client. Định tuyến (routing), quản lý trạng thái (state management), truy xuất dữ liệu (data fetching), logic kết xuất (rendering logic), thậm chí cả việc điều phối truy vấn cơ sở dữ liệu thông qua GraphQL — tất cả đều chuyển vào các gói JavaScript (bundles) vốn ngày càng trở nên nặng nề hơn sau mỗi bản phát hành. Các framework nhân lên, các quy trình build (build pipelines) trở nên phức tạp hơn, và những gì bắt đầu như một cách để làm cho ứng dụng cảm thấy nhanh nhạy đã biến thành một kiến trúc mà một trang web không thể kết xuất nổi một pixel có ý nghĩa nào cho đến khi hàng megabyte mã nguồn được tải xuống, phân tích và thực thi.

Sự chuyển dịch đó đã giải quyết được những vấn đề thực tế. Các trang web kết xuất từ phía máy chủ (server-rendered) với một chút jQuery đã gặp khó khăn trong việc mang lại các hiệu ứng chuyển cảnh mượt mà như ứng dụng mà người dùng mong đợi. Các ứng dụng đơn trang (Single Page Applications) đã mang lại cho chúng ta khả năng điều hướng tức thì, trạng thái nhất quán và các tương tác phong phú. Nhưng cái giá phải trả là sự tích tụ của những phức tạp. Các đội ngũ phát triển hiện phải quản lý các kho lưu trữ trạng thái phía client phức tạp, vật lộn với các gói JavaScript khổng lồ, duy trì các lớp đồng bộ hóa dữ liệu khó chiều và gỡ lỗi các quy trình build mà đôi khi cảm giác như một công việc toàn thời gian riêng biệt. Chúng ta đã đổi tập hợp vấn đề này lấy một tập hợp vấn đề khác, và nhiều nhà phát triển hiện đang tự hỏi liệu mọi ứng dụng có thực sự cần phải trả cái giá đó hay không.

Có hai bước phát triển đang giúp câu hỏi đó trở nên dễ trả lời hơn.

HTMX và sự trở lại của Hypermedia

Thứ nhất là HTMX. Nhìn bề ngoài, nó có vẻ giống như một thư viện nhỏ, nhưng hệ quả về mặt kiến trúc của nó lại rất lớn. HTMX coi HTML là định dạng gốc cho logic ứng dụng, thay vì coi nó như một lớp vỏ tĩnh cần được "thổi phồng" bằng JavaScript.

Dưới đây là những gì thay đổi trong thực tế. Theo truyền thống, khi người dùng nhấp vào một nút để tải thêm bình luận, frontend sẽ gửi một yêu cầu fetch, nhận một payload JSON, chuẩn hóa nó vào một kho lưu trữ phía client, chạy nó qua một template component, so sánh sự khác biệt (diff) của virtual DOM, và cuối cùng là cập nhật (patch) trang web. HTMX cắt ngắn chuỗi quy trình đó. Bản thân nút bấm đã chứa các thuộc tính cho trình duyệt biết cần gửi yêu cầu đi đâu và thay thế phần tử trang nào. Máy chủ trả về một đoạn HTML (fragment) — chỉ là các bình luận mới, được bao bọc trong một thẻ div. Trình duyệt sẽ tráo đổi nó vào. Không có JSON, không có cây trạng thái frontend, không có thuật toán đối soát (reconciliation algorithm), và không có JavaScript mệnh lệnh (imperative JavaScript) để giữ cho UI đồng bộ với máy chủ.

Đây không phải là sự bác bỏ phát triển hiện đại. Đây là sự bác bỏ những sự trừu tượng hóa không cần thiết. HTMX chứng minh rằng hypermedia, phong cách kiến trúc đã vận hành web thời kỳ đầu, vẫn có thể hỗ trợ các giao diện tinh vi khi được kết hợp với tính tiện dụng hiện đại. Bất kỳ phần tử nào cũng có thể phát hành yêu cầu, không chỉ là biểu mẫu và liên kết. Bất kỳ sự kiện nào cũng có thể kích hoạt một bản cập nhật. Máy chủ vẫn là nguồn sự thật duy nhất (source of truth) cho cả dữ liệu và phần hiển thị.

Cập nhật từng phần theo kiểu khai báo của Chrome

Sự chuyển dịch thứ hai mới hơn và nằm ngay bên trong trình duyệt. Chrome đang giới thiệu Declarative Partial Updates, hay DPU. Tính năng này cho phép trình duyệt truyền dữ liệu (stream) HTML và chèn trực tiếp vào các phần mục tiêu của trang web ngay khi các byte dữ liệu vừa đến.

Trước khi có DPU, nếu bạn muốn truyền dữ liệu trực tiếp vào một trang web, bạn thường phải sử dụng WebSockets, Server-Sent Events, hoặc long-polling kết hợp với thao tác DOM thủ công. Frontend phải quản lý kết nối, phân tích payload và quyết định chính xác cách thức cũng như vị trí để chèn markup. DPU thay đổi phương trình này bằng cách biến quy trình trở thành dạng khai báo (declarative). Nhà phát triển chỉ định một container mục tiêu, và trình duyệt sẽ xử lý phần còn lại: nhận luồng dữ liệu, phân tích đoạn mã và đặt nó chính xác vào nơi nó thuộc về, ngay cả trước khi phản hồi đầy đủ được đóng lại.

Hãy nghĩ về một bảng điều khiển giám sát (monitoring dashboard) hiển thị nhật ký máy chủ hoặc một hàng đợi hỗ trợ cập nhật theo thời gian thực. Với DPU, backend gửi các mẩu HTML thuần túy ngay khi chúng được tạo ra. Trình duyệt sẽ truyền chúng vào thân bảng hoặc một container tin tức mà không cần một dòng logic streaming nào ở phía client. Việc lắp ghép diễn ra một cách tự nhiên (natively).

Mô hình Ưu tiên Máy chủ (Server-First Model)

Khi kết hợp HTMX và DPU, bạn sẽ có một kiến trúc nhất quán, nơi máy chủ sở hữu trạng thái và tạo ra UI, trong khi trình duyệt xử lý việc hiển thị và đầu vào của người dùng. Các framework backend như Rails, Laravel, Django, Go templates, hoặc ASP.NET sẽ trở lại thành lớp giao diện chính. Frontend không còn là một ứng dụng riêng biệt tiêu thụ một API. Nó chính là giao diện hypermedia mà máy chủ tạo ra.

Mô hình này phù hợp với một phân khúc phần mềm rộng lớn đến bất ngờ. Hãy xem xét một ứng dụng SaaS điển hình. Đó là các dashboard với các bảng có thể sắp xếp. Đó là các admin panels với các biểu mẫu và bộ lọc. Đó là các công cụ nội bộ giúp chuyển đổi trạng thái của các bản ghi. Đó là các quy trình CRUD hiển thị một danh sách, cung cấp chế độ xem chi tiết và cho phép người dùng chỉnh sửa các trường dữ liệu. Thậm chí đó còn là các giao diện AI, nơi một mô hình ngôn ngữ truyền (stream) các token về phía người dùng, và mỗi token hoặc đoạn văn có thể được bao bọc trong HTML và nối thêm vào luồng hội thoại. Đối với tất cả những trường hợp này, một client JavaScript nặng nề thường là quá mức cần thiết.

Các lợi ích mang lại là tức thì và thực tế. Tốc độ tải trang ban đầu nhanh hơn vì lần hiển thị nội dung có ý nghĩa đầu tiên (first meaningful paint) đến dưới dạng HTML, chứ không phải sau khi chu kỳ hydration hoàn tất. Dung lượng JavaScript giảm xuống vì không cần virtual DOM, không cần client-side router và không cần gửi kèm thư viện quản lý trạng thái (state management library). Các công cụ tìm kiếm có thể thấy nội dung đầy đủ mà không cần thực thi các bundle, vì vậy SEO hoạt động một cách mặc định. Độ phức tạp giảm xuống vì chỉ một codebase duy nhất đảm nhiệm việc định tuyến (routing), logic nghiệp vụ và hiển thị (rendering). Việc gỡ lỗi (debugging) trở nên dễ dàng hơn. Khi có gì đó trông không ổn, bạn chỉ cần kiểm tra tab Network và thấy chính xác HTML mà máy chủ đã gửi. Không có đối tượng trạng thái (state object) phía client mờ mịt cần phải dịch ngược (reverse-engineer).

Còn React và các Client nặng nề thì sao?

Điều này không có nghĩa là React đã chết, hay các SPA là một sai lầm. Các ứng dụng phức tạp, đạt chuẩn trình chỉnh sửa (editor-grade) vẫn cần một client nặng nề. Figma chạy một engine C++ được biên dịch sang WebAssembly bên trong trình duyệt vì các lượt phản hồi từ máy chủ (server round-trips) sẽ khiến việc vẽ trở nên bất khả thi. Canva thao tác trên canvas với tốc độ 60 khung hình mỗi giây bằng hình học phía client (client-side geometry). Google Docs sử dụng operational transforms để giải quyết các xung đột chỉnh sửa trong vài mili giây. Những công cụ này về bản chất là các ứng dụng desktop được phân phối thông qua một tab trình duyệt. Chúng sẽ không quay trở lại việc sử dụng các biểu mẫu được render từ phía máy chủ.

Nhưng hầu hết phần mềm không phải là Figma. Hầu hết phần mềm không phải là một trình chỉnh sửa đồ họa thời gian thực. Hầu hết phần mềm là một màn hình báo cáo, một bảng cấu hình, một quy trình đặt chỗ, hoặc một biểu mẫu quản lý nội dung. Đối với "phần đuôi dài" (long tail) của các ứng dụng đó, việc gửi kèm hàng trăm kilobyte framework JavaScript chỉ để bật/tắt một modal hoặc lấy một danh sách bản ghi chưa bao giờ là điều hợp lý. Tính kinh tế của các stack công nghệ đang thay đổi. Chúng ta đang khám phá lại rằng máy chủ có thể ở gần người dùng hơn nhờ vào edge, và bản thân trình duyệt đã trở nên đủ khả năng để cập nhật các phân đoạn (fragments) mà không cần một framework làm trung gian cho từng byte dữ liệu.

Con lắc tìm thấy sự cân bằng

Cung chuyển động của kiến trúc web đang quay trở lại sự đơn giản, nhưng đây không phải là một sự quay lại ngây thơ về những năm chín mươi. Trình duyệt đang trở nên thông minh hơn. Các tính năng như DPU không thay thế sự sáng tạo của nhà phát triển; chúng hấp thụ các mô hình mà chúng ta từng phải triển khai thủ công — như streaming, cập nhật từng phần, chèn DOM có mục tiêu — vào chính nền tảng đó. HTMX cung cấp cho chúng ta "từ vựng" để diễn đạt các hành vi đó mà không cần phải tái cấu trúc một hệ điều hành thu nhỏ ở frontend.

Bạn không còn phải lựa chọn giữa một kiến trúc đơn giản và một trải nghiệm người dùng mượt mà. Bạn có thể có cả hai. Máy chủ có thể điều khiển giao diện, trình duyệt có thể lắp ráp nó, và JavaScript bạn viết có thể tập trung vào tính tương tác thực sự thay vì các công việc hạ tầng phụ trợ.

Đối với thế hệ tiếp theo của các dashboard, công cụ quản trị và giao diện hỗ trợ bởi AI, client thông minh nhất có thể chính là client làm ít việc nhất.