React性能优化之Context

先来讲说Context的做用

react之类的mvvm框架有个痛点想必你们都遇到过,那就是祖孙组件之间的通讯html

如今组件之间的通讯规范是:前端

  • 父 -> 子:属性绑定props
  • 子 -> 父:事件广播(vue之类的emit)react那仍是调用父类的props[fn]

仅仅是父子两个组件之间这没有问题
但是祖孙组件之间呢?vue

react中有个虫洞的概念,至关于直通车,组件【祖先】=> 组件【孙子】,而不须要中间的组件来代理propsreact

下面就先介绍下高端的虫洞(直通车)

直接show code数组

三个组件App.js,Son.js,GrandSon.js,下面介绍的是用虫洞从App props直通GrandSon,而不须要Son来作代理属性bash

// App.js的代码

import React from 'react';
import Son from './son';
import PropTypes from 'prop-types';

class App extends React.Component{
    constructor(){
		super();
		
        this.state = {
            grandSonDesc: 'i am grandSonDesc'
        };
    }

    getChildContext(){
        return {
            grandSonDesc: this.state.grandSonDesc
        }
    }
    
    render(){
        return <Son />
    }
}

App.childContextTypes = {
    grandSonDesc: PropTypes.string
}

export default App;
复制代码
// Son.js

import React from 'react';
import GrandSon from './grandSon';

const Son = () => {
    return <GrandSon />
}

export default Son;
复制代码
// GrandSon.js

import React from 'react';
import PropTypes from 'prop-types';

class GrandSon extends React.Component{
    render(){
        return <div>{this.context.grandSonDesc}</div>
    }
}

GrandSon.contextTypes = {
    grandSonDesc: PropTypes.string
};

export default GrandSon;
复制代码

至此就实现了虫洞了,但是须要注意两点:框架

  • GrrandSon不能为纯函数组件,这样是不能注入的
  • App组件和GrandSon组件须要定义类属性contextTypes,App组件须要定义getChildContext

那么问题来了,为啥要声明contextTypes? 缘由是,GrandSon组件除了App这个爷爷组件外,还有Son这个爸爸组件。那么grandSonDesc是从哪一个组件继承而来呢,contextTypes就是作这个标识做用,并且多个父组件的context会作merge,也是根据定义contextTypes来的mvvm

好的,到这里感受很爽,虫洞直通车传输props,可是我要说的是,这个实如今react17将被废弃!!!ide

为什么老版本虫洞将被废弃?

若是一个属性将被废弃,多半是跟它的性能有关了,能够试想下,虫洞传输props,其实son组件没有任何更新,那么当grandSonDesc变化时,Son组件会不会被更新?直接show code吧函数

// App.js

import React from 'react';
import Son from './son';
import PropTypes from 'prop-types';

class App extends React.Component{
    constructor(){
		super();
		
        this.state = {
            grandSonDesc: 'i am grandSonDesc'
        };
	}
	
	componentDidMount(){
		setTimeout(() => {
			this.setState({
				grandSonDesc: 'i am grandSonDesc啊啊啊啊啊啊啊啊'
			});
		}, 3000);
	}

    getChildContext(){
        return {
            grandSonDesc: this.state.grandSonDesc
        }
    }
    
    render(){
        return <Son />
    }
}

App.childContextTypes = {
    grandSonDesc: PropTypes.string
}

export default App;
复制代码
// Son.js

import React from 'react';
import GrandSon from './grandSon';

class Son extends React.Component{
    componentDidUpdate(){
        console.log('son updated!');
    }

    render(){
        return <GrandSon />
    }
}

export default Son;
复制代码
// GrandSon.js

import React from 'react';
import PropTypes from 'prop-types';

class GrandSon extends React.Component{
    componentDidUpdate(){
        console.log('grandson updated!');
    }

    render(){
        return <div>{this.context.grandSonDesc}</div>
    }
}

GrandSon.contextTypes = {
    grandSonDesc: PropTypes.string
};

export default GrandSon;
复制代码

在版本的代码中,App.js在三秒钟以后去更新grandSonDesc,而后在Son和GrandSon组件中加上componentDidUpdate更新以后的log,来看一下控制台输出

三秒以后GrandSon的显示变成了i am grandSonDesc啊啊啊啊啊啊啊啊,而控制台输出son updated

这说明Son组件也被更新了,那么虫洞若是中间层组件比较多的话,岂不是很浪费性能吗?

二代虫洞

那么既然性能很差,世界顶级前端团队就要开始优化了,那么就有了二代虫洞,直接上代码介绍下二代虫洞吧,仍是原来的配方App+Son+GrandSon,惟一多的是一个context的配置文件

// App.js

import React from 'react';
import Son from './son';
import Context from './context';
import PropTypes from 'prop-types';

class App extends React.Component {
    constructor(){
        super();

        this.state = {
            grandSonDesc: 'i am grandSon'
        };
    }

	render(){
		return (
			<div className="App">
				<Context.Provider value={this.state}>
					<Son />
				</Context.Provider>
			</div>
		);
	}
}

App.contextType = Context;

export default App;
复制代码
// Son.js

import React, { PureComponent } from 'react';
import GrandSon from './grandSon';
import PropTypes from 'prop-types';

class Son extends PureComponent{
    render(){
        return <GrandSon />
    }
}

export default Son;
复制代码
// GrandSon.js

import React, { PureComponent } from 'react';
import Context from './context';

class GrandSon extends PureComponent{
    render(){
        return <div>
            <Context.Consumer>
                {value => value.grandSonDesc}
            </Context.Consumer>
        </div>
    }
}

export default GrandSon
复制代码
// context.js

import { createContext } from 'react';

let Context = createContext();

export default Context;
复制代码

代码敲完了,能够start看看效果了,能够发现照样跑起来了

那么问题来了,你说一代虫洞性能差,凭什么嘛?

// App.js

import React from 'react';
import Son from './son';
import Context from './context';
import PropTypes from 'prop-types';

class App extends React.Component {
    constructor(){
        super();

        this.state = {
            grandSonDesc: 'i am grandSon'
        };
    }

    componentDidMount(){
        setTimeout(() => {
            this.setState({
                grandSonDesc: 'i am grandSon啊啊啊啊啊啊啊啊啊啊'
            });
        }, 3000);
    }

	render(){
		return (
			<div className="App">
				<Context.Provider value={{grandSonDesc: this.state.grandSonDesc}}>
					<Son />
				</Context.Provider>
			</div>
		);
	}
}

App.contextType = Context;

export default App;

复制代码
// Son.js

import React, { PureComponent } from 'react';
import GrandSon from './grandSon';
import PropTypes from 'prop-types';

class Son extends PureComponent{
    componentDidUpdate(){
        console.log('son updated');
    }

    render(){
        return <GrandSon />
    }
}

export default Son;
复制代码
// GrandSon.js

import React, { PureComponent } from 'react';
import Context from './context';

class GrandSon extends PureComponent{
    componentDidUpdate(){
        console.log('grandson updated');
    }

    render(){
        return <div>
            <Context.Consumer>
                {value => value.grandSonDesc}
            </Context.Consumer>
        </div>
    }
}

export default GrandSon
复制代码

能够发现,3秒以后grandSonDesc变了,两个子组件的componentDidUpdated都没进

这就是两种虫洞的实现方式,到这里差很少讲完了,关于更多新版context的用法能够参考官网 reactjs.org/docs/contex…