共计 3052 个字符,预计需要花费 8 分钟才能阅读完成。
内部小组分享底稿.
回顾一下 React
- class 组件的优化
- useMemo 提供的优化
- React.memo 优化
- useCallback 优化
- 避免 render 当中的 DOM 操作
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"
}
}
}
}
生成对象保存起来, 路由发生变更时再重新解析. 这样对象引用一般保持一致.
性能优调试
DevTools
https://developers.google.com…
React DevTools
https://www.debugbear.com/blo…
其他
官方推荐性能优化方案 …
https://reactjs.org/docs/opti…
实际遇到
树形组件: 隐藏子树, 定制减少更新. (个人建议看情况自己实现, 通用组件一般都不好优化).
略
useMemo
略
Dropdown 的替换, 老版本 antd 的 bug(升级 rc-select@9.0.3
).
略
https://github.com/react-comp…
需要优化
- form
- table
- …
THX. QA.