react/react-native性能优化

写在前面

本文适合新手入门,若是是react老玩家能够break或者查漏补缺。css


这里就说一些常见的新手误区以及优化方法:react

为什么须要优化

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


下面是正文,但愿能对你有所帮助。web

react优化重点

react性能优化的核心:react-native

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

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

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

优化实例

  1. 根据props初始化组件

例:bash

class Page extends Component (props) {
        state = {
            a: 1
        }
        componentDidMount() {
            this.setState({
                a: this.props.a
            })
        }
    }
复制代码

很明显的错误是在生命周期内二次修改组件,虽然符合了修改状态的时机(componentDidMount内修改状态),可是应该想一想是否有多余的二次渲染嫌疑,应该直接在constructor内初始化(也更符合“初始化”的字面定义)。dom

优化:异步

class Page extends Component (props) {
        constructor(props) {
            super(props)
            this.state = {
                a: props.a
            }
        }
    }
复制代码
  1. 继承PureComponent

例:

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( 经评论区的朋友提醒,由于componentWillMount react16.3已经标记为不安全的生命周期了,在17的时候会下掉,因此再也不推荐这一条,感谢@Flasco 的提醒)

例:

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>
            )
        }
    }
复制代码
  1. 使用Fragment

例:

() => <div> <div>content1</div> <div>content2</div> </div>
复制代码

当你写一个单独的组件时,若是发现最外层元素实质上是多余的,可是子元素又有多个标签并列不能直接暴露时,你应该使用Fragment,一个空的jsx标签。这样在渲染的时候会少一个标签,而且使咱们的代码看起来更加简洁。

优化:

例:

() => <> <div>content1</div> <div>content2</div> </> 复制代码

react-native优化点

上文中几条优化方法一样适用于react-native,由于它们有着一样的抽象层,可是react-native有一些独特的优化技巧,提供给即将须要写native的同窗😏

  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

相关文章
相关标签/搜索