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