
James Miller
Đăng ngày 18 tháng 10, 2024

Tìm hiểu sâu về tối ưu rendering, chiến lược memoization và công cụ profiling để làm ứng dụng nhanh hơn.
Ứng dụng React phát triển nhanh — và các vấn đề hiệu năng cũng vậy. Một component render 10 lần mỗi giây? Một danh sách 10.000 items? Bundle trên 2MB?
Tối ưu sớm là nguồn gốc của mọi điều xấu. Nhưng tối ưu có đo lường, có mục tiêu chính là phép màu.
Hãy cùng thành thạo những công cụ thực sự để làm ứng dụng React cực nhanh.
React re-render một component khi:
Ngay cả khi không có gì thay đổi trực quan.
<Parent> <ExpensiveChild data={hugeArray} /></Parent>→ ExpensiveChild re-render mỗi khi Parent cập nhật, ngay cả khi hugeArray không bao giờ thay đổi.
Wrap functional components để bỏ qua re-renders khi props shallow-equal.
const ExpensiveChild = React.memo(function ExpensiveChild({ data }) { console.log('ExpensiveChild rendered'); return <HeavyChart data={data} />;});// Chỉ re-render nếu reference của `data` thay đổiCảnh báo: Không memo tất cả. Memoization có chi phí bộ nhớ + so sánh. → Profile trước!
| Hook | Cache | Dùng khi |
|---|---|---|
| useMemo | Giá trị (arrays, objects, tính toán) | Tính toán tốn kém |
| useCallback | Hàm | Truyền cho child components hoặc useEffect |
const filteredUsers = useMemo(() => {return users.filter(user => user.active && user.role === 'admin');}, [users]);const handleSave = useCallback(() => {saveUser(user);}, [user]); // Ngăn child re-renderreturn <Form onSave={handleSave} />;Bundle 3MB? Người dùng phải chờ. Chia nhỏ nó!
React.lazyconst HeavyChart = React.lazy(() => import('./HeavyChart'));const Dashboard = React.lazy(() => import('./Dashboard'));function App() {return ( <Suspense fallback={<Spinner />}> <HeavyChart /> </Suspense>);}Bonus Next.js:
dynamic(() => import(...), { ssr: false })
Đừng render 10.000 list items. Chỉ render những gì nhìn thấy.
react-windowimport { FixedSizeList } from 'react-window';<FixedSizeList height={600} itemCount={10000} itemSize={50} width="100%"> {({ index, style }) => ( <div style={style}>User #{index}</div> )}</FixedSizeList>→ DOM chỉ có ~20 nodes, không phải 10.000 → Cuộn mượt 60 FPS
Không bao giờ đoán. Sử dụng React DevTools Profiler.
Tìm kiếm:
Quy tắc vàng: Đo lường → Tối ưu → Đo lường lại
// 1. Memo hóa mọi thứconst Button = React.memo(({ onClick }) => <button onClick={onClick}>Click</button>);// 2. Inline objects trong JSXreturn <Child config={{ theme: 'dark' }} />; // Object mới mỗi render!// 3. Không có Suspense fallback<React.lazy(...) /> {/* Crash khi mạng chậm */}// 1. Chỉ memo hóa components tốn kémconst Chart = React.memo(ExpensiveChart);// 2. Đưa config ra ngoàiconst darkTheme = { theme: 'dark' };return <Child config={darkTheme} />;// 3. Luôn dùng Suspense<Suspense fallback={<Loading />}> <LazyDashboard /></Suspense>React.memo chỉ trên components tốn kém?useMemo/useCallback có được biện minh bởi chi phí?Hiệu năng không phải là làm mọi thứ nhanh. Đó là làm đúng thứ nhanh.
Đo lường. Nhắm mục tiêu. Tối ưu.
Người dùng không quan tâm đến useMemo.
Họ quan tâm đến UI phản hồi nhanh, tải nhanh và cuộn mượt.
Thành thạo những công cụ này — và xây dựng ứng dụng React mượt như native.
Viết bởi James Miller
#React #Performance #Optimization #Frontend #WebDev #ReactProfiler #CodeSplitting