+3

Cách tối ưu hóa React để đạt hiệu suất làm việc tối đa

Chắc hẳn rất nhiều người đã và đang sử dụng React cho công việc hàng ngày của mình. Tuy nhiên tôi đoán rằng không nhiều người biết được cách tối ưu hóa React để khiến nó hoạt động mượt mà, từ đó cải thiện hiệu suất công việc tốt hơn.

Hãy cùng tìm hiểu xem các kỹ thuật tối ưu hóa React đó là gì nhé.

1. Tối ưu hóa các List lớn

Tối ưu hóa List là điều vô cùng phổ biến trong React vì đây là thành phần xuất hiện tương đối nhiều và có sức ảnh hưởng lớn. Việc tối ưu một List lớn là một thách thức không nhỏ đối với nhiều người, vì nó có thể gây chiếm dụng bộ nhớ và làm chậm các thao tác.

Chính vì lẽ đó mà ảo hóa là cách tốt nhất để tối ưu các List này, nó cho phép tối ưu hóa các List có thể nhìn thấy và các mục khác khi cần, chứ không tối ưu một cách tùy tiện gây tốn kém dung lượng bộ nhớ.

React Window và React Virtualized là những thư viện phổ biến cho danh sách ảo hóa. Chúng chỉ hiển thị các mục hiển thị trong chế độ xem, giúp giảm đáng kể số lượng nút DOM được hiển thị tại bất kỳ thời điểm nào.

Sau đây là một ví dụ với React Window:

import { FixedSizeList as List } from 'react-window';

    const MyList = ({ items }) => (
      <List
        height={500} // Height of the container
        itemCount={items.length} // Total number of items
        itemSize={35} // Height of each item
        width={300} // Width of the container
      >
        {({ index, style }) => (
          <div style={style}>
            {items[index]}
          </div>
        )}
      </List>
    );

2. Sử dụng hàm useMemo

useMemo là một hook React có nhiệm vụ ghi nhớ kết quả của phép tính. Do đó, nó không cho phép xử lý nhiều phép tính trừ khi có thay đổi về điều kiện. Điều này có thể hữu ích nhằm giúp tối ưu hóa hiệu suất trong các tình huống mà một hàm hoặc phép tính tốn kém và không nên thực hiện lại trên mỗi lần kết xuất dữ liệu.

Cú pháp của useMemo là:

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

Như bạn có thể thấy, useMemo sử dụng hai đối số:

  • Một hàm trả về một giá trị để có thể ghi nhớ được.
  • Một mảng các phụ thuộc xác định thời điểm giá trị đã ghi nhớ cần được tính toán lại.

Sau đây là ví dụ cụ thể về useMemo:

import React, { useState, useMemo } from 'react';

    const ExpensiveComponent = ({ a, b }) => {
      const computeExpensiveValue = (a, b) => {
        console.log('Computing expensive value...');
        return a + b;
      };

      const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

      return (
        <div>
          <p>Computed Value: {memoizedValue}</p>
        </div>
      );
    };

    const ParentComponent = () => {
      const [a, setA] = useState(1);
      const [b, setB] = useState(2);
      const [count, setCount] = useState(0);

      return (
        <div>
          <ExpensiveComponent a={a} b={b} />
          <button onClick={() => setCount(count + 1)}>Increment Count</button>
        </div>
      );
    };

3. Phân tách các đoạn mã

Trong thiết lập truyền thống, tất cả các thành phần trong ứng dụng của bạn được đóng gói lại thành một tệp duy nhất. Phân tách các đoạn mã là một kỹ thuật tối ưu hóa để chia nhỏ ứng dụng của bạn thành các phần nhỏ hơn. Nó làm giảm thời gian tải của ứng dụng khi bạn tải các thành phần nhỏ hơn và tránh các thành phần khác không cần thiết.

Sau đây là một ví dụ về Phân tách mã:

import React, { useState } from 'react';

    function App() {
      const [component, setComponent] = useState(null);

      const loadComponent = async () => {
        const { default: LoadedComponent } = await import('./MyComponent');
        setComponent(<LoadedComponent />);
      };

      return (
        <div>
          <h1>Code Splitting Example</h1>
          <button onClick={loadComponent}>Load Component</button>
          {component}
        </div>
      );
    }

    export default App;

4. Tải Lazy React

