Tối ưu hóa hiệu suất React: Làm chủ kỹ thuật giảm kích thước Bundle | MLOG | MLOG

Mỗi route trong ví dụ này tải thành phần tương ứng của nó một cách lười biếng (lazily), cải thiện thời gian tải ban đầu của ứng dụng.

2. Lược bỏ mã thừa (Tree Shaking)

Tree shaking là một kỹ thuật loại bỏ mã chết (dead code) khỏi ứng dụng của bạn. Mã chết là mã không bao giờ được sử dụng trong ứng dụng của bạn, nhưng vẫn được bao gồm trong bundle. Điều này thường xảy ra khi bạn nhập toàn bộ thư viện nhưng chỉ sử dụng một phần nhỏ chức năng của chúng.

Các bundler JavaScript hiện đại như Webpack và Rollup có thể tự động thực hiện tree shaking. Để đảm bảo tree shaking hoạt động hiệu quả, điều quan trọng là sử dụng các module ES (cú pháp importexport) thay vì CommonJS (cú pháp require). Các module ES cho phép bundler phân tích tĩnh mã của bạn và xác định những export nào thực sự được sử dụng.

Ví dụ:

Giả sử bạn đang sử dụng một thư viện tiện ích có tên là lodash. Thay vì nhập toàn bộ thư viện:

            import _ from 'lodash';

_.map([1, 2, 3], (n) => n * 2);
            

Chỉ nhập các hàm bạn cần:

            import map from 'lodash/map';

map([1, 2, 3], (n) => n * 2);
            

Điều này đảm bảo rằng chỉ có hàm map được bao gồm trong bundle của bạn, giảm đáng kể kích thước của nó.

3. Nhập động (Dynamic Imports)

Tương tự như React.lazy(), nhập động (sử dụng cú pháp import()) cho phép bạn tải các module theo yêu cầu. Điều này có thể hữu ích để tải các thư viện hoặc thành phần lớn chỉ cần thiết trong các tình huống cụ thể.

Ví dụ:

            async function handleClick() {
  const module = await import('./MyLargeComponent');
  const MyLargeComponent = module.default;
  // Use MyLargeComponent
}

            

Trong ví dụ này, MyLargeComponent sẽ chỉ được tải khi hàm handleClick được gọi, thường là để phản hồi một hành động của người dùng.

4. Thu nhỏ mã và Nén (Minification and Compression)

Thu nhỏ mã (minification) loại bỏ các ký tự không cần thiết khỏi mã của bạn, chẳng hạn như khoảng trắng, chú thích và các biến không sử dụng. Nén (compression) làm giảm kích thước mã của bạn bằng cách áp dụng các thuật toán tìm kiếm các mẫu và biểu diễn chúng một cách hiệu quả hơn.

Hầu hết các công cụ xây dựng hiện đại, như Webpack, Parcel, và Rollup, đều có hỗ trợ tích hợp cho việc thu nhỏ mã và nén. Ví dụ, Webpack sử dụng Terser để thu nhỏ mã và có thể được cấu hình để sử dụng Gzip hoặc Brotli để nén.

Ví dụ (cấu hình Webpack):

            const TerserPlugin = require('terser-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');

module.exports = {
  // ...
  optimization: {
    minimize: true,
    minimizer: [new TerserPlugin()],
  },
  plugins: [
    new CompressionPlugin({
      algorithm: 'gzip',
      test: /\.(js|css)$/,
      threshold: 10240,
      minRatio: 0.8,
    }),
  ],
};

            

Cấu hình này bật tính năng thu nhỏ mã bằng Terser và nén bằng Gzip. Tùy chọn threshold chỉ định kích thước tối thiểu (tính bằng byte) để một tệp được nén.

5. Tối ưu hóa hình ảnh

Hình ảnh thường có thể là một yếu tố đóng góp đáng kể vào kích thước bundle của ứng dụng. Tối ưu hóa hình ảnh của bạn có thể cải thiện hiệu suất một cách đáng kể.

Các kỹ thuật tối ưu hóa hình ảnh:

6. Lựa chọn thư viện một cách khôn ngoan

Hãy đánh giá cẩn thận các thư viện bạn sử dụng trong ứng dụng của mình. Một số thư viện có thể khá lớn, ngay cả khi bạn chỉ sử dụng một phần nhỏ chức năng của chúng. Cân nhắc sử dụng các thư viện nhỏ hơn, tập trung hơn, chỉ cung cấp các tính năng bạn cần.

Ví dụ:

Thay vì sử dụng một thư viện định dạng ngày lớn như Moment.js, hãy cân nhắc sử dụng một giải pháp thay thế nhỏ hơn như date-fns hoặc Day.js. Các thư viện này nhỏ hơn đáng kể và cung cấp chức năng tương tự.

So sánh kích thước Bundle:

7. HTTP/2

HTTP/2 là một phiên bản mới hơn của giao thức HTTP cung cấp một số cải tiến về hiệu suất so với HTTP/1.1, bao gồm:

