Chi phí tiềm ẩn của Virtual DOM
Trong suốt một thập kỷ, chúng ta được nghe rằng Virtual DOM (VDOM) là tiêu chuẩn vàng cho hiệu năng. React đã thúc đẩy ý tưởng rằng việc tính toán các thay đổi trên một cây dựa trên bộ nhớ (memory-based tree) sẽ nhanh hơn so với việc tác động trực tiếp vào DOM. Đó từng là một sự trừu tượng tuyệt vời trong một thời gian dài.
Nhưng khi các ứng dụng web hiện đại ngày càng trở nên phức tạp, sự trừu tượng đó đã trở thành một nút thắt cổ chai. Mỗi lần thay đổi trạng thái (state) đều buộc framework phải thực hiện quá trình ‘diff’ (so sánh) toàn bộ cây component. Để duy trì tốc độ, chúng ta thường bị sa lầy vào useMemo và useCallback chỉ để ngăn chặn engine thực hiện các công việc không cần thiết.
Đội ngũ của tôi đã chạm tới giới hạn này gần đây khi xây dựng một bảng điều khiển phân tích thời gian thực. Ngay cả khi đã tối ưu hóa mạnh mẽ, bảng dữ liệu 50 cột của chúng tôi vẫn chật vật để duy trì ở mức 60fps, thường xuyên giảm xuống còn 15fps chậm chạp khi có nhiều cập nhật lớn. Sự thất vọng đó đã dẫn chúng tôi đến với SolidJS. Chúng tôi nhanh chóng nhận ra vấn đề không nằm ở mã nguồn; mà nằm ở kiến trúc cơ bản của các framework dựa trên VDOM. SolidJS bỏ qua hoàn toàn VDOM, thay vào đó chọn cách biên dịch mã thành các cập nhật DOM trực tiếp và chính xác.
Triết lý của SolidJS: Chạy một lần, cập nhật mãi mãi
Điều đầu tiên bạn sẽ nhận thấy khi chuyển từ React sang SolidJS là cách các component hoạt động. Trong React, một component là một hàm thực thi lặp đi lặp lại. Ngược lại, một component SolidJS là một script thiết lập chỉ chạy đúng một lần. Nó khởi tạo biểu đồ phản ứng (reactive graph) và sau đó biến mất một cách hiệu quả. Chỉ những biểu thức cụ thể gắn liền với state của bạn mới được tính toán lại khi có thay đổi.
Cách tiếp cận này không chỉ là một mánh khóe quảng cáo; nó mang lại những thắng lợi lo lớn trong môi trường production. Kích thước bundle chính của chúng tôi đã giảm gần 30% vì không còn phải gửi kèm một bộ engine đối soát (reconciliation engine) nặng nề đến trình duyệt. Vì không có VDOM để xử lý, chỉ số Time-to-Interactive (TTI) trên thiết bị di động đã cải thiện hơn 400ms. SolidJS biên dịch JSX thành các chỉ lệnh DOM hiệu quả, mang lại cho bạn tốc độ của vanilla JavaScript với cú pháp của một framework hiện đại.
Tìm hiểu về Signals API
Signals là cơ chế cốt lõi của SolidJS. Mặc dù createSignal trông giống như useState của React, nhưng logic bên dưới lại khác biệt. Một Signal là một giá trị có thể quan sát (observable) tự theo dõi các subscriber của chính nó. Bạn không chỉ nhận được một giá trị; bạn nhận được một getter và một setter.
import { createSignal } from "solid-js";
const [count, setCount] = createSignal(0);
// Bạn truy cập giá trị bằng cách gọi nó như một hàm
console.log(count());
Việc gọi count() sẽ cho Solid biết chính xác vị trí của giá trị đó trong giao diện người dùng. Khi bạn gọi setCount, framework không chạy lại toàn bộ component. Nó chỉ cập nhật node văn bản cụ thể trong HTML được liên kết với signal đó. Cơ chế fine-grained reactivity (phản ứng tinh gọn) này là lý do tại sao SolidJS liên tục đứng đầu các bảng xếp hạng benchmark về tốc độ và hiệu quả bộ nhớ.
Derived State và Effects
Solid cung cấp createMemo cho các tính toán nặng và createEffect cho các side effect. Bạn có thể quên đi cơn ác mộng về ‘mảng phụ thuộc’ (dependency array) trong React. SolidJS tự động theo dõi các signal nào bạn sử dụng trong quá trình thực thi. Nó chỉ chạy lại effect khi các signal cụ thể đó thay đổi.
import { createSignal, createMemo, createEffect } from "solid-js";
const Counter = () => {
const [count, setCount] = createSignal(1);
// Solid tự động nhận diện dependency count()
const doubleCount = createMemo(() => count() * 2);
createEffect(() => {
console.log("Số lượng hiện tại:", count());
});
return (
<button onClick={() => setCount(count() + 1)}>
Đếm: {count()} | Gấp đôi: {doubleCount()}
</button>
);
};
Thực hành: Xây dựng danh sách Reactive
Để thấy được khoảng cách về hiệu năng, hãy xem cách các framework xử lý danh sách. Trong một framework VDOM, việc cập nhật một mục trong danh sách 1.000 mục thường yêu cầu kiểm tra tất cả 1.000 mục đó. Solid sử dụng component <For>, được thiết kế riêng để xử lý các node DOM một cách riêng lẻ mà không cần diffing không cần thiết.
Thiết lập dự án
Bạn có thể khởi chạy dự án trong vài giây bằng template Vite. Mở terminal và chạy:
npx degit solidjs/templates/ts my-solid-app
cd my-solid-app
npm install
npm run dev
Triển khai bảng dữ liệu hiệu quả
Hãy tưởng tượng một trình theo dõi giá thời gian thực nơi các hàng cập nhật mỗi giây. Bạn muốn các ô giá cập nhật mà không làm toàn bộ hàng bị nhấp nháy. Đây là phiên bản đơn giản hóa từ triển khai thực tế của chúng tôi:
import { createSignal, For, onCleanup } from "solid-js";
const PriceTracker = () => {
const [stocks, setStocks] = createSignal([
{ id: 1, name: "AAPL", price: 150 },
{ id: 2, name: "TSLA", price: 700 },
{ id: 3, name: "GOOGL", price: 2800 },
]);
// Cập nhật giá mỗi 1000ms
const interval = setInterval(() => {
setStocks(prev => prev.map(s => ({
...s,
price: s.price + (Math.random() - 0.5) * 10
})));
}, 1000);
onCleanup(() => clearInterval(interval));
return (
<div>
<h2>Giá cổ phiếu trực tuyến</h2>
<ul>
<For each={stocks()}>
{(stock) => (
<li>
{stock.name}: <strong>${stock.price.toFixed(2)}</strong>
</li>
)}
</For>
</ul>
</div>
);
};
export default PriceTracker;
Component <For> đảm bảo rằng nếu thứ tự danh sách thay đổi, Solid sẽ di chuyển các node DOM thay vì tạo lại chúng. Nếu chỉ có giá thay đổi, chỉ thẻ <strong> cụ thể đó được cập nhật. Phần còn lại của danh sách hoàn toàn tĩnh.
Tại sao SolidJS là lựa chọn đúng đắn cho dự án tiếp theo của bạn
Sau sáu tháng áp dụng vào thực tế, thắng lợi lớn nhất không chỉ là tốc độ—đó là sự rõ ràng trong tư duy. Bạn không còn phải lo lắng về stale closures hay các quy tắc hook phức tạp. Nếu muốn sử dụng các thư viện native như D3 hay Leaflet, bạn hoàn toàn có thể. Bạn có quyền truy cập trực tiếp vào các node DOM thực mà không bị framework cản trở.
Hệ sinh thái cũng đã sẵn sàng cho các dự án lớn. SolidStart cung cấp giải pháp SSR mạnh mẽ, và Solid Router xử lý điều hướng mượt mà. Nếu bạn đang xây dựng các ứng dụng nặng về dữ liệu, trình soạn thảo thời gian thực hoặc các bảng điều khiển phức tạp, Virtual DOM là một khoản nợ hiệu năng mà bạn không cần phải chi trả.
Chuyển từ React sang SolidJS rất dễ dàng vì cú pháp JSX mang lại cảm giác quen thuộc. Tuy nhiên, logic bên dưới gần gũi hơn nhiều với cách trình duyệt thực sự vận hành. Nó giống như đang viết vanilla JavaScript thuần khiết, sạch sẽ và mang tính khai báo.
Kết luận
SolidJS đại diện cho một bước chuyển mình hướng tới một nền tảng web hiệu quả hơn. Bằng cách chuyển các tác vụ nặng sang giai đoạn biên dịch và sử dụng Signals cho các cập nhật chính xác, nó mang lại hiệu năng mà các framework VDOM không thể sánh kịp. Kinh nghiệm của tôi cho thấy đây không chỉ là một sự cải thiện nhỏ; nó tạo ra các giao diện mượt mà hơn và trải nghiệm lập trình dễ dự đoán hơn. Nếu bạn đã mệt mỏi với việc vật lộn với framework để có được hiệu năng mà người dùng xứng đáng nhận được, đã đến lúc thử SolidJS.