React.Lazy là một phương pháp quan trọng để tối ưu hóa các thành phần. Nó cho phép bạn tải xuống chậm rãi các thành phần. Điều này có nghĩa là thành phần đó chỉ được tải nếu cần thiết. Sử dụng phương pháp này, bạn có thể chia ứng dụng của mình thành các thành phần nhỏ hơn và tiến hành tải xuống theo yêu cầu.

React.lazy() được sử dụng để nhập một thành phần động. Khi cần thành phần, nó được tải không đồng bộ và cho đến lúc đó, một giao diện người dùng dự phòng (như một vòng quay tải) có thể được hiển thị.

Sau đây là một ví dụ về Lazy Load:

 import React, { Suspense } from 'react';

    const LazyComponent = React.lazy(() => import('./MyComponent'));

    const App = () => {
      return (
        <div>
          <h1>My App</h1>
          <Suspense fallback={<div>Loading...</div>}>
            <LazyComponent />
          </Suspense>
        </div>
      );
    };

    export default App;

5. Throttling và Debouncing

Nó không chỉ dành riêng cho React mà còn dành cho việc lập trình nói chung khi gọi ra một hàm. Throttling là một kỹ thuật xác định tần suất thực thi một hàm. Khi một hàm bị throttling, nó chỉ được phép thực thi một lần trong một khoảng thời gian nhất định, bất kể sự kiện được kích hoạt bao nhiêu lần. Ví dụ, thêm throttling vào một lần nhấp vào nút để nút đó không được gọi quá thường xuyên.

Ví dụ về Throttling:

import React, { useState } from 'react';

    function ThrottledButton() {
      const [count, setCount] = useState(0);

      const throttle = (func, delay) => {
        let lastCall = 0;
        return () => {
          const now = new Date().getTime();
          if (now - lastCall >= delay) {
            lastCall = now;
            func();
          }
        };
      };

      const incrementCount = () => {
        setCount((prevCount) => prevCount + 1);
      };

      const throttledIncrement = throttle(incrementCount, 2000);

      return (
        <div>
          <h1>Count: {count}</h1>
          <button onClick={throttledIncrement}>Click Me</button>
        </div>
      );
    }

    export default ThrottledButton;

Debouncing được sử dụng để đảm bảo rằng một hàm sẽ được thực thi sau một khoảng thời gian nhất định sau khi gọi hàm. Khi một sự kiện xảy ra nhiều lần, hàm được debounce sẽ chỉ thực thi sau khi sự kiện đã dừng kích hoạt trong khoảng thời gian trễ được chỉ định. Ví dụ, khi người dùng đang nhập dữ liệu tìm kiếm và để cung cấp gợi ý, tôi sẽ đợi khoảng vài mili giây trước khi gọi hàm để người dùng hoàn tất việc nhập dữ liệu của mình.

Ví dụ về Debouncing:

import React, { useState } from 'react';

    function debounce(func, delay) {
      let timeoutId;
      return function (...args) {
        if (timeoutId) {
          clearTimeout(timeoutId);
        }
        timeoutId = setTimeout(() => {
          func(...args);
        }, delay);
      };
    }

    const DebouncedSearch = () => {
      const [query, setQuery] = useState('');

      const handleSearch = (event) => {
        setQuery(event.target.value);
        console.log('Searching for:', event.target.value);
        // Here you would typically trigger an API call or filter a list based on the query
      };

      const debouncedSearch = debounce(handleSearch, 500);

      return (
        <div>
          <h1>Search</h1>
          <input
            type="text"
            placeholder="Type to search..."
            onChange={debouncedSearch}
          />
          <p>Search Query: {query}</p>
        </div>
      );
    };

    export default DebouncedSearch;

Kết luận

Tối ưu hóa các ứng dụng React là điều vô cùng quan trọng để đảm bảo rằng chúng có thể chạy trơn tru và hoạt động hiệu quả, đặc biệt là khi chúng ngày càng phức tạp và có kích thước lớn hơn. Bằng cách kết hợp các kỹ thuật như ảo hóa các List, ghi nhớ với useMemo, phân tách mã, Lazy React, Throttling và Debouncing, bạn có thể cải thiện đáng kể hiệu suất của các ứng dụng React của mình.

Tôi hy vọng phương pháp này sẽ có ích trong việc tối ưu hóa hiệu suất ứng dụng React của bạn. Cảm ơn bạn đã đọc bài viết vừa rồi.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí