React createElement方法使用原理分析介绍

目录
  • 摘要
  • 1.创建方法
  • 2.处理type
  • 3.处理config
  • 4.处理children
  • 5.对比真正的React.createElement源码

摘要

在上一篇说过,React创建元素有两种方式:

第一种是通过JSX方式创建,第二种是通过React.createElement方法创建。但是通过JSX创建React元素的方式,最终也会经过babel进行转换,再用React.createElement进行元素创建。

而这一篇文章,主要是讲一下React.createElement是如何创建React元素的。

1.创建方法

为了了解React.createElement这个方法,我们自己手动去实现一个简易版的。

OK,首先我们通过React.createElement方法创建一个React元素,并且打印出来:

    const b = React.createElement('div',{data: '1234'},'oneTwo')
    console.log(b);

打印出来的结果为:

所以,如果我们要实现出来React.createElement这个方法,定义的时候我们可以这么写:

function createElement(type, config, children){
  let props = {}
  let myType = ''
  let key = ''
  let ref = ''
  return {
    $$typeOf: 'react.element',
    type: myType,
    props,
    key,
    ref
  }
}

这里我们不管**$$typeOf**这个变量,只考虑其他的变量,最终返回的对象应该是这个数据结构。

2.处理type

好了,方法的定义已经写完了。针对于传进来的这三个参数,我们一个一个看。

第一个参数type,它代表的是创建React元素的类型。

这里面可以是传统的HTML元素,例如div,h1,span等标签。

也可以是一个React组件(注意这里哦)。

而React创建组件又有两种方式,所以type的值,有三种数据类型:

(1)字符串:例如"div",“h1”,“span”,"p"等标签

(2)函数:通过函数的方式创建React组件

(3)类:通过class的方式创建React组件

而这个时候就有一个问题!

class Demo {
}
typeof Demo

上面的值应该为什么呢?答案是function,所以在这里我们只需要考虑type为string和function两种类型即可。

所以我们可以写一个判断类型的方法:

function isValidElementType(type){
  if(typeof type === 'string' || typeof type === 'function'){
    return true;
  }
  return false;
}

在我们的主方法里面引用:

function createElement(type, config, children){
  let props = {}
  let myType = ''
  let key = ''
  let ref = ''
  if(isValidElementType(type)){
    myType = type;
  }
  return {
    $$typeOf: 'react.element',
    type: myType,
    props,
    key,
    ref
  }
}

3.处理config

对于React.createElement方法的第二个参数,接收看一个对象。而对象下所有的属性,都会被放在props里面。

这句话对不对呢?其实也不是很对,是有特例的,如果在这个对象下,有key或者ref这两个属性。它是不会被放在props里面的,而是直接赋值给key和ref

Ok,有了上面的话,我们就可以对config进行处理了:

  if(config != null){
    if(config.ref){
      ref = config.ref
    }
    if(config.key){
      key = config.key
    }
    for(let propName in config){
      if(!(['ref','key'].includes(propName))){
        props[propName] = config[propName]
      }
    }
  }

我们只需要把config的key和ref分别给到我们返回对象里面的key和ref。再便利一下config,拿出来的属性和值把key和ref排除。最终我们的config属性就处理好了。

4.处理children

最后一步就是处理children属性了。而React.createElement方法的第三个参数,也可以是第四个参数(就是后面的所有参数)。都可以为字符串,或者是React元素。

这里的React元素我们不管它是通过JSX创建的,还是通过React.createElement方法创建的都可以

而参数的情况分两种:

第一种是只有三个参数,也就是children为一个值。这个时候props里面的children就是该字符串。

第二种是参数大于三个,这个时候,props里面的children是一个数组,数组里的元素就是后面的所有参数。

OK,有了上面的基础,就可以对children进行处理了:

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

这里通过arguments来判断参数的个数,进而确定分支条件。

然后再根据情况,确定props里的children。

最后再贴上完整的createElement方法(简易版):

function createElement(type, config, children){
  let props = {}
  let myType = ''
  let key = ''
  let ref = ''
  if(isValidElementType(type)){
    myType = type;
  }
  if(config != null){
    if(config.ref){
      ref = config.ref
    }
    if(config.key){
      key = config.key
    }
    for(let propName in config){
      if(!(['ref','key'].includes(propName))){
        props[propName] = config[propName]
      }
    }
  }
  let childrenLength = arguments.length - 2
  if(childrenLength === 1){
    props.children = children
  }else if(childrenLength > 1){
    let children = new Array(childrenLength)
    for(let i = 0;i<childrenLength;i++){
      children[i] = arguments[i+2]
    }
    props.children = children
  }
  return {
    $$typeOf: 'react.element',
    type: myType,
    props,
    key,
    ref
  }
}

5.对比真正的React.createElement源码

OK,上面只是实现了一个比较简单的React.createElement方法,但是懂了其中的过程,我们就可以看一下真正的React.createElement源码:

isValidElementType方法

function isValidElementType(type) {
  if (typeof type === 'string' || typeof type === 'function') {
    return true;
  } // Note: typeof might be other than 'symbol' or 'number' (e.g. if it's a polyfill).
  if (type === REACT_FRAGMENT_TYPE || type === REACT_PROFILER_TYPE || enableDebugTracing  || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || enableLegacyHidden  || type === REACT_OFFSCREEN_TYPE || enableScopeAPI  || enableCacheElement  || enableTransitionTracing ) {
    return true;
  }
  if (typeof type === 'object' && type !== null) {
    if (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object
    // types supported by any Flight configuration anywhere since
    // we don't know which Flight build this will end up being used
    // with.
    type.$$typeof === REACT_MODULE_REFERENCE || type.getModuleId !== undefined) {
      return true;
    }
  }
  return false;
}

这里面也主要就是判断type的类型,不过判断情况多了几种React自带的元素类型。

createElement方法

function createElement(type, config, children) {
  var propName; // Reserved names are extracted
  var props = {};
  var key = null;
  var ref = null;
  var self = null;
  var source = null;
  if (config != null) {
    if (hasValidRef(config)) {
      ref = config.ref;
      {
        warnIfStringRefCannotBeAutoConverted(config);
      }
    }
    if (hasValidKey(config)) {
      {
        checkKeyStringCoercion(config.key);
      }
      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];
      }
    }
  } // Children can be more than one argument, and those are transferred onto
  // the newly allocated props object.
  var childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    props.children = children;
  } else if (childrenLength > 1) {
    var childArray = Array(childrenLength);
    for (var i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }

    {
      if (Object.freeze) {
        Object.freeze(childArray);
      }
    }
    props.children = childArray;
  } // Resolve default props
  if (type && type.defaultProps) {
    var defaultProps = type.defaultProps;

    for (propName in defaultProps) {
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }
  {
    if (key || ref) {
      var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type;
      if (key) {
        defineKeyPropWarningGetter(props, displayName);
      }
      if (ref) {
        defineRefPropWarningGetter(props, displayName);
      }
    }
  }
  return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
};

这个方法主要是对config和children进行处理。

其余的部分就不粘过来了,对源码感兴趣的可以自己打断点尝试一哈!

到此这篇关于React createElement方法使用原理分析介绍的文章就介绍到这了,更多相关React createElement内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • React的createElement和render手写实现示例

    目录 TL;DR 科普概念 准备工作 实现 createElement 实现 render 测试 TL;DR 本文的目标是,手写实现createElement和render React.createElement实现的本质就是整合参数变成对象,这个对象就是react元素 ReactDOM.render实现的本质就是根据react元素(对象)创建真实元素及其属性和子元素 科普概念 JSX 语法 - 就是类似 html 的写法<h1>颜酱<span>最酷</span><

  • React createElement方法使用原理分析介绍

    目录 摘要 1.创建方法 2.处理type 3.处理config 4.处理children 5.对比真正的React.createElement源码 摘要 在上一篇说过,React创建元素有两种方式: 第一种是通过JSX方式创建,第二种是通过React.createElement方法创建.但是通过JSX创建React元素的方式,最终也会经过babel进行转换,再用React.createElement进行元素创建. 而这一篇文章,主要是讲一下React.createElement是如何创建Rea

  • React this.setState方法使用原理分析介绍

    目录 摘要 1.异步的setState 2.多个setState方法 3.手动实现mySetState 摘要 这一篇文章,主要是简单的实现一下this.setState方法,为了实现该方法,就要知道this.setState方法具有什么特点. 首先在React组件中,我们先定义一个state和setState方法: myState = { value: 0 } mySetState = ( changeState ) =>{ this.setState( this.myState ) } 这里可

  • js中数组排序sort方法的原理分析

    本文实例分析了js中数组排序sort方法的原理.分享给大家供大家参考.具体分析如下: 最近在百度的项目中要用到对数组进行排序,当然一开始自然想到了数组的sort方法,这方法应用非常简单,大致如下: 复制代码 代码如下: window.onload=function(){         var arr=[2,55,55,1,75,3,9,35,70,166,432,678,32,98];         var arr2=["George","John","

  • Android延迟实现的几种解决方法及原理分析

    前言 在Android开发中我们可能会有延时执行某个操作的需求,例如我们启动应用的时候,一开始呈现的是一个引导页面,过了两三秒后,会自动跳转到主界面.这就是一个延时操作. 而写这篇文章的目的,是看到群里有人在实现延迟的时候,用如下的第四种方法,个人感觉有点不妥,为了防止更多的人有这种想法,所以自己抽空深入分析,就分析的结果,写下此文,希望对部分人有启示作用. 1.实现延迟的几种方法? 答: 1.java.util.Timer类的: public void schedule(TimerTask t

  • Spring Validation方法实现原理分析

    最近要做动态数据的提交处理,即需要分析提交数据字段定义信息后才能明确对应的具体字段类型,进而做数据类型转换和字段有效性校验,然后做业务处理后提交数据库,自己开发一套校验逻辑的话周期太长,因此分析了Spring Validation的实现原理,复用了其底层花样繁多的Validator,在此将分析Spring Validation原理的过程记录下,不深入细节 如何使用Spring Validation Spring Bean初始化时校验Bean是否符合JSR-303规范 1.手动添加BeanVali

  • 浅谈vue,angular,react数据双向绑定原理分析

    传统做法 前端维护状态,手动操作DOM更新视图.前端框架对服务器数据通过模版进行渲染.当用户产生了一个动作之后,我们通过document.getElementBy... 手动进行DOM更新. 框架帮忙分离数据和视图,后续状态更新需要手动操作DOM,因为框架只管首次渲染,不追踪状态监听变化. 双向数据绑定 当我们在前端开发中采用MV*的模式时,M - model,指的是模型,也就是数据,V - view,指的是视图,也就是页面展现的部分.通常,我们需要编写代码,将从服务器获取的数据进行"渲染&qu

  • jQuery的ready方法实现原理分析

    jQuery中的ready方法实现了当页面加载完成后才执行的效果,但他并不是window.onload或者doucment.onload的封装,而是使用 标准W3C浏览器DOM隐藏api和IE浏览器缺陷来完成的,首先,我们来看jQuery的代码 DOMContentLoaded = function() { //取消事件监听,执行ready方法 if ( document.addEventListener ) { document.removeEventListener( "DOMContent

  • React状态管理Redux原理与介绍

    目录 一.Redux 二.Redux的组成 2.1 store 2.2 state 2.3 action 2.4 reducer 三.三大原则 3.1 单一数据源 3.2 State只读 3.3 使用纯函数修改State 四.基于Redux的TodoList 五.react-redux 5.1 connect方法 5.2 Provider组件 一.Redux 和vuex一样,redux的出现是为了管理web应用的公共状态. 这些 state 可能包括服务器响应.缓存数据.本地生成尚未持久化到服务

  • Android增量升级的方法和原理详细介绍

    总结:我们使用delta编码算法减少Android应用升级程序的大小.我们通过bsdiff和bspatch工具在android上实现delta编码算法.服务器软件和android应用已经部署.当前,我们能够减少Android升级流量的50%,如果大量的部署,这将会减少网络的压力. 升级机制:我们打算采用delta编码的patch升级Android应用.新的升级机制可以描述如下: 1.  在服务器上生成一个patch.2.  下载patch到手机中.3.  通过补丁获取一个已安装应用的新的安装ap

  • 浅谈web上存漏洞及原理分析、防范方法(文件名检测漏洞)

    我们通过前篇:<浅谈web上存漏洞及原理分析.防范方法(安全文件上存方法)>,已经知道后端获取服务器变量,很多来自客户端传入的.跟普通的get,post没有什么不同.下面我们看看,常见出现漏洞代码.1.检测文件类型,并且用用户上存文件名保存 复制代码 代码如下: if(isset($_FILES['img'])){    $file = save_file($_FILES['img']); if($file===false) exit('上存失败!'); echo "上存成功!&qu

随机推荐