国外优秀网站建设,工业设计专业怎么样,百度网盘24小时人工电话,做网页需要什么本文主要讲解实战项目中React性能优化的方法#xff0c;主要分为三个大的方面#xff1a;减少不必要的组件更新、组件优化以及tree-shaking#xff0c;共11个方法
一、减少不必要组件更新
以下是一些可以避免在 React 提交阶段进行不必要重新渲染的方法#xff1a;
1、使…本文主要讲解实战项目中React性能优化的方法主要分为三个大的方面减少不必要的组件更新、组件优化以及tree-shaking共11个方法
一、减少不必要组件更新
以下是一些可以避免在 React 提交阶段进行不必要重新渲染的方法
1、使用 React.memo对于函数组件和 PureComponent对于类组件 React.memo React.memo 是一个高阶组件用于包装函数组件。它通过对组件的 props 进行浅层比较来决定是否重新渲染组件。 示例 import React from react;const MyComponent React.memo(({ data }) {// 组件渲染逻辑return div{data}/div;
});当 data 的引用没有发生变化时组件将不会重新渲染。 PureComponent对于类组件 PureComponent 会对 props 和 state 进行浅层比较。如果它们没有变化组件将不会重新渲染。 示例 以下是一个在类组件中使用 PureComponent 的示例包括数据传递和更新
import React, { PureComponent } from react;class MyComponent extends PureComponent {// 构造函数初始化状态constructor(props) {super(props);this.state {count: 0,name: Initial Name,};}// 处理点击事件更新状态handleClick () {// 示例 1更新数字状态this.setState({ count: this.state.count 1 });// 示例 2更新字符串状态如果 name 是从父组件传递的 props 且未变化不会触发重新渲染// 假设 name 是从父组件传递的 props以下更新不会触发重新渲染如果 name 未变化// this.setState({ name: this.props.name });};render() {return (divpCount: {this.state.count}/ppName: {this.state.name}/pbutton onClick{this.handleClick}Increment Count/button/div);}
}// 父组件
class ParentComponent extends React.Component {constructor(props) {super(props);this.state {name: Parent Name,};}handleNameChange () {this.setState({ name: Updated Name });};render() {return (divMyComponent name{this.state.name} /button onClick{this.handleNameChange}Change Name/button/div);}
}export default ParentComponent;在这个例子中 MyComponent 是一个继承自 PureComponent 的类组件。它有一个 count 状态用于数字的递增展示还有一个 name 状态也可以是从父组件传递的 props用于展示字符串。 在 render 方法中展示了 count 和 name 的值并有一个按钮用于触发 count 的递增。 ParentComponent 是父组件它有一个 name 状态并将其传递给 MyComponent。还有一个按钮用于更改 name 的状态。
PureComponent 会对 props 和 state 进行浅层比较。如果 props 或 state 的引用没有变化组件将不会重新渲染。在上面的例子中如果 MyComponent 接收到的 props.name 没有变化并且 state 中的 count 没有更新MyComponent 就不会重新渲染。
注意事项
PureComponent 的浅层比较对于基本数据类型如数字、字符串、布尔值是有效的但对于复杂数据类型如对象、数组它只会比较引用。如果对象或数组的内容发生变化但引用不变PureComponent 可能不会检测到变化。在这种情况下可以使用 immutable.js 或手动在 shouldComponentUpdate 中进行深层比较。如果组件的 props 或 state 变化频繁且计算成本不高或者需要进行深层比较可能不需要使用 PureComponent。
2、使用 useCallback 和 useMemo useCallback useCallback 用于记忆函数确保传递给子组件的函数在依赖项不变的情况下不会重新创建。 示例 import React, { useState, useCallback } from react;function ParentComponent() {const [count, setCount] useState(0);const handleClick useCallback(() {// 处理点击的逻辑}, [count]); // 仅当 count 变化时重新创建函数return (divChildComponent onClick{handleClick} //div);
}useMemo useMemo 用于记忆计算结果避免在每次渲染时都进行昂贵的计算。 示例 import React, { useState, useMemo } from react;function MyComponent() {const [data, setData] useState([]);const computedValue useMemo(() {// 进行昂贵的计算return data.map((item) item * 2);}, [data]);return div{computedValue}/div;
}3、优化 shouldComponentUpdate对于类组件
在类组件中可以重写 shouldComponentUpdate 方法来进行更细粒度的控制。
import React from react;class MyComponent extends React.Component {shouldComponentUpdate(nextProps, nextState) {// 进行 props 和 state 的比较决定是否更新return (nextProps.someValue! this.props.someValue ||nextState.someState! this.state.someState);}render() {return div{/*... */}/div;}
}4、避免在渲染阶段进行副作用操作
副作用操作如网络请求、订阅事件等应该在 useEffect 中进行而不是在组件的渲染函数中。这样可以确保渲染函数的纯粹性减少不必要的重新渲染触发。
import React, { useState, useEffect } from react;function MyComponent() {const [data, setData] useState(null);useEffect(() {// 进行网络请求获取数据fetchData().then((result) setData(result));}, []); // 空依赖数组确保只在组件挂载时执行一次return div{data? data : Loading...}/div;
}5、正确设置 key 属性对于列表渲染
在渲染列表时为每个列表项设置唯一的 key 属性。这有助于 React 更高效地识别和更新列表项。import React from react;function ListComponent({ items }) {return (ul{items.map((item) (li key{item.id}{item.name}/li))}/ul);
}二、组件优化
1、useIntersectionObserver
在 React 项目中使用 TypeScript 和 useIntersectionObserver 实现虚拟滚动懒加载的示例代码
import React, { useEffect, useRef } from react;function LazyLoadComponent() {const imageRefs useRefHTMLDivElement[]([]);const observerRef useRefIntersectionObserver | null(null);useEffect(() {const options {root: null,rootMargin: 0px,threshold: 0.1,};observerRef.current new IntersectionObserver((entries) {entries.forEach((entry) {if (entry.isIntersecting) {// 这里可以进行实际的图片加载或其他数据加载逻辑const index imageRefs.current.findIndex((ref) ref entry.target);console.log(图片 ${index 1} 进入可视区域);// 加载完成后可以停止观察该元素observerRef.current?.unobserve(entry.target);}});}, options);// 开始观察所有的元素imageRefs.current.forEach((ref) {if (ref) {observerRef.current?.observe(ref);}});return () {// 组件卸载时清理观察者if (observerRef.current) {observerRef.current.disconnect();}};}, []);const imageList Array.from({ length: 10 }, (_, index) index 1);return (div style{{ height: 300px, overflowY: auto }}{imageList.map((item, index) (divkey{index}ref{(ref) {imageRefs.current[index] ref as HTMLDivElement;}}style{{height: 200px,width: 200px,backgroundColor: gray,marginBottom: 10px,}}/))}/div);
}export default LazyLoadComponent;示例详述
useRef 用于创建 imageRefs 和 observerRef 引用imageRefs 用于存储每个元素的引用observerRef 用于存储 IntersectionObserver 的实例。useEffect 中创建了 IntersectionObserver 实例并设置了观察的选项。在 entries 的回调中当元素进入可视区域时进行相应的操作这里只是简单地打印了信息。在返回的组件结构中模拟了一个包含多个灰色方块的列表每个方块都有一个 ref用于被观察。
注意实际应用中你需要根据具体的需求进行更多的逻辑处理和样式调整比如实际的图片加载、数据获取等操作。
2、react-lazyload
在 React 项目中react-lazyload 可以用于长列表加载。
一基本原理和适用场景
react-lazyload 的核心原理是监听元素是否进入可视区域当元素进入可视区域时才触发实际的加载操作。对于长列表加载场景这一特性非常有用。
在长列表中可能存在大量的数据项需要展示一次性加载所有数据项可能会导致性能问题尤其是在处理图片等资源较大的内容时。使用 react-lazyload 可以延迟加载列表中的元素只有当用户滚动到相应位置元素即将进入可视区域时才进行加载这样可以显著提高初始页面加载速度和整体的用户体验。
二使用示例
以下是一个在 React 项目中使用 react-lazyload 处理长列表加载的简单示例 首先安装 react-lazyload npm install react-lazyload然后在代码中使用
import React from react;
import LazyLoad from react-lazyload;
import ./App.css;const ListItem ({ index }) (div style{{ height: 100, backgroundColor: lightblue, marginBottom: 10 }}列表项 {index}/div
);const LongList () {const listLength 100;const listItems [];for (let i 0; i listLength; i) {listItems.push(ListItem key{i} index{i} /);}return (div style{{ height: 500, overflowY: scroll }}{listItems.map((item, index) (LazyLoad key{index} once{true}{item}/LazyLoad))}/div);
};export default LongList;在上述示例中创建了一个包含 100 个列表项的长列表通过 react-lazyload 的 LazyLoad 组件包裹每个列表项实现了懒加载功能。当用户滚动列表时每个列表项会根据其是否进入可视区域来决定是否进行加载。
三性能优势 减少初始加载时间在长列表场景下不必在页面初始加载时就加载所有的列表项内容尤其是当列表项包含较大的图片或其他资源时这可以大大减少初始页面加载时间让用户更快地看到页面的主要内容。 降低内存占用由于不是一次性加载所有数据因此可以减少内存的占用特别是对于移动设备或内存有限的环境这有助于提高设备的响应速度和整体性能。 优化用户体验通过逐步加载内容避免了因为大量数据同时加载而导致的页面卡顿或无响应现象用户可以在滚动过程中平滑地浏览列表内容提升了用户体验。
四注意事项
样式处理在使用 react-lazyload 时需要注意列表项的样式设置。特别是当列表项的高度或宽度不确定时可能会导致懒加载的判断出现偏差。可以通过固定列表项的尺寸或者使用合适的 CSS 布局技巧来解决这个问题。
三、tree-shaking
1、package.json 中的 sideEffects 配置
在 package.json 中添加 sideEffects 字段 如果你的项目中所有的 .css 文件都没有副作用例如没有在 CSS 中使用 :global 或类似会产生全局影响的选择器可以将 sideEffects 配置为 false这将告诉 Webpack 可以更激进地进行 Tree Shaking。 {name: your-app,version: 1.0.0,sideEffects: false}如果项目中有部分文件有副作用你可以这样配置
{name: your-app,version: 1.0.0,sideEffects: [*.css,some-module-with-side-effects]
}这里列出了有副作用的文件或模块其他未列出的模块将被更积极地进行 Tree Shaking。
2、组件按需加载Babel-plugin-import
以下是一个在 React 项目中使用 Babel-plugin-import 的代码示例。 首先创建一个简单的 React 项目结构 my-react-app/
├── package.json
├── src/
│ ├── App.js
│ └── index.js在 package.json 中添加必要的依赖 {dependencies: {react: ^18.2.0,react-dom: ^18.2.0},devDependencies: {babel/core: ^7.22.10,babel/plugin-proposal-class-properties: ^7.22.3,babel/plugin-transform-runtime: ^7.22.5,babel/preset-env: ^7.22.5,babel/preset-react: ^7.18.6,babel-loader: ^9.1.2}
}创建 .babelrc 文件并配置 Babel-plugin-import {presets: [babel/preset-react,babel/preset-env],plugins: [[import,{libraryName: antd,libraryDirectory: es,style: css}]]
}在 src/App.js 中编写示例代码 import React from react;
// 使用 Babel-plugin-import 优化引入 antd 的 Button 组件
import { Button } from antd;const App () {return (divButton typeprimary点击我/Button/div);
};export default App;在 src/index.js 中渲染 App 组件 import React from react;
import ReactDOM from react-dom;
import App from ./App;ReactDOM.render(App /, document.getElementById(root));假设使用 Webpack 进行构建配置 webpack.config.js const path require(path);module.exports {entry: ./src/index.js,output: {path: path.resolve(__dirname, dist),filename: bundle.js},module: {rules: [{test: /\.(js|jsx)$/,exclude: /node_modules/,use: {loader: babel-loader}}]}
};这样在项目中通过 Babel-plugin-import 对 antd 的组件引入进行了优化实际应用中可以根据自己的项目需求和库的使用情况进行相应的调整。
3、使用 Lodash 库的优化
以下是一个简单的代码示例展示如何在 React 项目中使用 lodash-es 版本并结合 Webpack 的 Tree Shaking 功能 创建一个 React 项目 npx create-react-app my-lodash-example
cd my-lodash-example安装 lodash-es npm install lodash-es创建一个示例组件 App.js import React from react;
import pick from lodash-es/pick;const data {name: John,age: 30,city: New York
};const filteredData pick(data, [name, age]);const App () {return (divpName: {filteredData.name}/ppAge: {filteredData.age}/p/div);
};export default App;在 package.json 中确保 sideEffects: false如果你的项目没有真正的副作用 {name: my-lodash-example,version: 0.1.0,private: true,dependencies: {//...lodash-es: ^4.17.21,react: ^18.2.0,react-dom: ^18.2.0,react-scripts: 5.0.1},sideEffects: false,scripts: {start: react-scripts start,build: react-scripts build,test: react-scripts test,eject: react-scripts eject}
}因为 create-react-app 隐藏了 Webpack 配置但是在生产构建模式下npm run build它默认会启用 Tree Shaking。
在这个示例中我们只从 lodash-es 中引入了 pick 函数并且通过配置 sideEffects 和在生产构建时Webpack 会进行 Tree Shaking 来去除未使用的代码。
在 create-react-app 项目中虽然隐藏了 Webpack 配置但默认在生产构建时已经开启了一些优化措施包括 Tree Shaking不过你可以通过以下几种方式来进一步优化和确保 Tree Shaking 效果
4、使用 purgecss针对 CSS 安装 purgecss 及其相关依赖 npm install purgecss purgecss-webpack-plugin --save-dev在 webpack.config.js虽然 create-react-app 隐藏了此文件但可以通过 eject 暴露出来这是一个不可逆操作需谨慎考虑中添加 PurgeCSSPlugin const PurgeCSSPlugin require(purgecss-webpack-plugin);module.exports {//...其他配置plugins: [new PurgeCSSPlugin({paths: glob.sync(${paths.appSrc}/**/*, { nodir: true }),}),],
};这将帮助去除未使用的 CSS 代码与 Tree Shaking 一起优化项目体积。
请注意在对 create-react-app 的配置进行修改时尤其是涉及到 eject 操作要充分了解其影响和风险并且在修改前最好备份项目代码。