Trong bài viết này, chúng ta sẽ khám phá Performance Optimization trong ReactJS cũng như những kỹ thuật tối ưu hiệu suất cần thiết để xây dựng các ứng dụng React có thể đáp ứng nhu cầu người dùng.
React JS là một trong những thư viện JavaScript phổ biến và được sử dụng rộng rãi trong việc phát triển các ứng dụng web hiện đại. Tuy nhiên, khi phát triển các ứng dụng React lớn và phức tạp, tối ưu hóa hiệu suất trở thành yếu tố quan trọng để đảm bảo ứng dụng hoạt động trơn tru và đáp ứng tốt yêu cầu của người dùng.
1. Performance Optimization trong React Js là gì?
Performance Optimization trong ReactJS là quá trình nâng cao hiệu suất của ứng dụng ReactJS bằng cách áp dụng các kỹ thuật và công cụ nhằm giảm bớt công việc của trình duyệt và máy chủ. Điều này cải thiện trải nghiệm người dùng, giảm thời gian tải trang và tăng khả năng mở rộng của ứng dụng.
Có nhiều thành phần hỗ trợ việc tối ưu hóa ứng dụng. Ví dụ, React.memo() giúp tránh render lại các component không cần thiết, trong khi PureComponent sẽ kiểm tra xem có cần render lại một component hay không.
Như vậy, việc sử dụng các phương thức do React cung cấp sẽ giúp ứng dụng trở nên tối ưu hơn đáng kể.
2. Các Components trong Performance Optimization
React.memo()
React.memo()
là một component quan trọng trong Performance Optimization của ReactJS. Nó giúp tối ưu hóa hiệu suất bằng cách tránh việc render lại component khi không cần thiết.
Khi sử dụng React.memo(), React sẽ tự động so sánh các props của component trước và sau khi update. Nếu các props không thay đổi, React sẽ bỏ qua việc render lại component, giúp giảm thiểu số lần render và cải thiện hiệu suất ứng dụng.
React.memo()
được sử dụng như một HOC (Higher-Order Component) và bao bọc bên ngoài component cần được tối ưu hóa.
Ví dụ:
1
2
3
4
5
|
import React, { memo } from 'react' ; const MyComponent = memo((props) => { // Component code here }); |
Lưu ý rằng React.memo()
chỉ hoạt động với các component dựa trên props và không hoạt động với các component sử dụng state hoặc context. Nếu muốn tối ưu hóa các component sử dụng state hoặc context, có thể sử dụng PureComponent hoặc shouldComponentUpdate()
thay thế.
PureComponent
Trong React, một PureComponent
là một lớp component được cung cấp sẵn mà tự động thực hiện các kiểm tra sâu (deep checks) để xác định xem có cần render lại component hay không. Khi một component được render lại, nó sẽ tạo ra một cây DOM mới và tất cả các thành phần (components) bên trong của nó cũng sẽ được render lại. Việc render lại có thể làm giảm hiệu suất của ứng dụng.
Tuy nhiên, nếu chỉ có những thay đổi nhỏ trong component, việc render lại là không cần thiết.PureComponent
tự động thực hiện các kiểm tra để xác định xem props và state của component đã thay đổi hay chưa. Nếu không có sự thay đổi nào, PureComponent
sẽ không render lại component, giúp cải thiện hiệu suất của ứng dụng.
Một ví dụ về sử dụng PureComponent
trong React là trong một ứng dụng web, ta có một component hiển thị danh sách các sản phẩm và component này được cập nhật mỗi khi có một sản phẩm mới được thêm vào hoặc một sản phẩm đã có được cập nhật.
Khi sử dụng PureComponent
, ta có thể viết component hiển thị danh sách sản phẩm này như sau:
Ví Dụ:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import React, { PureComponent } from 'react' ; class ProductList extends PureComponent { render() { const { products } = this .props; return ( <ul> {products.map(product => ( <li key={product.id}>{product.name}</li> ))} </ul> ); } } |
Trong ví dụ này, ta sử dụng PureComponent
để đảm bảo rằng component chỉ được render lại khi có sự thay đổi trong products. Nếu không có sự thay đổi, PureComponent
sẽ không thực hiện việc render lại component, giúp tăng hiệu suất của ứng dụng.
Với mỗi lần cập nhật danh sách sản phẩm, React sẽ tự động kiểm tra sâu (deep check) để xác định xem danh sách sản phẩm có thay đổi hay không. Nếu danh sách không thay đổi, component sẽ không được render lại, giúp tiết kiệm tài nguyên và tăng tốc độ hiển thị trang web.
Tuy nhiên, nếu trong component này có nhiều props và state phức tạp khác, việc sử dụng PureComponent
có thể không phù hợp và ta nên sử dụng các kỹ thuật tối ưu hơn để cải thiện hiệu suất
shouldComponentUpdate()
Trong React, shouldComponentUpdate()
là một lifecycle method được gọi trước khi một component được render lại. Phương thức này cho phép ta kiểm soát việc render lại component bằng cách xác định xem component có cần được render lại hay không dựa trên những thay đổi trong props hoặc state.
Nếu shouldComponentUpdate()
trả về false, component sẽ không được render lại. Nếu phương thức này trả về true, component sẽ được render lại. Việc sử dụng shouldComponentUpdate()
có thể giúp tối ưu hóa hiệu suất của ứng dụng bằng cách tránh render lại component khi không cần thiết.
Tuy nhiên, việc sử dụng shouldComponentUpdate()
có thể làm cho mã của component trở nên phức tạp và khó hiểu hơn. Ngoài ra, việc thực hiện các kiểm tra tùy chỉnh trong phương thức này cũng có thể làm giảm hiệu suất của ứng dụng nếu không được sử dụng đúng cách.
Ví dụ dưới đây cho thấy cách sử dụng shouldComponentUpdate()
để kiểm soát việc render lại component:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import React, { Component } from 'react' ; class MyComponent extends Component { shouldComponentUpdate(nextProps, nextState) { // Kiểm tra xem có sự thay đổi trong props hoặc state không if ( this .props.foo === nextProps.foo && this .state.bar === nextState.bar) { return false ; // Không cần render lại component } return true ; // Cần render lại component } render() { return ( <div> // Nội dung component </div> ); } } |
Trong ví dụ này, ta sử dụng shouldComponentUpdate()
để kiểm tra xem có sự thay đổi trong props (this.props.foo
và nextProps.foo
) và state (this.state.bar
và nextState.bar
) của component không. Nếu không có sự thay đổi, ta trả về false để ngăn component được render lại.
Tóm lại, shouldComponentUpdate()
là một trong những cách để tối ưu hóa hiệu suất của ứng dụng React bằng cách kiểm soát việc render lại component. Tuy nhiên, cần cân nhắc khi sử dụng để đảm bảo tính hiệu quả và độ tin cậy của ứng dụng.
React.lazy()
React.lazy()
là một API mới được giới thiệu trong React 16.6.0, cho phép tạo các lazy-loaded
components, tức là chỉ tải các components cần thiết khi cần sử dụng, chứ không tải tất cả các components cùng lúc. Điều này giúp cải thiện hiệu suất của ứng dụng bằng cách giảm thời gian tải các components không cần thiết và giảm kích thước của bundle.
Khi sử dụng React.lazy()
, ta có thể định nghĩa một component dưới dạng hàm import() trả về một promise. Khi component này được sử dụng, React sẽ tự động tải và render component đó.
Ví dụ sau đây minh họa cách sử dụng React.lazy()
để lazy load một component:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import React, { lazy, Suspense } from 'react' ; const MyComponent = lazy(() => import( './MyComponent' )); function App() { return ( <div> <Suspense fallback={<div>Loading...</div>}> <MyComponent /> </Suspense> </div> ); } |
Trong ví dụ này, ta sử dụng lazy()
để định nghĩa MyComponent dưới dạng một hàm import(). Sau đó, ta sử dụng component <Suspense>
để hiển thị một placeholder (<div>Loading...</div>
) trong khi component đang được tải. Khi component được tải xong, React sẽ tự động render component đó.
Tuy nhiên, khi sử dụng React.lazy()
, cần cân nhắc khi áp dụng cho các trường hợp phù hợp, chẳng hạn như các components lớn, phức tạp hoặc không được sử dụng thường xuyên. Việc sử dụng quá nhiều lazy-loaded
components có thể dẫn đến tăng thời gian phản hồi của ứng dụng do tải các components khi cần thiết.
Tóm lại, React.lazy()
là một cách để cải thiện hiệu suất của ứng dụng React bằng cách giảm thời gian tải và kích thước của bundle bằng cách tải các components cần thiết khi cần sử dụng. Tuy nhiên, cần cân nhắc khi sử dụng để đảm bảo tính hiệu quả và độ tin cậy của ứng dụng.
useCallback()
useCallback()
là một hook trong React được sử dụng để tối ưu hóa hiệu suất của ứng dụng bằng cách giảm số lần render của các components.
Khi một component được render, các hàm và các object literals bên trong component đó sẽ được tạo mới. Việc tạo mới này có thể gây ra việc render lại các components con, dù chúng không thay đổi props. Điều này có thể làm chậm tốc độ của ứng dụng, đặc biệt là khi các components con có kích thước lớn.
Với useCallback()
, ta có thể tạo ra một hàm mới chỉ khi các dependencies của nó thay đổi. Điều này giúp giảm số lần render của các components, vì các hàm được tạo lại chỉ khi các dependencies của chúng thay đổi.
Ví dụ sau đây minh họa cách sử dụng useCallback()
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import React, { useState, useCallback } from 'react' ; function MyComponent(props) { const [count, setCount] = useState(0); const handleClick = useCallback(() => { setCount(count + 1); }, [count]); return ( <div> <p>Count: {count}</p> <button onClick={handleClick}>Increment Count</button> </div> ); } |
Trong ví dụ này, ta sử dụng useCallback()
để tạo hàm handleClick
. Hàm này chỉ được tạo lại khi dependency count thay đổi. Trong trường hợp này, nếu count không thay đổi, hàm handleClick
sẽ không được tạo lại. Việc này giúp giảm số lần render của component MyComponent.
Tuy nhiên, cần cân nhắc khi sử dụng useCallback()
vì việc sử dụng quá nhiều có thể dẫn đến tình trạng lãng phí bộ nhớ hoặc làm tăng thời gian phản hồi của ứng dụng.
Tóm lại, useCallback()
là một cách để tối ưu hóa hiệu suất của ứng dụng React bằng cách giảm số lần render của các components. Việc sử dụng hook này phù hợp trong trường hợp các components con có kích thước lớn và cần render lại nhiều lần.
useMemo()
useMemo()
là một hook trong React được sử dụng để tối ưu hóa hiệu suất của ứng dụng bằng cách cache giá trị tính toán và trả về giá trị đã được cache khi các dependency không thay đổi. Điều này giúp giảm số lần tính toán lại, đặc biệt là khi tính toán phức tạp.
Ví dụ sau đây minh họa cách sử dụng useMemo()
để tính toán giá trị của một biến result:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import React, { useMemo } from 'react' ; function MyComponent(props) { const [number1, setNumber1] = useState(0); const [number2, setNumber2] = useState(0); const result = useMemo(() => { console.log( 'compute result' ); return number1 + number2; }, [number1, number2]); return ( <div> <p>Number 1: {number1}</p> <input value={number1} onChange={(e) => setNumber1(parseInt(e.target.value))} /> <p>Number 2: {number2}</p> <input value={number2} onChange={(e) => setNumber2(parseInt(e.target.value))} /> <p>Result: {result}</p> </div> ); } |
Trong ví dụ này, ta sử dụng useMemo()
để tính toán giá trị của biến result dựa trên number1 và number2. Giá trị của result sẽ được tính toán lại chỉ khi các dependency của nó (trong trường hợp này là number1 và number2) thay đổi. Nếu các dependency không thay đổi, useMemo()
sẽ trả về giá trị đã được cache của result, giúp giảm số lần tính toán lại và tăng tốc độ của ứng dụng.
Tuy nhiên, cần cân nhắc khi sử dụng useMemo()
vì việc sử dụng quá nhiều có thể dẫn đến tình trạng lãng phí bộ nhớ hoặc làm tăng thời gian phản hồi của ứng dụng. useMemo()
là một cách để tối ưu hóa hiệu suất của ứng dụng React bằng cách cache giá trị tính toán và trả về giá trị đã được cache khi các dependency không thay đổi. Việc sử dụng hook này phù hợp trong trường hợp tính toán phức tạp và cần giảm số lần tính toán lại.
3. Kết Luận
Tối ưu hiệu suất ứng dụng React đòi hỏi phải cân bằng giữa độ phức tạp và hiệu quả. Tối ưu hóa hiệu suất không phải là mục tiêu cuối cùng, mà là một phương tiện để cải thiện trải nghiệm người dùng. Việc tối ưu hóa hiệu suất có thể tăng tốc độ ứng dụng, giảm thời gian phản hồi và nâng cao trải nghiệm người dùng.
Để đạt được điều này, có nhiều kỹ thuật và công cụ có thể áp dụng, chẳng hạn như sử dụng PureComponent, shouldComponentUpdate, React.lazy để tải chậm các component, và các hooks như useCallback và useMemo để tránh tính toán lại nhiều lần. Thư viện React Profiler cũng có thể được sử dụng để phân tích hiệu suất của ứng dụng.
Tuy nhiên, quá trình tối ưu hóa hiệu suất cũng có thể dẫn đến các vấn đề như mã nguồn trở nên phức tạp hoặc khó bảo trì hơn. Do đó, cần xem xét và lựa chọn các kỹ thuật và công cụ phù hợp nhằm cải thiện trải nghiệm người dùng mà không làm giảm tính linh hoạt và khả năng bảo trì của ứng dụng.