react/react-native性能优化

栏目: IOS · Android · 发布时间: 5年前

内容简介:笔者一直觉的性能优化是一个累积的过程,贯穿在你所写的每一行代码中。不注意优化平常或许不会有什么大的问题,但是谁也不知道哪一句会变成压死骆驼的那最后一根稻草,造成世界爆炸:boom:。下面是正文,希望能对你有所帮助。react性能优化的核心:

笔者一直觉的性能优化是一个累积的过程,贯穿在你所写的每一行代码中。不注意优化平常或许不会有什么大的问题,但是谁也不知道哪一句会变成压死骆驼的那最后一根稻草,造成世界爆炸:boom:。

下面是正文,希望能对你有所帮助。

react优化重点

react性能优化的核心:

减少render次数!推迟或者分摊render

原因是react绝大部分的开销都产生在render时期 , 在render过程中会有大量的对象复制现象 , 同时会产生许多碎对象(react element) , 用于与上个对象进行对比 , 并且在每一次render中产生。

针对这一点特性 , 总结了一下几点常用的优化方法:

优化实例

  1. 根据 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
            }
        }
    }
复制代码
  1. 继承 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
            }
        }
    }
复制代码
  1. 不使用 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>
        }
    }
复制代码
  1. 复杂页面未拆分组件

例:

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>
        }
    }
复制代码
  1. 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
            })
        }
    }
复制代码
  1. 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}/>
            )
        }
    }
复制代码
  1. 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>
            )
        }
    }
复制代码
  1. 不使用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的同学:smirk:

  1. 使用 Animated动画,将一部分的功能放到原生上去执行。可以理解为css3的动画,底层优化与更简单的实现使我快乐。

  2. 考虑能否使用更优的组件:listView、Flatlist ... 和属性:pagesize、removeClippedSubviews... ,同样是底层优化带来的便利。

  3. 使用Interactionmanager将一些耗时较长的工作安排到所有的交互或者动画完成之后再进行可以分摊开销(react-native的js与原生代码通信带来的性能瓶颈),Interactionmanager.runAfterInteractions()中回调。

以上就是我总结的几个react/react-native性能优化点, 若你有更多方案,请于下方留言,我会及时补充,最后祝大家写代码永无bug,性能永远最优。

//
//                            _ooOoo_
//                           o8888888o
//                           88" . "88
//                           (| -_- |)
//                           O\  =  /O
//                        ____/`---'\____
//                      .'  \\|     |//  `.
//                     /  \\|||  :  |||//  \
//                    /  _||||| -:- |||||-  \
//                    |   | \\\  -  /// |   |
//                    | \_|  ''\---/''  |   |
//                    \  .-\__  `-`  ___/-. /
//                  ___`. .'  /--.--\  `. . __
//               ."" '<  `.___\_<|>_/___.'  >'"".
//              | | :  `- \`.;`\ _ /`;.`/ - ` : | |
//              \  \ `-.   \_ __\ /__ _/   .-` /  /
//         ======`-.____`-.___\_____/___.-`____.-'======
//                            `=---='
//        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//                      佛祖保佑       永无BUG
复制代码

-- The end


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

刷屏

刷屏

[美] 凯文•阿洛卡 / 侯奕茜、何语涵 / 中信出版社 / 2018-10-1 / 68.00

1. YouTube流行趋势经理,解密如何打造爆款视频 在视频时代,制造互动,才能创造潮流 用户不再是被动的观众,而是主动的传播者 2. 《刷屏》以行内人视角解读: 病毒视频 粉丝经济 网红产业 平台如何为内容创作者赋能 3. 你是否常常被病毒视频刷屏?你是否觉得很多网红火爆到“无法用常理解释”? 视频时代已经到来,我们每天观看网络......一起来看看 《刷屏》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码