近期项目遇到了一些性能瓶颈,其中就包括组件重复渲染的问题。就在我为如何定位组件重复渲染问题发愁时,突然想起了之前看过别人文章推荐过一个库:why-did-you-render。
于是抱着试一试的想法试了一下,发现是真香啊。话不多说,我们来看下如何使用这个神器以及其使用效果是什么样的。
安装
这个库适用于 React 以及 React Native。安装很简单,和其他 npm 库一样安装即可。
使用 npm:
npm install @welldone-software/why-did-you-render --save-dev
使用 yarn:
yarn add --dev @welldone-software/why-did-you-render
使用 pnpm:
pnpm add -D -w @welldone-software/why-did-you-render
初始化
在安装成功之后,首先我们需要创建一个 wdyr.js 配置文件。
// wdyr.js
if (__DEV__) {
const React = require('react');
const whyDidYouRender = require('@welldone-software/why-did-you-render');
whyDidYouRender(React);
}
要特别注意,千万别在生产环境中引入这个库!
创建配置文件后,需要在你应用入口文件的最开始被 import:
React:
import './wdyr'; // <--- first import
import React from 'react';
import App from './App';
ReactDOM.render(<App/>, document.getElementById('root'));
React Native:
import './wdyr'; // <--- first import
import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('Appname', () => App);
使用
wdyr 支持多种配置项:github.com/welldone-so… 。
其中比较常用的包括:
- trackAllPureComponents: 如果设置为 true,则会自动检测所有 PureComponent 以及 React.memo 包裹的组件
- trackExtraHooks: 用于检测 hooks 的变化是否会引起 re-render
// wdyr.js
if (__DEV__) {
const React = require('react');
const ReactRedux = require('react-redux');
const whyDidYouRender = require('@welldone-software/why-did-you-render');
whyDidYouRender(React, {
trackAllPureComponents: true,
trackExtraHooks: [
[ReactRedux, 'useSelector']
],
});
}
如果你的组件并不是 PureComponent 或 React.memo,那么只要直接设置组件的 whyDidYouRender 属性为 true,wdyr 就会对你的组件进行检测。
const Comp = () => {...};
Comp.whyDidYouRender = true;
export default Comp;
使用效果
在配置完成之后,我们来看看实际使用效果:
我在自己的 RN 项目中引入了这个库,用于检测项目首页的组件渲染问题,首页的代码结构大致如下:
// Homepage.tsx
const Homepage = () => {
// 一些 state
const [state1, setState1] = useState();
const [state2, setState2] = useState();
// 一些 redux
const state1 = useSelector(state1);
const state2 = useSelector(state2);
// ...
return (
{/* 首页头部组件 */}
<Header />
{/* 首页其他组件 */}
<Comp1 ... />
<Comp2 ... />
<Comp3 ... />
)
}
AppRegistry.registerComponent('Homepage', () => Homepage);
接下来,我要对 Header 组件进行渲染检测,由于不是 React.memo,所以需要手动设置 whyDidYouRender 属性为 true:
// Header.tsx
const Header = () => {
// ...
return <>{...}</>
}
Header.whyDidYouRender = true;
export default Header;
配置成功后,重新进行本地调试,可以看到输出了如下 render log:
渲染优化
通过分析前面产生的 render log,我们可以得到 Header 组件触发 re-render 的主要原因:
这句 log 的意思是 Header 组件由于 props 发生变化,导致触发了 re-render。而 props 发生变化的原因是父组件 Homepage 的 state / redux store 触发了更新。
大家看到这里可能会有疑问,我 Header 组件明明没有接收任何 props,为什么还会因为 props 更新而重新渲染呢?
请记住:JSX 中,
<Header />只是React.createElement('Header', {})的语法糖。 这意味着每当 Header 的父组件重新渲染时,Header都会尝试使用新的 props 对象来重新渲染。
在 log 中 wdyr 也给出了优化建议,由于 Header 不接收任何 props,意味着父组件的更新并不影响 Header 的更新,因此可以使用 React.memo 将其包裹起来,减少重复渲染开销。
优化后的 Header 组件如下:
// Header.tsx
import React from 'react';
const Header = () => {
// ...
return <>{...}</>
}
Header.whyDidYouRender = true;
export default React.memo(Header);
优化后可以发现,Header 组件不会再出现 re-render 的情况了。
写在最后
why-did-you-render 的确是个检测 React / React Native 组件重复渲染的神器,它不仅能帮忙定位 re-render 问题,还会提供一些优化建议。
大家如果需要对项目进行一些渲染优化和性能优化,不妨尝试一下。
但千万记住,只能在本地调试中使用它!
BTW, React Native 项目在 Expo 上使用这个库可能会遇到一些问题,请参考这里的解决方案:github.com/welldone-so…