react 性能优化
使用 React Fragments 避免额外标记 片段不会向组件引入任何额外标记,但它仍然为两个相邻标记提供父级,因此满足在组件顶级具有单个父级的条件.
export default class NestedRoutingComponent extends React.Component {
render() {
return (
<>
<h1>This is the Header Component</h1>
<h2>Welcome To Demo Page</h2>
</>
)
}
}
不要使用内联函数定义
上面的函数创建了内联函数。每次调用 render 函数时都会创建一个函数的新实例,render 函数会将该函数的新实例绑定到该按钮。
避免 componentWillMount() 中的异步请求 在检索数据时 React 会触发组件的 render 函数。因此第一个调用的渲染仍然不包含它所需的数据。
这样一开始渲染组件没有数据,然后检索数据,调用 setState,还得重新渲染组件。在 componentWillMount 阶段进行 AJAX 调用没有好处可言。
我们应避免在此函数中发出 Async 请求。这些函数和调用可以延迟到 componentDidMount 生命周期事件里。
import React from "react";
import axios from "axios";
export default class UsingAsyncInComponentWillMount extends React.Component {
constructor() {
this.state = {
userData: null
}
}
componentWillMount() {
axios.get("someResourceUrl").then((data) => {
this.setState({
userData: data
});
});
}
render() {
return (
<>
<b>UserName: {this.state.name}</b>
<b>UserAge: {this.state.age}</b>
</>
)
}
}
在 Constructor 的早期绑定函数
每次调用 render 函数时都会创建并使用绑定到当前上下文的新函数,但在每次渲染时使用已存在的函数效率更高。优化方案如下:
import React from "react";
export default class DelayedBinding extends React.Component {
constructor() {
this.state = {
name: "Mayank"
}
this.handleButtonClick = this.handleButtonClick.bind(this)
}
handleButtonClick() {
alert("Button Clicked: " + this.state.name)
}
render() {
return (
<>
<input type="button" value="Click" onClick={this.handleButtonClick} />
</>
)
}
}
箭头函数与构造函数中的绑定 处理类时的标准做法就是使用箭头函数。使用箭头函数时会保留执行的上下文。
我们调用它时不需要将函数绑定到上下文。
import React from "react";
export default class DelayedBinding extends React.Component {
constructor() {
this.state = {
name: "Mayank"
}
}
handleButtonClick = () => {
alert("Button Clicked: " + this.state.name)
}
render() {
return (
<>
<input type="button" value="Click" onClick={this.handleButtonClick} />
</>
)
}
}
避免使用内联样式属性
import React from "react";
export default class InlineStyledComponents extends React.Component {
render() {
return (
<>
<b style={{"backgroundColor": "blue"}}>Welcome to Sample Page</b>
</>
)
}
}
内联样式需要话费更多时间来处理脚本和渲染,映射传递给实际 css 属性的所有样式规则.添加的内联样式是 JavaScript 对象而不是样式标记. 样式 backgroundColor 需要转换为等效的 css 样式属性,然后才应用样式.
优化 React 中的条件渲染 减少组件的安装和卸载. 条件渲染进行提升.
不要在 render 方法中导出数据
import React from "react";
export default class RenderFunctionOptimization extends React.Component {
constructor() {
this.state = {
name: "Mayank"
}
}
render() {
this.setState({
name: this.state.name + "_"
});
return (
<div>
<b>User Name: {this.state.name}</b>
</div>
);
}
}
更新组件状态的问题在于,当状态更新时会触发另一个 render 循环,后者在内部会再触发一个 render 循环,以此类推。
每次渲染时,都会在内存中创建一个新函数(因为它是在 render 函数中创建的),并将对内存中新地址的新引用传递给 ,虽然输入完全没有变化,该 Button 组件还是会重新渲染。不要在 render 中 定义这些函数官方推荐: createAlertBox = () => { alert(this.props.message); }; <Button onClick={createAlertBox} />通过传递 createAlertBox 方法,它就和 SomeComponent 重新渲染无关了,甚至和 message 这个属性是否修改也没有关系。createAlertBox 内存中的地址不会改变,这意味着 Button 不需要重新渲染,节省了处理时间并提高了应用程序的渲染速度class LoggingButton extends React.Component { handleClick() { console.log('this is:', this); } render() { // 此语法确保 `handleClick` 内的 `this` 已被绑定。 return ( <button onClick={() => this.handleClick()}> Click me </button> ); }}此语法问题在于每次渲染 LoggingButton 时都会创建不同的回调函数。在大多数情况下,这没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染。我们通常建议在构造器中绑定或使用 class fields 语法来避免这类性能问题。显示与隐式参数传递<button onClick = { (e)=> this.handleClick( id,e ) }></button><button onClick = { this.handleClick.bind( this,id ) }></button>上述两种方式是等价的,分别通过 箭头函数 和 Function.prototype.bind 来实现。在上面两种情况下,React的事件对象 e 会被作为第二个参数传递。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 bind 的方式,事件对象以及更多的参数将会被隐式的进行传递。神奇的 childrenimport React, { useContext, useState } from "react";const ThemeContext = React.createContext();function ChildNonTheme() { console.log("不关心皮肤的子组件渲染了"); return <div>我不关心皮肤,皮肤改变的时候别让我重新渲染!</div>;}function ChildWithTheme() { const theme = useContext(ThemeContext); return <div>我是有皮肤的哦~ {theme}</div>;}function ThemeApp({ children }) { const [theme, setTheme] = useState("light"); const onChangeTheme = () => setTheme(theme === "light" ? "dark" : "light"); return ( <ThemeContext.Provider value={theme}> <button onClick={onChangeTheme}>改变皮肤</button> {children} </ThemeContext.Provider> );}export default function App() { return ( <ThemeApp> <ChildWithTheme /> <ChildNonTheme /> <ChildNonTheme /> <ChildNonTheme /> <ChildNonTheme /> <ChildNonTheme /> <ChildNonTheme /> <ChildNonTheme /> </ThemeApp> );}这本质上是由于 React 是自上而下递归更新,<ChildNonTheme /> 这样的代码会被 babel 翻译成 React.createElement(ChildNonTheme) 这样的函数调用,React官方经常强调 props 是immutable 的,所以在每次调用函数式组件的时候,都会生成一份新的 props 引用。通过 children 传入后直接渲染,由于 children 从外部传入的,也就是说 ThemeApp 这个组件内部不会再有 React.createElement 这样的代码,那么在 setTheme 触发重新渲染后,children 完全没有改变,所以可以直接复用。让我们再看一下被 ThemeApp 包裹下的 <ChildNonTheme />,它会作为 children 传递给 ThemeApp,ThemeApp 内部的更新完全不会触发外部的 React.createElement,所以会直接复用之前的 element 结果:import React, { useContext, useState } from "react";const ThemeContext = React.createContext();export function ChildNonTheme() { console.log("不关心皮肤的子组件渲染了"); return <div>我不关心皮肤,皮肤改变的时候别让我重新渲染!</div>;}export function ChildWithTheme() { const theme = useContext(ThemeContext); return <div>我是有皮肤的哦~ {theme}</div>;}function ThemeApp({ children }) { const [theme, setTheme] = useState("light"); const onChangeTheme = () => setTheme(theme === "light" ? "dark" : "light"); return ( <ThemeContext.Provider value={theme}> <button onClick={onChangeTheme}>改变皮肤</button> {children} </ThemeContext.Provider> );}export default function App() { return ( <ThemeApp> <ChildWithTheme /> <ChildNonTheme /> <ChildNonTheme /> <ChildNonTheme /> <ChildNonTheme /> <ChildNonTheme /> <ChildNonTheme /> <ChildNonTheme /> </ThemeApp> );}总结下来,就是要把渲染比较费时,但是不需要关心状态的子组件提升到「有状态组件」的外部,作为 children 或者props传递进去直接使用,防止被带着一起渲染。React.memo神奇的 children组合 Providersconst StateProviders = ({ children }) => ( <LogProvider> <UserProvider> <MenuProvider> <AppProvider> {children} </AppProvider> </MenuProvider> </UserProvider> </LogProvider>)function App() { return ( <StateProviders> <Main /> </StateProviders> )}function composeProviders(...providers) { return ({ children }) => providers.reduce( (prev, Provider) => <Provider>{prev}</Provider>, children, )}const StateProviders = ({ children }) => ( <LogProvider> <UserProvider> <MenuProvider> <AppProvider> {children} </AppProvider> </MenuProvider> </UserProvider> </LogProvider>)function App() { return ( <StateProviders> <Main /> </StateProviders> )}尽量提升渲染无关的子组件元素到「有状态组件」的外部。 在需要的情况下对 Context 进行读写分离。 包装Context 的使用,注意错误处理。 组合多个 Context,优化代码。
最后更新于
这有帮助吗?