7.3.1 React component performance exploration
Recommended by React Devtools
- React V16.5.0 + (development mode)
- The React Developer Tools V3.3.2 +
Tracking user behavior
-
Install the Schedule package, yarn Add Schedule
-
Embed code where you need to track
import { unstable_track as track} from 'schedule/track'
export default class Home extends Component {
handleSubmit =e= >{
const text = e.target.value.trim()
// The user hits the Enter button to track
if(e.which===13){
track("Add TOdo",performance,now,() = >{
this.props.onSave(text)
if(this.props.newTodo){
this.setState({text:' '})}})}Copy the code
React Profiler API
- Profilter is in the React package.
- The onRender callback function returns a series of messages.
7.3.2 Optimizing Component Performance
1. PureComponent
-
Class Component optimization tool
-
The essence is a shallow comparison in the shouldComponentUpdate method
The parent component
import React from 'react';
export default class extends React.Component {
constructor(props){
super(props);
this.state = {
date : new Date(),
id:1}}componentDidMount(){
setInterval(() = >{
this.setState({
date:new Date()})},1000)}render(){
return (
<div>
<Child seconds={id}/>
<div>{this.state.date.toString()}</div>
</div>)}Copy the code
We can see that the entire component needs diff. We can see that the child component does not depend on the date variable. So when the date changes, the subcomponent does not render at all, so we can use PureComponent to optimize.
class Child extends React.PureComponent {
render(){
return (
<div>{this.props.seconds}</div>)}}Copy the code
2. memo
-
Function component optimization tool
-
Is a higher-order function that performs an internal comparison similar to shouldComponentUpdate
-
You can specify a comparison function
function Child({seconds}){
return (
<div>I am update every {seconds} seconds</div>)};export default React.memo(Child)
Copy the code
Don’t assume that all is well with a child component called React. Memo
function Father({seconds}){
/* If you use the reacte. memo to optimise the function, it will not work. /* If you use the reacte. memo to optimise the function, it will not work. You can use useCallback for packages */
/ / before optimization
function change() {}
/ / after optimization
const change = useCallback(() = >{
},[])
return (
<Child change={change}></Child>)};function Child({seconds}){
return (
<div>I am update every {seconds} seconds</div>)};export default React.memo(Child)
Copy the code
::: Warning React.memo() can take two arguments. The first argument is the component of a pure function. The second argument is used to compare props for flushing, similar to shouldComponentUpdate(). [2]
React. Memo is equivalent to PureComponent, but it only compares props. You can also compare the old and new props by specifying a custom comparison function with the second argument. If the function returns true, the update is skipped. : : :
function Child({seconds}){
return (
<div>I am update every {seconds} seconds</div>)};function areEqual(prevProps, nextProps) {
if(prevProps.seconds===nextProps.seconds){
return true
}else {
return false}}export default React.memo(Child,areEqual)
Copy the code
3 Destruction of native events and timers
4. Use unchanging data structures
Data invariance is not an architecture or a design pattern, but rather an opinionated way of writing code. This forces you to think about how to organize your application data flow. In my view, data invariance is a practice that revolves around strictly unidirectional data flows.
Advantage:
- Zero side effects
- Immutable data objects are easier to create, test, and use
- Easy to track changes
Case study:
class Imu extends Component {
state = {
users: []
}
addNewUser = () = >{
const users = this.state.users;
users.push({
userName: "robin".email: "[email protected]"
});
this.setState({users: users}); }}Copy the code
In this case, user and this.state.users are the same reference. If we change user directly, we change this.state.users directly. The react state should be immutable because setState() can replace changes you made earlier
Changing state directly causes problems:
We use shouldComponentUpdate to determine whether to rerender the component. This.state. users and nextstate. user are the same reference, so React will not rerender the UI even if the array changes
shouldComponentUpdate(nextProps, nextState) {
if (this.state.users ! == nextState.users) {return true;
}
return false;
}
Copy the code
How can I avoid this problem
addNewUser = () = > {
this.setState(state= > ({
users: state.users.concat({
timeStamp: new Date(),
userName: "robin".email: "[email protected]"})})); };Copy the code
Consider the following immutable approach:
Array: [].concat or […params]
Object: object.assign ({}…) Or es6 {… params}
Optimization library for variable data structures:
-
mmutable.js
-
react-copy-write
5. Split the file
As new features and dependencies continue to be added, not only will your project become larger, we can consider separating third-party packages; By splitting files, your browser can download resources in parallel, reducing wait times. SplitChunksPlugin
6. Dependency optimization
When optimizing your application code, it’s important to check how many libraries you’re using in your application. For example, if you’re using moment.js; This library contains many national language packs that you don’t need, so consider using the moment-locales-webpack-plugin to remove unused language packs for your final package.
Lodash, you can remove unused features with the lodash-webpack-plugin
7. React.Fragments are used to avoid redundant HTML elements
In React we have to wrap the child elements with a root element. We can do this with react.Fragment, which will not render the actual HTML element during rendering
class Comments extends React.PureComponent{
render() {
return (
<React.Fragment>
<h1>Comment Title</h1>
<p>comments</p>
<p>comment time</p>
</React.Fragment>); }}// or
class Comments extends React.PureComponent{
render() {
return (
<>
<h1>Comment Title</h1>
<p>comments</p>
<p>comment time</p>
</>); }}Copy the code
8. Avoid using inline function definitions in render functions
Since the function is JavaScript ({}! == {}), so when React diff checks, inline functions will always diff fail. In addition, if you use the arrow function in the JSX property, a new instance of the function is created on each rendering. This can be a lot of work for the garbage collector.
class CommentList extends React.Component {
state = {
comments: [].selectedCommentId: null
}
render(){
const { comments } = this.state;
return (
comments.map((comment) = >{
return <Comment onClick={(e)= >{
this.setState({selectedCommentId:comment.commentId})
}} comment={comment} key={comment.id}/>}}}))Copy the code
Instead of defining an inline function for props, you can define an arrow function.
default class CommentList extends React.Component {
state = {
comments: [].selectedCommentId: null
}
onCommentClick = (commentId) = >{
this.setState({selectedCommentId:commentId})
}
render(){
const { comments } = this.state;
return (
comments.map((comment) = >{
return <Comment onClick={this.onCommentClick}
comment={comment} key={comment.id}/>}}}))Copy the code
9. Use anti-shake throttling
Throttling: To trigger once within a specified time, no matter how many actions you do in that time, I only trigger one action
Anti-shaking: Prevents events from being triggered frequently and only after the user has stopped the action, at a time after the delay
You can use lodash
import debouce from 'lodash.debounce';
class SearchComments extends React.Component {
constructor(props) {
super(props);
this.state = { searchQuery: ""}; } setSearchQuery = debounce(e= > {
this.setState({ searchQuery: e.target.value });
}, 1000);
render() {
return (
<div>
<h1>Search Comments</h1>
<input type="text" onChange={this.setSearchQuery} />
</div>); }}Copy the code
10. Avoid using Index as the Key of a Map
{
comments.map((comment, index) = > {
<Comment
{..comment}
key={index} />})}Copy the code
Using index may cause your application to display incorrectly, because the key is used when diff is used; When you delete, add, or move a list, elements with the same key are no longer the same element.
In some cases you can use index as the key
- Lists and items are static
- Items in the list have no IDS, and the list is never reordered or filtered
- Lists are immutable
11. Avoid starting the state of the component with props
class EditPanelComponent extends Component {
constructor(props){
super(props);
this.state ={
isEditMode: false.applyCoupon: props.applyCoupon
}
}
render(){
return <div>
{this.state.applyCoupon &&
<>Enter Coupon: <Input/></>}
</div>
}
}
Copy the code
If you change the props without refreshing the component, the new props value will never be assigned to the applyCoupon of the state because constructor will only be called at initialization time.
The solution: can componentWillReceiveProps, props to update the status
constructor(props){
super(props);
this.state ={
isEditMode: false.applyCoupon: props.applyCoupon
}
}
componentWillReceiveProps(nextProps){
if(nextProps.applyCoupon ! = =this.props.applyCoupon) {
this.setState({ applyCoupon: nextProps.applyCoupon })
}
}
render(){
return <div>{this.props.applyCoupon &&
<>Enter Coupon: <Input/></>}</div>
}
}
Copy the code
12. Webpack use mode
Webpack 4,mode set to Production, WebPack will use built-in optimizations
module.exports = {
mode: 'production'
};
Copy the code
13. Propagates props on the DOM element
Doing so adds unknown HTML attributes, which are unnecessary
const CommentsText = props= > {
return (
<div {. props} >
{props.text}
</div>
);
};
Copy the code
You can set specific properties
const CommentsText = props= > {
return (
<div specificAttr={props.specificAttr}>
{props.text}
</div>
);
};
Copy the code
14. CSS animation instead of JS animation
Animation is inevitable for a smooth and enjoyable user experience. There are many ways to animate web pages. In general, we can create animations in three ways:
CSS transition
CSS animations
JavaScript
15. CDN
CDN can transfer static content faster, from your website or mobile application faster.
16 Web Workers API attempt
-
After using Web Workers, the Web application can:
- Run a script in a background thread separate from the main thread.
- Perform time-consuming tasks in separate threads to prevent time-consuming tasks from blocking the user experience
-
Communication mechanism Communication between the Web Worker and the main thread after completing time-consuming tasks
- postMessage
// sort.worker.js
export default function sort() {
self.addEventListener('message'.e= >{
if(! e)return;
let posts = e.data;
for (let index = 0, len = posts.length - 1; index < len; index++) {
for (let count = index+1; count < posts.length; count++) {
if (posts[index].commentCount > posts[count].commentCount) {
const temp = posts[index];
posts[index] = users[count];
posts[count] = temp;
}
}
}
postMessage(posts);
});
}
export default Posts extends React.Component{
constructor(props){
super(posts);
}
state = {
posts: this.props.posts
}
componentDidMount() {
this.worker = new Worker('sort.worker.js');
this.worker.addEventListener('message'.event= > {
const sortedPosts = event.data;
this.setState({
posts: sortedPosts
})
});
}
doSortingByComment = () = > {
if(this.state.posts && this.state.posts.length){
this.worker.postMessage(this.state.posts); }}render(){
const posts = this.state.posts;
return (
<React.Fragment>
<Button onClick={this.doSortingByComment}>
Sort By Comments
</Button>
<PostList posts={posts}></PostList>
</React.Fragment>)}}Copy the code
17. Virtualization long list
List virtualization, or windozing, is a technique to improve performance when presenting long lists of data. This technique renders only a small portion of the rows at any given time, and can significantly reduce the time required to re-render components, as well as the number of DOM nodes created.
There are popular React libraries such as React-Window and React-Virtualized which provide several reusable components to display lists and grids and then an virtualized table.
18. Server rendering
You can refer to the last chapter, project actual combat, there are server-side rendering code click me
19. Enable Gzip compression on the Web server
20. UseMemo caches a large amount of calculated data, and useCallback caches functions to avoid repeated creation
useMemo
The idea behind useMemo is similar to that of Memo. The second argument is an array of deps, and the changes in the parameters in the array determine whether useMemo updates the callback function.
The useMemo argument is the same as the useCallback argument. The difference is that useMemo returns a cached value, while useCallback returns a function.
- UseMemo reduces unnecessary rendering
// Lists wrapped with useMemo can be restricted to updating the list if and only if the list changes, so that the list can be avoided recycling
{useMemo(() = > (
<div>{
list.map((i, v) => (
<span
key={v} >
{i.patentName}
</span>
))}
</div>
), [list])}
Copy the code
- UseMemo reduces the rendering times of subcomponents
useMemo(() = >({/* Reduced rendering of PatentTable component */ }
<PatentTable
getList={getList}
selectList={selectList}
cacheSelectList={cacheSelectList}
setCacheSelectList={setCacheSelectList} />
), [listshow, cacheSelectList])
Copy the code
- UseMemo avoids a lot of unnecessary computing overhead
const Demo=() = >{
/* Use useMemo to wrap the log function in useMemo to avoid redeclaring each component update, and to limit context execution */
const newLog = useMemo(() = >{
const log =() = >{
// Calculate a lot
// There is no way to get other values in real time
}
return log
},[])
// or
constLog2 = useMemo (()=>{// Calculate a lot
return // The calculated value
},[list])
return <div onClick={()= >newLog()} >{log2}</div>
}
Copy the code
useCallback
Both useMemo and useCallback receive the same parameters, and are executed only when the dependency changes. UseMemo returns the result of a function run, and useCallback returns the function; When a parent component passes a function to a child component, the function component generates new props each time. This causes the function to change each time it is passed to the child component. This can trigger updates to the child component, some of which are unnecessary.
const Father=({ id }) = >{
const getInfo = useCallback((sonName) = >{
console.log(sonName)
},[id])
return <div>{/* Click the button to trigger the parent component update, but the child component is not updated */}<button onClick={() = >SetNumber (number+1)} > increment</button>
<DemoChildren getInfo={getInfo} />
</div>
}
/ * the react. Memo * /
const Children = React.memo((props) = >{
/* Only when the child component is initialized is printed */
console.log('Child Component Update',props.getInfo())
return <div>Child components</div>
})
Copy the code
The useCallback must be compatible with the React. Memo pureComponent, otherwise it will not improve performance and may degrade performance.
The react-hooks are not meant to replace the class-declared components completely. For complex components, class components are preferred, but we can separate class components into funciton components, which are responsible for logical interaction and which need dynamic rendering according to business requirements. Then with usememo and other apis, to improve performance. There are also restrictions on react-hooks use, such as they cannot be placed in process control statements, and execution context requirements.
21. Lazy initialization
Before optimization:
function table(props) {
const [state,setState]=useState(createRows(props.count))
}
// This will cause createRows to be invoked every time a component is updated
const values = createRows(props.count)
const [state,setState]=useState(values)
Copy the code
After the optimization:
function table(props) {
const [state,setState]=useState(() = >{
return createRows(props.count)
})
}
Copy the code
conclusion
It is recommended to benchmark and measure performance first. Consider using the Chrome Timeline analysis and visualization component. You can see which components have been uninstalled, installed, updated, and how much time they have spent relative to each other. It will help you begin the journey of performance tuning.
Complete documentation: hejialianghe. Gitee. IO/react/react…