写在前面
本文适合新手入门, 如果是 react 老玩家可以 break 或者查漏补缺.
这里就说一些常见的新手误区以及优化方法:
为何需要优化
笔者一直觉的性能优化是一个累积的过程, 贯穿在你所写的每一行代码中. 不注意优化平常或许不会有什么大的问题, 但是谁也不知道哪一句会变成压死骆驼的那最后一根稻草, 造成世界爆炸.
下面是正文, 希望能对你有所帮助.
react 优化重点
react 性能优化的核心:
减少 render 次数! 推迟或者分摊 render
原因是 react 绝大部分的开销都产生在 render 时期 , 在 render 过程中会有大量的对象复制现象 , 同时会产生许多碎对象(react element) , 用于与上个对象进行对比 , 并且在每一次 render 中产生.
针对这一点特性 , 总结了一下几点常用的优化方法:
优化实例
根据 props 初始化组件
例:
- class Page extends Component (props) {
- this.state = {
- a: 1
- }
- componentDidMount() {
- this.setState({
- a: this.props.a
- })
- }
- }
很明显的错误是在生命周期内二次修改组件, 虽然符合了修改状态的时机(componentDidMount 内修改状态), 但是应该想想是否有多余的二次渲染嫌疑, 应该直接在 constructor 内初始化(也更符合 "初始化" 的字面定义).
优化:
- class Page extends Component (props) {
- constructor(props) {
- super(props)
- this.state = {
- a: props.a
- }
- }
- }
继承 Component
例:
- class Page extends Component (props) {
- constructor(props) {
- super(props)
- this.state = {
- a: props.a
- }
- }
- }
在有 props 传入同时没有使用 shouldComponentUpdate 的情况下使用 PureComponent 可以有效减少 render 次数, 其本质上是使用了组件的 shouldComponentUpdate(newProps, newState)生命周期, 在 render 之前对比 props, state(浅对比), 若无变化则不更新.
优化:
- class Page extends PureComponent (props) {
- constructor(props) {
- super(props)
- this.state = {
- a: props.a
- }
- }
- }
不使用
shouldComponentUpdate
例:
- class Page extends Component (props) {
- constructor(props) {
- super(props)
- const { a, b } = props.data;
- this.state = {
- a,
- b
- }
- }
- render() {
- const { a } = this.props.data;
- return <div>{a}</div>
- }
- }
在整个 render 中只需要 a 的情况下未使用 shouldComponentUpdate, 而组件的的更新通常会传入一个新的对象, 如果 a 值未变, 这就会造成无意义的 rerender.
优化:
- class Page extends Component (props) {
- constructor(props) {
- super(props)
- const { a, b } = props.data;
- this.state = {
- a,
- b
- }
- }
- shouldComponentUpdate(newProps, newState) {
- if (newProps.data.a !== this.props.data.a) {
- return true;
- }
- return false;
- }
- render() {
- const { a } = this.props.data;
- return <div>{a}</div>
- }
- }
复杂页面未拆分组件
例:
- class Page extends PureComponent (props) {
- constructor(props) {
- super(props)
- this.state = {
- a: 1,
- ...
- }
- }
- render() {
- const { a } = this.props.data;
- return <div>
- {...}
- </div>
- }
- }
react 的 diff 比对是以组件为单位进行的, page 也是一个大组件, 所有的数据都在一个页面, 任何一个状态的变化会引起整个页面的刷新. 合理地拆分组件, 并且结合 PureComponent 定义组件, 可以减少页面无变化部分的 render 次数, 同时 diff 比对的粒度更细.
优化:
- class Page extends PureComponent (props) {
- constructor(props) {
- super(props)
- this.state = {
- a: 1,
- b: 2
- }
- }
- render() {
- const { a } = this.props.data;
- return <div>
- <Component1 a={a} />
- <Component2 b={b} />
- ...
- </div>
- }
- }
在 componentDidMount 周期调用接口并在返回之后 setState
例:
- class Page extends PureComponent (props) {
- constructor(props) {
- this.state = {
- a: 1
- }
- }
- componentDidMount() {
- this.getData()
- }
- getData async() {
- const result = await API.getData();
- this.setState({
- a: result.a
- })
- }
- }
react 确实强调不能在 componentWillMount 中修改状态, 但是这里要考虑到的是, 调用接口是异步操作, web 端所有的异步操作都会在同步操作跑完之后再执行, 所以在有接口调用或者其他异步操作的情况下, 可以在 componentWillMount 中调用接口, 并将状态修改写在回调中, 可以加快响应时间.
优化:
- class Page extends PureComponent (props) {
- constructor(props) {
- this.state = {
- a: 1
- }
- }
- componentWillMount() {
- this.getData()
- }
- getData async() {
- const result = await API.getData();
- this.setState({
- a: result.a
- })
- }
- }
jsx 中定义函数
例:
- class Page extends PureComponent (props) {
- render() {
- return (
- <div onClick={() => {
- ...
- }}/>
- )
- }
- }
render 方法中定义的函数会在每次组件更新中重新定义, 每次定义又都会重新申请一块内存, 造成更多的内存占用, 触发 JS 的垃圾回收也会增大开销, 严重影响性能. 应该将函数存在实例上, 持久化方法和内存, 在 render 中绑定或使用.
优化:
- class Page extends PureComponent (props) {
- onClick = () => {
- ...
- }
- render() {
- return (
- <div onClick={this.onClick}/>
- )
- }
- }
jsx 中绑定 this
例:
- class Page extends PureComponent (props) {
- onClick() {
- ...
- }
- render() {
- return (
- <div onClick={this.onClick.bind(this)}/>
- )
- }
- }
虽然实例中定义存储了函数, 但是 bind 方法却会返回一个新的函数, 同样加大了内存占用和垃圾回收的开销. 可以将函数直接定义为箭头函数, 或者在 constructor 中使用 bind 改 this 指向.
优化:
- class Page extends PureComponent (props) {
- constructor(props) {
- super(props)
- this.state = {
- ...
- }
- this.onBindClick = this.onBindClick.bind(this)
- }
- onClick = () => {
- ...
- }
- onBindClick() {
- ...
- }
- render() {
- return (
- <div onClick={this.onClick}>
- <div onClick={this.onBindClick}/>
- </div>
- )
- }
- }
不使用 ref
例:
- const SLIDER_WEIGHT = 200
- class Page extends PureComponent (props) {
- constructor(props) {
- super(props)
- this.state = {
- left: 0
- }
- }
- componentDidMount() {
- this.initSwiper()
- }
- initSwiper = () => {
- this.intervalId = setInterval(() => {
- this.setState((state) => ({
- left: left + SLIDER_WEIGHT
- })))
- }, 2000)
- }
- render() {
- const { left } = this.state
- return (
- <div>
- <div style={{left: left + 'px'}}>
- ...
- </div>
- </div>
- )
- }
- }
假设这里要实现一个轮播图, 为了实现轮播效果在循环定时器中频繁修改 state, 每次更新组件状态, 会造成组件的频繁渲染. 这时候可以使用 ref 修改 dom 样式而不需要触发组件更新.
优化:
例:
- const SLIDER_WEIGHT = 200
- class Page extends PureComponent (props) {
- left = 0
- componentDidMount() {
- this.initSwiper()
- }
- initSwiper = () => {
- this.intervalId = setInterval(() => {
- this.left += SLIDER_WEIGHT
- this.refs.swiper.style.left = this.left + 'px'
- }, 2000)
- }
- render() {
- const { left } = this.state
- return (
- <div>
- <div ref="swiper">
- ...
- </div>
- </div>
- )
- }
- }
react-native 优化点
上文中几条优化方法同样适用于 react-native, 因为它们有着同样的抽象层, 但是 react-native 有一些独特的优化技巧, 提供给即将需要写 native 的同学
使用 Animated 动画, 将一部分的功能放到原生上去执行. 可以理解为 CSS3 的动画, 底层优化与更简单的实现使我快乐.
考虑能否使用更优的组件: listView,Flatlist ... 和属性: pagesize,removeClippedSubviews... , 同样是底层优化带来的便利.
使用 Interactionmanager 将一些耗时较长的工作安排到所有的交互或者动画完成之后再进行可以分摊开销 (react-native 的 JS 与原生代码通信带来的性能瓶颈),Interactionmanager.runAfterInteractions() 中回调.
以上就是我总结的几个 react/react-native 性能优化点, 若你有更多方案, 请于下方留言, 我会及时补充, 最后祝大家写代码永无 bug, 性能永远最优.
- //
- // _ooOoo_
- // o8888888o
- // 88"."88
- // (| -_- |)
- // O\ = /O
- // ____/`---'\____
- // .' \\| |// `.
- // / \\||| : |||// \
- // / _||||| -:- |||||- \
- // | | \\\ - /// | |
- // | \_| ''\---/'' | |
- // \ .-\__ `-` ___/-. /
- // ___`. .' /--.--\ `. . __
- // .""'<`.___\_<|>_/___.'>'"".
- // | | : `- \`.;`\ _ /`;.`/ - ` : | |
- // \ \ `-. \_ __\ /__ _/ .-` //
- // ======`-.____`-.___\_____/___.-`____.-'======
- // `=---='
- // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- // 佛祖保佑 永无 BUG
-- The end
来源: https://juejin.im/post/5c12040de51d4556400a9cce