Tối ưu ứng dụng React bằng Code-Spliting

Code-Spliting là một kỹ thuật quan trọng trong ReactJS giúp tối ưu hóa hiệu năng và tải trang nhanh chóng. Khi phát triển ứng dụng web, việc tải toàn bộ mã nguồn của ứng dụng tại một thời điểm có thể làm giảm trải nghiệm người dùng, đặc biệt là khi ứng dụng có kích thước lớn. Code-Splitting giải quyết vấn đề này bằng cách chia mã nguồn thành các phần nhỏ hơn và chỉ tải các phần cần thiết khi cần sử dụng.

Bundle JS Files là gì?

Trước tiên bạn cần hiểu khái niệm bundle file trong React là gì. Thông thường khi tạo ra một ứng dụng thì chúng ta sẽ viết source code của mình vào nhiều files khác nhau, trong đó có chứa nhiều các modules và thư viện bên thứ 3 (3rd-party libs). Khi tiến hành build ứng dụng của bạn, React sẽ thực hiện việc chuyển đổi rất nhiều file source code mà bạn viết trở thành 1 file lớn hơn để sử dụng nó đưa cho các trình duyệt web khi load ứng dụng. Những file đó được gọi là bundle.

Ban đầu ứng dụng  của bạn tạo ra những file bundle nhỏ, và việc trình duyệt load chúng lên không thành vấn đề; sau một thời gian phát triển, với việc import và sử dụng ngày càng nhiều các thư viện và module thì các files bundle của bạn cũng ngày càng nặng thêm. Nếu không thực hiện tối ưu, kích thước các file bundle có thể lên tới 40-50Mb là chuyện bình thường, điều đó cũng đồng nghĩa với việc ứng dụng của bạn trở nên nặng nề khi load, user sẽ cần chờ 1 khoảng thời gian khá lâu để có thể tương tác được với các phần tử trên màn hình.

Vậy cách giải quyết cho vấn đề này là gì?

Code-Splitting là gì?

Rõ ràng để load một ứng dụng hay một màn hình cụ thể, ứng dụng của bạn không cần phải “nạp” hết các module hay thư viện được import vào; vì thế để giải quyết cho vấn đề bundle file size lớn, chúng ta cần một kỹ thuật để tách nó ra thành 2 phần: phần cần thiết load để khởi động ứng dụng (hay màn hình) và phần có thể load nạp vào sau khi ứng dụng đã được chạy. Và khi cần cần thiết load để có thể khởi động ứng dụng càng nhỏ, thì thời gian tải ứng dụng của chúng ta càng nhanh hơn. React đã cung cấp cho chúng ta tính năng này và gọi nó là Code-Splitting.

Có 2 kỹ thuật xử lý trong Code-Splitting thường được sử dụng, chúng ta cùng lần lượt tìm hiểu và xem cách triển khai của chúng nhé.

Sử dụng React.lazy và Suspense

ReactJS cung cấp một cách dễ dàng để thực hiện Code-Splitting bằng cách sử dụng hàm lazySuspense. Khi bạn sử dụng lazy, bạn có thể trễ việc tải một thành phần React và trả về một phiên bản đã được tải trễ của thành phần đó. Suspense được sử dụng để hiển thị một phần tử UI thay thế trong quá trình tải.

Để sử dụng Code-Splitting, đầu tiên bạn cần cài đặt React bằng phiên bản 16.6.0 trở lên. Sau đó, bạn có thể sử dụng lazySuspense. Dưới đây là một ví dụ minh họa:

import React, { lazy, Suspense } from 'react';

// Import một thành phần React thông qua hàm lazy
const MyComponent = lazy(() => import('./MyComponent'));

function App() {
  return (
    <div>
      <h1>Ứng dụng React với Code-Splitting</h1>
      <Suspense fallback={<div>Loading...</div>}>
        <MyComponent />
      </Suspense>
    </div>
  );
}

export default App;

Trong ví dụ trên, MyComponent sẽ được tải trễ chỉ khi nó cần thiết để hiển thị trên trang. Trong quá trình tải, <Suspense> sẽ hiển thị phần tử UI fallback (ở đây là một thông báo “Loading…”) cho người dùng biết rằng dữ liệu đang được tải.

Khi ứng dụng được xây dựng và triển khai, mã nguồn của MyComponent sẽ được chia thành một tệp riêng và chỉ tải khi người dùng truy cập vào trang chứa MyComponent. Điều này giúp giảm thời gian tải trang ban đầu và cải thiện hiệu năng tổng thể của ứng dụng.

Dynamic Import

Phương thức dynamic import giúp việc import module, file một cách bất đồng bộ bằng việc trả về 1 Promise. Nó hoạt động được cả ở server-side và client-side giúp bạn có thể sử dụng trong các trường hợp load file, assets hay những module, 3rd-party libs không cần thiết cho việc hiển thị ứng dụng, màn hình lần đầu tiên.

Để minh họa việc sử dụng dynamic import trong ReactJS, hãy xem xét một ví dụ đơn giản. Giả sử bạn có hai thành phần React là ComponentAComponentB, và bạn chỉ muốn tải ComponentB khi người dùng tương tác với ComponentA. Dưới đây là cách bạn có thể sử dụng dynamic import để thực hiện điều này:

import React, { useState } from 'react';

function ComponentA() {
  const [componentBLoaded, setComponentBLoaded] = useState(false);

  const loadComponentB = () => {
    import('./ComponentB').then(() => {
      setComponentBLoaded(true);
    });
  };

  return (
    <div>
      <h1>Component A</h1>
      <button onClick={loadComponentB}>Load Component B</button>
      {componentBLoaded && <ComponentB />}
    </div>
  );
}

export default ComponentA;

Trong ví dụ trên, ComponentA chứa một nút “Load Component B”. Khi người dùng nhấp vào nút đó, loadComponentB được gọi. Trong hàm này, chúng ta sử dụng import động để tải ComponentB từ tệp riêng của nó.

Khi ComponentB được tải thành công, chúng ta cập nhật trạng thái componentBLoaded để báo hiệu rằng nó đã được tải. Sau đó, ComponentB được hiển thị trên trang.

Việc sử dụng dynamic import trong ví dụ trên cho phép tải ComponentB chỉ khi cần thiết, giúp tối ưu hóa hiệu suất và giảm thời gian tải trang ban đầu.

Tuy nhiên, cần lưu ý rằng Code-Splitting cũng có một số tác động tiêu cực. Việc tải trễ các thành phần có thể làm giảm trải nghiệm người dùng nếu không được thực hiện một cách thích hợp. Nếu Code-Splitting được sử dụng quá nhiều, việc tải trễ có thể tạo ra hiệu ứng nhấp nháy và làm giảm sự liền mạch của ứng dụng.

Tóm lại, Code-Splitting là một kỹ thuật quan trọng trong ReactJS để tối ưu hóa hiệu năng và tải trang nhanh chóng. Bằng cách chia mã nguồn thành các phần nhỏ và chỉ tải khi cần thiết, Code-Splitting giúp cải thiện trải nghiệm người dùng và tăng cường hiệu suất của ứng dụng web.

Related Posts