Tuyệt chiêu giúp các app trò truyện trên React chạy nhanh hơn 45%

672

Tôi hiện đang làm cho Missive, một team chuyên về xây và quản lí hệ thống email/chat client của React. Nó sử dụng nhiều tầng chứa các cột thông tin để hiển thị cuộc đối thoại. Khi nhấn phím ⬆︎️⬇ thì thanh navigation của app sẽ chuyển qua một cuộc nói chuyện mới. Tuy nhiên, điều đó cần diễn gần như là ngay tức khắc để người dùng không cảm nhận được app bị delay.

Một cuộc nói chuyện có thể chứa đến hàng trăm comment, email, và event. Vì thế để tối ưu hóa hệ thống navigation giữa các cuộc nói chuyện, chúng tôi chuyển đổi nhiều thành phần của cuộc nói chuyện thành một chức năng chính – functional one.

Ví dụ cụ thể là như component Avatar sẽ được chuyển đổi từ:

class Avatar extends React.Component {
  render() {
    return <img src={this.props.url} />;
  }
}

… thành:

// simplified version of our real component
const Avatar = (props) => {
  return <img src={props.url} />;
}

Như bạn thấy đấy, một functional component chính là một tính năng “return element” trong JavaScript. Functional component cũng được gọi là component không có “state”.

Ban đầu, chúng tôi cứ nghĩ rằng sự thay đổi trên sẽ giúp cải thiện hiệu năng cho ứng dụng ngay lập tức bởi khi điều hướng giữa các cuộc nói chuyện trong Missive, React phải unmounts mounts hàng trăm component. Bạn sẽ nghĩ rằng functional component sẽ tránh được quá trình trên cũng như cả sự kiện life-cycle kế đó bởi vì nó chỉ là function thế nhưng thật ra điều đó sai hoàn toàn.

Chính bản React 0.14 release notes đã chứng thực điều đó:

Pattern này nhằm khuyến khích việc tạo ra các component đơn giản vốn sẽ chiếm phần lớn những gì tạo nên ứng dụng của bạn. Trong tương lai, chúng tôi sẽ tập trung vào việc tối ưu hóa hiệu năng cho một số component bằng việc giảm đi các thao tác kiểm tra không cần thiết cũng như memory allocation.

Hóa ra, React vẫn phải làm khá nhiều việc cho các functional component, mà vốn chẳng cần đến. Vì thế mà ta bị mất đi đến vài millisecond quí giá.

Vậy liệu ta có thể bỏ qua các bước thủ tục từ React cho các component này? Thay vì gọi là mount, ta hãy gọi nó đúng theo bản chất của nó: các JavaScript function nhạt nhẽo. Nói cách khác, ta chỉ cần thay đổi các JSX tags của Avatar thành brace, và gọi những function đó trực tiếp luôn. Như thế này:

ReactDOM.render(
   <div>
-    <Avatar url={avatarUrl} />
+    {Avatar({ url: avatarUrl })}
     <div>{commentBody}</div>
   </div>,
   mountNode
 );

 // Compiled JavaScript
 ReactDOM.render(React.createElement(
   'div',
   null,
-  React.createElement(Avatar, { url: avatarUrl }),
+  Avatar({ url: avatarUrl }),
   React.createElement(
     'div',
     null,
     commentBody
   )
 ), mountNode);

Như vậy chúng ta vừa mới loại bỏ một  React.createElementcall quan trọng, và tất cả events của React component lifecycle. Do đó mà, ta cũng không cần phải đợi React team phải lo về vấn đề tối ưu hóa nội bộ.

Để biết được sự thay đổi có ảnh hưởng thế nào, tôi đã tạo ra benchmark này, kết quả thu được khá kinh ngạc. Từ việc chỉ chuyển đổi một class-based component thành functional component , tốc độ được cải thiện chỉ khoảng 6%. Thế nhưng khi gọi component đó là một function đơn giản thay vì mounting, thì tốc độ lại được cải thiện tới 45%.

Qua đó ta thấy được rằng, Missive đã phải mount/unmount một lượng rất lớn các component nhanh nhất có thể (điều hướng giữa các cuộc conversation)

TL;DR

Khi trực tiếp gọi một functional component là function thay vì mounting nó thì hãy sử dụng `React.createElement`. Bạn có thể làm như sau trong JSX:

 

// simplified version of our real component
const Avatar = (props) => {
  return <img src={props.url} />;
}
 ReactDOM.render(
   <div>
-    <Avatar url={avatarUrl} />
+    {Avatar({ url: avatarUrl })}
     <div>{commentBody}</div>
   </div>,
   mountNode
 );

 // Compiled JavaScript
 ReactDOM.render(React.createElement(
   'div',
   null,
-  React.createElement(Avatar, { url: avatarUrl }),
+  Avatar({ url: avatarUrl }),
   React.createElement(
     'div',
     null,
     commentBody
   )
 ), mountNode);

Thật đơn giản đúng không.

Nguồn: topdev.vn via Medium

Tham khảo các vị trí tuyển dụng React hấp dẫn tại đây