Làm thế nào để bạn quyết định, làm thế nào để bạn chọn giữa ba dựa trên mục đích / kích thước / đạo cụ / hành vi của các thành phần của chúng tôi?
Mở rộng từ React.PureComponent
hoặc từ React.Component
một shouldComponentUpdate
phương thức tùy chỉnh có ý nghĩa về hiệu suất. Sử dụng các thành phần chức năng không trạng thái là một lựa chọn "kiến trúc" và không có bất kỳ lợi ích hiệu suất nào ngoài hộp (chưa).
Đối với các thành phần đơn giản, chỉ mang tính trình bày cần được tái sử dụng dễ dàng, thích các thành phần chức năng không trạng thái. Bằng cách này, bạn chắc chắn rằng chúng được tách rời khỏi logic ứng dụng thực tế, rằng chúng rất dễ kiểm tra và chúng không có tác dụng phụ không mong muốn. Ngoại lệ là nếu vì một lý do nào đó bạn có rất nhiều trong số họ hoặc nếu bạn thực sự cần tối ưu hóa phương thức kết xuất của họ (vì bạn không thể xác định shouldComponentUpdate
cho một thành phần chức năng không trạng thái).
Mở rộng PureComponent
nếu bạn biết đầu ra của mình phụ thuộc vào đạo cụ / trạng thái đơn giản ("đơn giản" nghĩa là không có cấu trúc dữ liệu lồng nhau, vì PureComponent thực hiện so sánh nông) VÀ bạn cần / có thể nhận được một số cải tiến hiệu suất.
Mở rộng Component
và thực hiện của riêng shouldComponentUpdate
bạn nếu bạn cần một số tăng hiệu suất bằng cách thực hiện logic so sánh tùy chỉnh giữa các đạo cụ tiếp theo / hiện tại và trạng thái. Ví dụ: bạn có thể nhanh chóng thực hiện so sánh sâu bằng cách sử dụng lodash # isEqual:
class MyComponent extends Component {
shouldComponentUpdate (nextProps, nextState) {
return !_.isEqual(this.props, nextProps) || !_.isEqual(this.state, nextState);
}
}
Ngoài ra, thực hiện của riêng bạn shouldComponentUpdate
hoặc mở rộng từ PureComponent
là tối ưu hóa, và như thường lệ, bạn chỉ nên bắt đầu xem xét điều đó nếu bạn có vấn đề về hiệu suất ( tránh tối ưu hóa sớm ). Theo nguyên tắc thông thường, tôi luôn cố gắng thực hiện các tối ưu hóa này sau khi ứng dụng ở trạng thái hoạt động, với hầu hết các tính năng đã được triển khai. Việc tập trung vào các vấn đề hiệu suất sẽ dễ dàng hơn rất nhiều khi chúng thực sự cản trở.
Thêm chi tiết
Thành phần không trạng thái chức năng:
Chúng được định nghĩa chỉ bằng cách sử dụng một chức năng. Vì không có trạng thái bên trong cho một thành phần không trạng thái, đầu ra (những gì được hiển thị) chỉ phụ thuộc vào các đạo cụ được cung cấp làm đầu vào cho chức năng này.
Ưu điểm:
Cách đơn giản nhất có thể để xác định một thành phần trong React. Nếu bạn không cần quản lý bất kỳ trạng thái nào, tại sao phải bận tâm với các lớp và kế thừa? Một trong những khác biệt chính giữa hàm và lớp là với hàm bạn chắc chắn đầu ra chỉ phụ thuộc vào đầu vào (không phụ thuộc vào bất kỳ lịch sử nào của các lần thực hiện trước đó).
Lý tưởng nhất trong ứng dụng của bạn, bạn nên đặt mục tiêu có càng nhiều thành phần không trạng thái càng tốt, bởi vì điều đó thường có nghĩa là bạn đã di chuyển logic của mình ra khỏi lớp khung nhìn và chuyển nó sang một thứ như redux, có nghĩa là bạn có thể kiểm tra logic thực của mình mà không cần phải hiển thị bất cứ điều gì (dễ kiểm tra hơn nhiều, có thể tái sử dụng nhiều hơn, v.v.).
Nhược điểm:
Không có phương pháp vòng đời. Bạn không có cách nào để xác định componentDidMount
và những người bạn khác. Thông thường bạn làm điều đó trong một thành phần cha mẹ cao hơn trong hệ thống phân cấp để bạn có thể biến tất cả trẻ em thành những người không quốc tịch.
Không có cách nào để kiểm soát thủ công khi cần kết xuất lại, vì bạn không thể xác định shouldComponentUpdate
. Kết xuất lại xảy ra mỗi khi thành phần nhận được đạo cụ mới (không có cách nào để so sánh nông, v.v.). Trong tương lai, React có thể tự động tối ưu hóa các thành phần không trạng thái, hiện tại có một số thư viện bạn có thể sử dụng. Vì các thành phần không trạng thái chỉ là các chức năng, về cơ bản, đây là vấn đề kinh điển của "ghi nhớ chức năng".
Tham chiếu không được hỗ trợ: https://github.com/facebook/react/issues/4936
Một thành phần mở rộng lớp PureComponent VS Một thành phần bình thường mở rộng lớp Thành phần:
React được sử dụng để có một PureRenderMixin
bạn có thể đính kèm vào một lớp được xác định bằng React.createClass
cú pháp. Mixin chỉ đơn giản là định nghĩa một phép shouldComponentUpdate
so sánh nông giữa các đạo cụ tiếp theo và trạng thái tiếp theo để kiểm tra xem có gì thay đổi không. Nếu không có gì thay đổi, thì không cần phải thực hiện kết xuất lại.
Nếu bạn muốn sử dụng cú pháp ES6, bạn không thể sử dụng mixins. Vì vậy, để thuận tiện, React đã giới thiệu một PureComponent
lớp mà bạn có thể kế thừa từ thay vì sử dụng Component
. PureComponent
chỉ thực hiện shouldComponentUpdate
theo cùng một cách của PureRendererMixin
. Đây chủ yếu là một điều thuận tiện để bạn không phải tự mình thực hiện, vì so sánh nông cạn giữa trạng thái hiện tại / tiếp theo và đạo cụ có lẽ là kịch bản phổ biến nhất có thể mang lại cho bạn một số chiến thắng hiệu suất nhanh.
Thí dụ:
class UserAvatar extends Component {
render() {
return <div><img src={this.props.imageUrl} /> {{ this.props.username }} </div>
}
}
Như bạn có thể thấy đầu ra phụ thuộc vào props.imageUrl
và props.username
. Nếu trong một thành phần cha mẹ bạn kết xuất <UserAvatar username="fabio" imageUrl="http://foo.com/fabio.jpg" />
với cùng một đạo cụ, React sẽ gọi render
mọi lúc, ngay cả khi đầu ra sẽ giống hệt nhau. Tuy nhiên, hãy nhớ rằng React thực hiện dom diffing, vì vậy DOM sẽ không thực sự được cập nhật. Tuy nhiên, thực hiện dom diffing có thể tốn kém, vì vậy trong kịch bản này sẽ là một sự lãng phí.
Nếu UserAvatar
thành phần mở rộng PureComponent
thay thế, một so sánh nông được thực hiện. Và bởi vì đạo cụ và nextProps giống nhau, render
sẽ không được gọi chút nào.
Lưu ý về định nghĩa "thuần túy" trong React:
Nói chung, "hàm thuần túy" là một hàm luôn luôn đánh giá cùng một kết quả với cùng một đầu vào. Đầu ra (đối với React, đó là những gì được render
phương thức trả về ) không phụ thuộc vào bất kỳ lịch sử / trạng thái nào và nó không có bất kỳ tác dụng phụ nào (các hoạt động thay đổi "thế giới" bên ngoài chức năng).
Trong React, các thành phần không trạng thái không nhất thiết là các thành phần thuần túy theo định nghĩa ở trên nếu bạn gọi "statless" là thành phần không bao giờ gọi this.setState
và không sử dụng this.state
.
Trong thực tế, trong một PureComponent
, bạn vẫn có thể thực hiện các tác dụng phụ trong các phương pháp vòng đời. Ví dụ: bạn có thể gửi yêu cầu ajax bên trong componentDidMount
hoặc bạn có thể thực hiện một số tính toán DOM để tự động điều chỉnh chiều cao của div bên trong render
.
Định nghĩa "thành phần câm" có ý nghĩa "thực tế" hơn (ít nhất là theo cách hiểu của tôi): một thành phần câm "được nói" phải làm gì bởi một thành phần cha mẹ thông qua đạo cụ và không biết cách làm gì ngoài việc sử dụng đạo cụ gọi lại thay thế.
Ví dụ về một "thông minh" AvatarComponent
:
class AvatarComponent extends Component {
expandAvatar () {
this.setState({ loading: true });
sendAjaxRequest(...).then(() => {
this.setState({ loading: false });
});
}
render () {
<div onClick={this.expandAvatar}>
<img src={this.props.username} />
</div>
}
}
Ví dụ về một "người câm" AvatarComponent
:
class AvatarComponent extends Component {
render () {
<div onClick={this.props.onExpandAvatar}>
{this.props.loading && <div className="spinner" />}
<img src={this.props.username} />
</div>
}
}
Cuối cùng tôi sẽ nói rằng "ngu ngốc", "không quốc tịch" và "thuần túy" là những khái niệm khá khác nhau đôi khi có thể trùng lặp, nhưng không nhất thiết, phụ thuộc chủ yếu vào trường hợp sử dụng của bạn.