Việc bật HTTP/2 trên máy chủ của bạn có thể cải thiện đáng kể hiệu suất của ứng dụng React, đặc biệt là khi xử lý nhiều tệp nhỏ. Hầu hết các máy chủ web và CDN hiện đại đều hỗ trợ HTTP/2.

8. Bộ nhớ đệm trình duyệt (Browser Caching)

Bộ nhớ đệm của trình duyệt cho phép trình duyệt lưu trữ các tài sản tĩnh (như hình ảnh, tệp JavaScript và tệp CSS) cục bộ. Khi người dùng truy cập lại ứng dụng của bạn, trình duyệt có thể truy xuất các tài sản này từ bộ nhớ đệm thay vì tải chúng lại, giúp giảm đáng kể thời gian tải.

Cấu hình máy chủ của bạn để đặt các header bộ nhớ đệm phù hợp cho các tài sản tĩnh của bạn. Header Cache-Control là quan trọng nhất. Nó cho phép bạn chỉ định thời gian trình duyệt nên lưu tài sản vào bộ nhớ đệm.

Ví dụ:

            Cache-Control: public, max-age=31536000
            

Header này yêu cầu trình duyệt lưu tài sản vào bộ nhớ đệm trong một năm.

9. Kết xuất phía máy chủ (Server-Side Rendering - SSR)

Kết xuất phía máy chủ (SSR) liên quan đến việc kết xuất các thành phần React của bạn trên máy chủ và gửi HTML ban đầu đến máy khách. Điều này có thể cải thiện thời gian tải ban đầu và SEO, vì các công cụ tìm kiếm có thể dễ dàng thu thập dữ liệu nội dung HTML.

Các framework như Next.js và Gatsby giúp dễ dàng triển khai SSR trong các ứng dụng React của bạn.

Lợi ích của SSR:

  • Cải thiện thời gian tải ban đầu: Trình duyệt nhận được HTML đã được kết xuất sẵn, cho phép nó hiển thị nội dung nhanh hơn.
  • SEO tốt hơn: Các công cụ tìm kiếm có thể dễ dàng thu thập dữ liệu nội dung HTML, cải thiện thứ hạng của ứng dụng trên công cụ tìm kiếm.
  • Nâng cao trải nghiệm người dùng: Người dùng thấy nội dung nhanh hơn, dẫn đến trải nghiệm hấp dẫn hơn.
  • 10. Ghi nhớ (Memoization)

    Ghi nhớ là một kỹ thuật để lưu vào bộ nhớ đệm kết quả của các lệnh gọi hàm tốn kém và sử dụng lại chúng khi các đầu vào tương tự xuất hiện lại. Trong React, bạn có thể sử dụng thành phần bậc cao hơn (higher-order component) React.memo() để ghi nhớ các thành phần chức năng. Điều này ngăn chặn việc kết xuất lại không cần thiết khi các props của thành phần không thay đổi.

    Ví dụ:

                import React from 'react';
    
    const MyComponent = React.memo(function MyComponent(props) {
      // Render component
      return 
    {props.data}
    ; }); export default MyComponent;

    Trong ví dụ này, MyComponent sẽ chỉ kết xuất lại nếu prop props.data thay đổi. Bạn cũng có thể cung cấp một hàm so sánh tùy chỉnh cho React.memo() nếu bạn cần kiểm soát nhiều hơn về thời điểm thành phần nên kết xuất lại.

    Ví dụ thực tế và các cân nhắc quốc tế

    Các nguyên tắc giảm kích thước bundle là phổ quát, nhưng việc áp dụng chúng có thể khác nhau tùy thuộc vào bối cảnh cụ thể của dự án và đối tượng mục tiêu của bạn. Dưới đây là một số ví dụ:

    Công cụ và tài nguyên

    Dưới đây là một số công cụ và tài nguyên hữu ích để giảm kích thước bundle:

    Kết luận

    Giảm kích thước bundle là một quá trình liên tục đòi hỏi sự chú ý cẩn thận đến từng chi tiết. Bằng cách thực hiện các kỹ thuật được nêu trong hướng dẫn này, bạn có thể cải thiện đáng kể hiệu suất của ứng dụng React và mang lại trải nghiệm người dùng tốt hơn. Hãy nhớ thường xuyên phân tích kích thước bundle của bạn và xác định các lĩnh vực để tối ưu hóa. Lợi ích của một bundle nhỏ hơn—thời gian tải nhanh hơn, sự tương tác của người dùng được cải thiện và trải nghiệm tổng thể tốt hơn—hoàn toàn xứng đáng với nỗ lực bỏ ra.

    Khi các phương pháp phát triển web tiếp tục phát triển, việc cập nhật các kỹ thuật và công cụ mới nhất để giảm kích thước bundle là rất quan trọng để xây dựng các ứng dụng React hiệu suất cao, đáp ứng nhu cầu của khán giả toàn cầu.