React性能优化整理
class 组件的优化
通过判断减少数据变化触发的重新渲染, 以及之后的 dom diff
shouldComponentUpdate(nextProps, nextState) {
if (this.props.color !== nextProps.color) {
return true;
}
if (this.state.count !== nextState.count) {
return true;
}
return false;
}JavaScript 对象引用问题
函数式语言当中, 语言设计允许两个对象一样, 举例 Clojure:
(= {:a 1} {:a 1}) ; true
(identical? {:a 1} {:a 1}) ; false递归匹配, 性能并不高.
JavaScript 对象基于引用传值, 比较单一
{a: 1} === {a: 1} // false大体方案, 通过手动维护, 让相同的数据尽量保证引用一致, 控制性能.
function updateColorMap(colormap) {
return {...colormap, right: 'blue'};
}useMemo 优化
每个函数体当中生成的对象都会有新的引用, useMemo 可以保留一致的引用.
const myObject = useMemo(() => ({ key: "value" }), [])注意: 用花括号直接写对象基本上就是新的引用了,
{}
{a: 1}
{...obj}一般组件内部不变的对象, 都是从 state, ref, 再或者组件外全局有一个引用.
react.memo 优化
判断参数是否改变, 如果没有改变, 就直接复用已有的组件, 不重新生成:
const MyComponent = React.memo(function MyComponent(props) {
/* only rerenders if props change */
});React.memo 有第二个参数, 用于自定义判断的规则:
const MemoItem = React.memo(Item, (prevProps, nextProps) => {
if (prevProps.item.selected === nextProps.item.selected) {
return true;
}
return false;
});
useCallback 优化
使用 React.memo 包裹组件:
let Inner: FC<{
onClick: () => void
}> = React.memo((props) => {
return <div>
<span>inner</span>
</div>;
});使用 useCallback
let Outer: FC<{}> = React.memo((props) => {
const [counter, setCounter] = useState(0);
const onClick = useCallback(()=>{
setCounter(prevState => ++prevState)
},[]);
return <div>
<span>outer: {counter}</span>
<Inner onClick={onClick} />
</div>;
});避免 render 当中的 DOM 操作
let NewComponent: FC<{}> = React.memo((props) => {
let elRef = useRef<htmlDivElement>()
// 错误写法
if (elRef.current) {
elRef.current.style.color = 'red'
}
return <div ref={elRef}></div>;
});DOM 发生改变的时候, 一般会有比较多后续的布局和 compose 计算去绘制新的界面.
特别是在脚本执行过程当中发生的话, 会对性能有明显影响.
脚本执行完再执行, 让浏览器自动处理(合并, 避免频繁 DOM 操作).
业务相关
- immer 对优化方案的影响
- Rex 组件当中优化的坑
- 路由相关的优化
- 性能调试
Immer 对优化方案的影响
let a = {}
let b = produce(a, draft => {
draft.b = 1
})
a === b // false如果数据不发生改变, 直接用原始数据.
(Hooks api 之后, 数据被拆散了, 可以减少 immer 的使用.)
Rex 当中优化的相关
class 组件, 高阶组件当中自动做了基础的优化.
shouldComponentUpdate(nextProps: IRexDataLayerProps, nextState: any) {
if (!shallowequal(nextProps.parentProps, this.props.parentProps)) return true;
if (!shallowequal(nextProps.computedProps, this.props.computedProps)) return true;
return false;
}Hook API, 没有中间一层组件, 直接触发当前组件更新, 存在性能问题.(还要考虑优化方案)
let contextData = useRexContext((store: IGlobalStore) => {
return {
data: store.data,
homeData: store.homeData,
};
});业务当中一般可以接受, 因为数据通常都是在更新的. 新能敏感场景需要额外考虑.
ruled-router 提供的优化
/home/plant/123/shop/456/789解析为
{
"raw": "home",
"name": "home",
"matches": true,
"restPath": ["plant", "123", "shop", "456", "789"],
"params": {},
"data": {},
"next": {
"raw": "plant/:plantId",
"name": "plant",
"matches": true,
"restPath": ["shop", "456", "789"],
"params": {
"plantId": "123"
},
"data": {
"plantId": "123"
},
"next": {
"raw": "shop/:shopId/:corner",
"name": "shop",
"matches": true,
"next": null,
"restPath": [],
"data": {
"shopId": "456",
"corner": "789"
},
"params": {
"plantId": "123",
"shopId": "456",
"corner": "789"
}
}
}
}生成对象保存起来, 路由发生变更时再重新解析. 这样对象引用一般保持一致.
来自:https://segmentfault.com/a/1190000020769066
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!