性能优化
1. list渲染使用唯一-key --- 加快DOM Diff更新
2. 超列表使用虚拟滚动 --- 减少无意义列表渲染,提升用户体验
3. 不使用内连对象 --- 避免对象饮用重复创建导致组件render
4. JSX中直接引入 --- 加快svg展示速度 可自定义svg属性
首先是列表渲染使用唯一 key
我们都知道 React 在执行 Diff 算法时,会用到元素的 key 属性,它可以帮助 React 定位更改、添加或删除的项目,
在重新 render 的时候尽可能少得更新 DOM 内容,加快更新速度。
当然 key 值的设置也是有一定要求的,那就是要保证稳定性。
对于一个列表组件来说,每一个列表项的 key 应该是唯一且固定的,比如每个列表项数据固定的 id。
import React from "react";
export default class MyComponent extends React.Component {
render() {
const List = data.map(item => {
return <li key={item.id}>{item.name}</li>;
})
return (
<ul>
{List}
</ul>
)
}
}
除了像 id 这样列表数据已有的单一字段,也可以使用多个字段
,通过一些简单的组合或者算法,生成一个唯一且固定的值作为 key 的值。
比如我们可以通过列表数据的 type 字段以及 name 字段唯一确定一个列表项。
const List = data.map(item => {
return <li key={`${item.type}_${item.name}`}>{item.name}</li>;
})
如果我们找不到 id 或者多个字段确定唯一列表项的话,一些情况下我们也可以使
用列表数据的索引来作为 key 值,比如:列表项不会被重新排序或过滤;
列表顶部或中间不会插入或删除列表项等
(否则 React 可能会因为索引导致更新效率极大降低甚至数据更新错误)。
const List = data.map((item, index) => {
return <li key={index}>{item.name}</li>;
})
第二点是长列表使用虚拟滚动,相信大家在平时开发时经常会遇到长列表,如会话列表、订单列表等等,列表项成百上千甚至更多,页面在渲染这些列表项时非常耗时,有时候页面可能都无法与用户交互,出现假死的情况,用户体验很不好。
import React from "react";
import "./index.css";
// 列表项
const Row = ({ index, style }) => (
<div className={index % 2 ? "ListItemOdd" : "ListItemEven"}>
Row {index}
</div>
);
// 定义一个长度为 1000 的数组用于生成列表项
const arr = new Array(10000).fill('*');
let startRender = 0;
let finishRender = 0;
// 列表
class App extends React.Component {
componentDidMount() {
finishRender = new Date().getTime();
console.log('renderTime', (finishRender - startRender)/1000, 's');
}
render() {
startRender = new Date().getTime();
return (
<div className="List">
{arr.map((el, index) => <Row key={index} index={index} />) }
</div>
)
}
}
export default App;
我们以 npm 包 react-window 来举例,仍然是 1 万个列表项,我们用 react-window 提供的 FixedSizeList 组件作为列表项的父组件,并且为父组件提供列表的宽高、列表项高度以及列表项数量等数据:
import React from "react"
import { FixedSizeList as List } from "react-window"
import "./index.css"
// 列表项
const Row = ({ index, style }) => (
<div className={index % 2 ? "ListItemOdd" : "ListItemEven"} style={style}>
Row {index}
</div>
);
// 定义一个长度为 1000 的数组用于生成列表项
const arr = new Array(10000).fill('*')
let startRender = 0
let finishRender = 0
// 列表
class App extends React.Component {
componentDidMount() {
finishRender = new Date().getTime()
console.log('renderTime', (finishRender - startRender)/1000, 's')
}
render() {
startRender = new Date().getTime()
return (
<List
className="List"
height={200}
itemCount={arr.length}
itemSize={35}
width={300}
>
{Row}
</List>
);
}
}
export default App;
不使用内联对象
我们可以看到 OtherComponent 接收的 props 中还有对象 extraProps,也会出现上面多次创建对象引用的问题,但由于使用了 App 的 props.title 值,我们无法像上述的解决办法一样,在组件外初始化一次对象 extraProps。
那么这时我们可以使用 ES6 扩展运算符,将 extraProps 对象解构,让 OtherComponent 接收到的 props 为基本类型 String,如果 props.title 的值没有变化,则通过浅比较一样不会触发 render:
import React from "react"
import OtherComponent from './OtherComponent'
const styles = { paddingTop: 10 }
const App = props => {
// props.title 为 String 类型
const extraProps = { title: props.title }
return <OtherComponent
style={styles}
{...extraProps}
/>
}
在 JSX 中直接引入 svg。大家可能经常会在项目中使用到 svg 格式的图片
import React from 'react'
const Thumb = props => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="#000000"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
{...props} >
<path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"></path>
</svg>
);
}
export default Thumb;
class App extends React.Component {
render() {
return (
<div className="content">
<p>This is content</p>
<p className="row" >
<span> 使用 img 标签引入 svg:</span>
<img
className="thumb"
alt="thumb"
src="https://my.app.com/cdn/thumb.svg"
/
</p
<p className="row" >
<span>react 无状态组件引入 svg:</span>
<Thumb/>
</p>
<p className="row" >
<span> 更改属性后的 svg:</span>
<Thumb stroke="#ff6044" />
</p>
</div>
)
}
}
export default App;