最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • react源码解析一

    正文概述 掘金(幻丶城)   2021-03-24   739

    0 前言

    本React系列文章是基于React最新的代码仓库进行分析,也许和老版本有些许不同,大佬们可酌情观看。

    1 Component && Pure Component

    Component和PureComponent作为所有学习React开发的人的入口,其对我们来说还是比较重要的,所以本篇文章选择从此处作为切入进行分析。

    源码位于:ReactBaseClasses.js文件。

    该文件更多的类似于抽象类的作用,对很多进行定义,然后提供给外部调用,真正的细节实现并不在此处。

    1.1 Component

    组件更新的基类装饰器,主要工作就是初始化参数。

    具体代码如下:

    function Component(props, context, updater) {
      this.props = props;
      this.context = context;
      this.refs = emptyObject;
      this.updater = updater || ReactNoopUpdateQueue;
    }
    
    

    主要是对props,context,updater这三个参数进行初始化。

    其中最重要的就是初始化装饰器,因为react所有的操作都在存在updater的前提下继续下去的。我们在常规新建的时候是不会传入updater参数的,所以一般都是初始化的默认的ReactNoopUpdateQueue

    refs默认设置为emptyObject的空对象。

    1.2 PureComponent

    唯一的区别就是在PureComponent的prototype上带有了类型标示,会判断isPureReactComponent。

    pureComponentPrototype.isPureReactComponent = true;
    

    TIPS:React中对比一个ClassComponent是否需要更新,只有两个地方。一是看有没有shouldComponentUpdate方法,二就是这里的PureComponent判断。

    关于updater将在专门的文章中进行叙述。

    2 React Context

    相关代码如下所示,已经删除了dev有关的逻辑。

    export function createContext<T>(
      defaultValue: T,
      calculateChangedBits: ?(a: T, b: T) => number,
    ): ReactContext<T> {
      if (calculateChangedBits === undefined) {
        calculateChangedBits = null;
      }
    
      const context: ReactContext<T> = {
        $$typeof: REACT_CONTEXT_TYPE,
        _calculateChangedBits: calculateChangedBits,
        _currentValue: defaultValue,
        _currentValue2: defaultValue,
        _threadCount: 0,
        Provider: (null: any),
        Consumer: (null: any),
      };
    
      context.Provider = {
        $$typeof: REACT_PROVIDER_TYPE,
        _context: context,
      };
      context.Consumer = context;
      return context;
    }
    
    

    _currentValue:作为支持多个并发渲染器的解决方法,我们将一些渲染器归类为主要渲染器,而将其他渲染器归类为次要渲染器。我们预计最多只有两个并发渲染器:Reaction Native(主要)和Fabric(次要);Reaction DOM(主要)和Reaction ART(次要)。辅助渲染器将其上下文值存储在单独的字段中。

    _threadCount:用于记录当前渲染器中支持并发渲染器的数量。

    Provider,Consumer:这些是循环调用过的。

    typeof:因为JSON不支持Symbol类型。所以即使服务器存在用JSON作为文本返回安全漏洞,JSON里也不包含Symbol.for(react.element)React会检测element.typeof:因为JSON不支持Symbol 类型。所以即使服务器存在用JSON作为文本返回安全漏洞,JSON 里也不包含Symbol.for('react.element')。React 会检测 element.typeof:因为JSON不支持Symbol类型。所以即使服务器存在用JSON作为文本返回安全漏洞,JSON里也不包含Symbol.for(′react.element′)。React会检测element.typeof,如果元素丢失或者无效,会拒绝处理该元素。特意用 Symbol.for() 的好处是 Symbols 通用于 iframes 和 workers 等环境中。因此无论在多奇怪的条件下,这方案也不会影响到应用不同部分传递可信的元素。同样,即使页面上有很多个 React 副本,它们也 「接受」 有效的 $$typeof值。

    3 React.createRef

    这是一个具有单个可变值的不可变对象。实现原理也超级简单。

    export function createRef(): RefObject {
      const refObject = {
        current: null,
      };
      return refObject;
    }
    

    就创建一个refOject,然后return即可。

    用法通过dom的ref实现绑定:

    class App extends React.Component{
      constructor() {
        this.ref = React.createRef()
      }
      render() {
        return <div ref={this.ref} />
        // or
        return <div ref={(node) => this.funRef = node} />
      }
    }
    

    4 createElement

    4.1 总体概述

    创建一个ReactElement元素的接口。

    export function createElement(type, config, children) {}
    

    type:表示要创建的节点的类型,

    config:表示创建节点的一些属性,比如id,class之类的。

    children:创建节点中包含的内容,这个children可以又是一个ReactElement。

    4.2 分段解析

        if (hasValidRef(config)) {
          ref = config.ref;
    			// dev环境下的类型警告
          if (__DEV__) {
            warnIfStringRefCannotBeAutoConverted(config);
          }
        }
        if (hasValidKey(config)) {
          key = '' + config.key;
        }
    
        self = config.__self === undefined ? null : config.__self;
        source = config.__source === undefined ? null : config.__source;
        // Remaining properties are added to a new props object
        for (propName in config) {
          if (
            hasOwnProperty.call(config, propName) &&
            !RESERVED_PROPS.hasOwnProperty(propName)
          ) {
            props[propName] = config[propName];
          }
        }
    

    解析:

    大概就是对传递过来的config对象进行解析。


    如果config不为空就开始进行解析,首先解析的就是ref属性。

    如果存在Key,同样把key绑定到要生成的dom上去。

    将传递进来的这些config,赋值到对应的props上去。

      const childrenLength = arguments.length - 2;
      if (childrenLength === 1) {
        props.children = children;
      } else if (childrenLength > 1) {
        const childArray = Array(childrenLength);
        for (let i = 0; i < childrenLength; i++) {
          childArray[i] = arguments[i + 2];
        }
        if (__DEV__) {
          if (Object.freeze) {
            Object.freeze(childArray);
          }
        }
        props.children = childArray;
      }
    

    传递过来的children对象可以存在多个参数,上述代码的意义就是将这些参数赋值到this.props.children上去。

      if (type && type.defaultProps) {
        const defaultProps = type.defaultProps;
        for (propName in defaultProps) {
          if (props[propName] === undefined) {
            props[propName] = defaultProps[propName];
          }
        }
      
    

    如果传入的当前JSX.Element存在默认props,再把默认的props赋值到当前这个已经创建好的Element对象中去。

    最后调用ReactElement完成元素创建。

    5 cloneElement

    cloneElement的步骤和createElement的步骤十分相似,但是也存在一些不同的。

    比如说,在clone之前会对传入的element进行类型校验,如果不符合条件将会直接报出错误。

      invariant(
        !(element === null || element === undefined),
        'React.cloneElement(...): The argument must be a React element, but you passed %s.',
        element,
      );
    

    在创建元素之前首先会把原props进行复制操作。

    const props = Object.assign({}, element.props);
    

    下面的操作就几乎是相同的了,比如说解析config,解析默认的props,解析children。

    6 createFactory && isValidElement

    createFactory是用来创建专门用来创建某一类ReactElement的工厂的,我们几乎不会涉及到。

    isValidElement顾名思义就是用来验证是否是一个ReactElement的,基本也用不太到。

    7 ReactElement

    前面做了那么多铺垫,其实最终的目的就是突出创建ReactElement的方法。

    const ReactElement = function(type, key, ref, self, source, owner, props) {
      const element = {
        $$typeof: REACT_ELEMENT_TYPE,
        type: type,
        key: key,
        ref: ref,
        props: props,
        _owner: owner,
      };
      return element;
    };
    
    

    在前端一堆操作完成之后,我们此时只需要将已经准备好的参数传入进行创建即可。

    其中$$typeof代表的该对象作为react element的唯一表示,如果没有就无法被react对象识别。

    _owner:记录负责创建此元素的组件(这里比较重要,在后面会做进一步分析)。

    type,key,ref,props等由createElement处理完成之后传入,具体加工过程可以看关于createElement的源码。


    起源地下载网 » react源码解析一

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元