react 性能优化
使用 React Fragments 避免额外标记 片段不会向组件引入任何额外标记,但它仍然为两个相邻标记提供父级,因此满足在组件顶级具有单个父级的条件.
不要使用内联函数定义
上面的函数创建了内联函数。每次调用 render 函数时都会创建一个函数的新实例,render 函数会将该函数的新实例绑定到该按钮。
避免 componentWillMount() 中的异步请求 在检索数据时 React 会触发组件的 render 函数。因此第一个调用的渲染仍然不包含它所需的数据。
这样一开始渲染组件没有数据,然后检索数据,调用 setState,还得重新渲染组件。在 componentWillMount 阶段进行 AJAX 调用没有好处可言。
我们应避免在此函数中发出 Async 请求。这些函数和调用可以延迟到 componentDidMount 生命周期事件里。
在 Constructor 的早期绑定函数
每次调用 render 函数时都会创建并使用绑定到当前上下文的新函数,但在每次渲染时使用已存在的函数效率更高。优化方案如下:
箭头函数与构造函数中的绑定 处理类时的标准做法就是使用箭头函数。使用箭头函数时会保留执行的上下文。
我们调用它时不需要将函数绑定到上下文。
避免使用内联样式属性
内联样式需要话费更多时间来处理脚本和渲染,映射传递给实际 css 属性的所有样式规则.添加的内联样式是 JavaScript 对象而不是样式标记. 样式 backgroundColor 需要转换为等效的 css 样式属性,然后才应用样式.
优化 React 中的条件渲染 减少组件的安装和卸载. 条件渲染进行提升.
不要在 render 方法中导出数据
更新组件状态的问题在于,当状态更新时会触发另一个 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,优化代码。
最后更新于
这有帮助吗?