【React源码 - ReactDom.render发生了什么】

在React开发中,在入口文件我们都会执行ReactDom.render来讲整个应用挂载在主document中,那其中发生了什么,React是如何讲我们写的JSX代码,一步一步更新Fiber进而挂载渲染的呢。本文主要是基于react@17.0.2的源码以及自己的理解来简单介绍一下这里面发生了什么。

流程总览

如下图所示,总体流程就是开发编写JSX代码之后会交由Babel来进行转移,并调用React.createElement转换为reactElement,然后生成虚拟DOM,通过render函数进一步更新filber渲染在页面上。所以本文主要是从源码角度来分析createElement和ReactElement函数的内部实现逻辑。
在这里插入图片描述

createElement

函数文件路径:packages/react/src/ReactElement.js

该函数接收三个参数(type、config、children),然后创建并返回一个给定类型的新ReactElement。主要做了一下一些事:

  1. 从config参数中拆分props属性和特殊属性
  2. 将子元素挂载到props.children上
  3. 给未设置的props属性设置默认值defaultProps
  4. 创建并返回一个ReactElement

由于开发环境下的代码主要是一些log或者warn的提示信息,所以这里不再分析,下面请看源码解析:
第一步:从config参数中拆分props属性和特殊属性

/*** 定义props的属性名,用于后面两次for遍历* 一次是遍历config配置,并将props属性拆出来* 一次是遍历defaultProps默认属性,给未赋值的属性给默认值*/let propName;// 保留props属性配置,不包括key、ref、self、sourceconst props = {};// config中的react内置的特殊属性let key = null;let ref = null;let self = null;let source = null;// 处理config参数,主要就是拆分props和特殊属性if (config != null) {// 将ref赋予有效值,用来保存当前的dom元素if (hasValidRef(config)) {ref = config.ref;// 开发环境提示warn信息if (__DEV__) {// ref属性设置成字符串的一个提示,后续版本会删除该用法warnIfStringRefCannotBeAutoConverted(config);}}// 以字符串的形式保留key属性if (hasValidKey(config)) {key = '' + config.key;}/*** 如果没有self、source属性则设置为null* 个人猜测可能是由于undefined可以被重写,所以这里改为null* 而且null比undefined更符合在这里的语义*  */self = config.__self === undefined ? null : config.__self;source = config.__source === undefined ? null : config.__source;// 去除key、ref、self、source特殊属性并赋值给props保存// RESERVED_PROPS定义的包含key、ref、self、source的对象for (propName in config) {if (hasOwnProperty.call(config, propName) &&!RESERVED_PROPS.hasOwnProperty(propName)) {props[propName] = config[propName];}}}

第二步:将子元素挂载到props.children上

/*** arguments是参数合集数组,包含上面的三个行参type、config、children...* 这里arguments.length - 2就是获取后面所有children的长度*/const childrenLength = arguments.length - 2;// 如果只有一个子元素,则直接赋值if (childrenLength === 1) {props.children = children;} else if (childrenLength > 1) {// 如果有多个,则通过数组来保存,并存储在props.children中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;}

第三步:给未设置的props属性设置默认值defaultProps

// 遍历type元素的默认类型,给未设置的属性赋予默认值if (type && type.defaultProps) {const defaultProps = type.defaultProps;for (propName in defaultProps) {if (props[propName] === undefined) {props[propName] = defaultProps[propName];}}}

第四步:创建并返回一个ReactElement
通过将上面解析点属性配置,传递给ReactElement函数,然后返回一个ReactElement类型的DOM元素

return ReactElement(type,key,ref,self,source,ReactCurrentOwner.current,props,);

ReactElement

函数文件路径:packages/react/src/ReactElement.js
该函数使用工厂模式创建一个新的React Element,不在使用类的模式,所以不能通过new的方式来获取实例,该函数接收7个参数,并返回一个react element元素,不能用instanceof来判断该Element是不是React Element,只能通过Symbol.for(‘react.element’)来判断是否是React Element。react中是通过isValidElement方法判断元素是否为空对象并且$$typeof 是否为 REACT_ELEMENT_TYPE来判断的

/*** Verifies the object is a ReactElement.* See https://reactjs.org/docs/react-api.html#isvalidelement* @param {?object} object* @return {boolean} True if `object` is a ReactElement.* @final*/
export function isValidElement(object) {return (typeof object === 'object' &&object !== null &&object.$$typeof === REACT_ELEMENT_TYPE);
}
/*** Factory method to create a new React element. This no longer adheres to* the class pattern, so do not use new to call it. Also, instanceof check* will not work. Instead test $$typeof field against Symbol.for('react.element') to check* if something is a React Element.** @param {*} type 元素类型(dov、span...)* @param {*} props 子元素和元素基础属性* @param {*} key 元素的唯一标识,用于diff算法,节点对比* @param {string|object} ref 元素实例* @param {*} owner 父组件或者上级组件* @param {*} self 用于辅助判断this指向,后续会结合箭头函数去掉owner和字符串类型的ref参数* @param {*} source 保存文件名、行号和/或其他信息的资源数据* @internal*/
const ReactElement = function(type, key, ref, self, source, owner, props) {const element = {/*** 通过这个属性唯一标识是否是react element类型的元素* React在最后渲染的时候,会确认元素的类型是REACT_ELEMENT_TYPE* 表示组件元素的类型,值为十六进制或者Symbol值*/$$typeof: REACT_ELEMENT_TYPE,// 元素类型 (div、span...)type: type,// 元素唯一标识 diff中vmdom和真实dom对比时,首先判断keykey: key,// 组件实例ref: ref,// 向组件内部传递的属性数据props: props,// 父组件或者上级组件_owner: owner,};// 返回一个带有react element表示的dom元素return element;
};

ReactDOM.render

函数文件路径:packages/react-dom/src/client/ReactDOMLegacy.js
创建一个React Element元素之后,会传入render函数中进行渲染,render函数接收三个参数element、container、callback(可选), 然后调用legacyRenderSubtreeIntoContainer函数在container容器中调用react-reconciler中的updateContainer来更新队列以及整个Fiber。

/*** * @param {*} element 元素类型* @param {*} container element挂载在那些容器组件下* @param {*} callback 可选,渲染完成之后执行的回调* @returns */export function render(element: React$Element<any>,container: Container,callback: ?Function,
) {// 判断传入的父容器是否有效invariant(isValidContainer(container),'Target container is not a DOM element.',);return legacyRenderSubtreeIntoContainer(null,element,container,false,callback,);
}

从render到更新fiber进而更新真实dom渲染的大致流程如下:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/308084.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NFC物联网智慧校园解决方案

近场通信(Near Field Communication&#xff0c;NFC)又称近距离无线通信&#xff0c;是一种短距离的高频无线通信技术&#xff0c;允许电子设备之间进行非接触式点对点数据传输交换数据。这个技术由免接触式射频识别(RFID)发展而来&#xff0c;并兼容 RFID&#xff0c;主要用于…

【开源】基于Vue+SpringBoot的房屋出售出租系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 房屋销售模块2.2 房屋出租模块2.3 预定意向模块2.4 交易订单模块 三、系统展示四、核心代码4.1 查询房屋求租单4.2 查询卖家的房屋求购单4.3 出租意向预定4.4 出租单支付4.5 查询买家房屋销售交易单 五、免责说明 一、摘…

【Redis-01】RedisObject基本原理及各属性结构的作用

我们知道&#xff0c;redis常用的5种类型底层都是通过redisObject去封装的。看一下redisObject的源码&#xff1a; typedef struct redisObject {unsigned type:4;unsigned encoding:4;unsigned lru:LRU_BITS; int refcount;void *ptr; } robj;这几个属性都很重要&#xff0c;…

ubuntu python播放MP3,wav音频和录音

目录 一.利用pygame&#xff08;略显麻烦&#xff0c;有时候播放不太正常&#xff09;1.安装依赖库2.代码 二.利用mpg123&#xff08;简洁方便&#xff0c;但仅争对mp3&#xff09;1.安装依赖库2.代码 三.利用sox&#xff08;简单方便&#xff0c;支持的文件格式多&#xff09;…

YOLO训练results.csv文件可视化(原模型与改进模型对比可视化)

一、单独一个文件可视化&#xff08;源码对应utils文件夹下的plots.py文件的plot_results类&#xff09; from pathlib import Path import matplotlib.pyplot as plt import pandas as pd def plot_results(fileruns/train/exp9/results.csv, dir):# Plot training results.c…

图像的颜色及Halcon颜色空间转换transfrom_rgb/trans_to_rgb/create_color_trans lut

图像的颜色及Halcon颜色空间转换 文章目录 图像的颜色及Halcon颜色空间转换一. 图像的色彩空间1. RGB颜色 2. 灰度图像3. HSV/ HSI二. Bayer 图像三. 颜色空间的转换1. trans_from_rgb算子2. trans_to_rgb算子3. create_color_trans_lut算子 图像的颜色能真实地反映人眼所见的真…

python+django网上购物商城系统o9m4k

语言&#xff1a;Python 框架&#xff1a;django/flask可以定制 软件版本&#xff1a;python3.7.7 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发工具pycharm/vscode都可以 前端框架:vue.js 系统使用过程主要涉及到管理员和用户两种角色&#xff0c;主要包含个…

Android MVVM 写法

前言 Model&#xff1a;负责数据逻辑 View&#xff1a;负责视图逻辑 ViewModel&#xff1a;负责业务逻辑 持有关系&#xff1a; 1、ViewModel 持有 View 2、ViewModel 持有 Model 3、Model 持有 ViewModel 辅助工具&#xff1a;DataBinding 执行流程&#xff1a;View &g…

[NCTF 2022] web题解

[NCTF 2022]calc 考点&#xff1a;python环境变量注入 打开题目&#xff0c;F12有hint 访问一下得到源码 app.route("/calc",methods[GET]) def calc():ip request.remote_addrnum request.values.get("num")log "echo {0} {1} {2}> ./tmp/log…

@PersistenceContext和@Autowired在EntityManager上应用的不同

首先PersistenceContext是jpa专有的注解&#xff0c;而Autowired是spring自带的注释 上方图片的意思就是EntityManager不是线程安全的&#xff0c;当多个请求进来的时候&#xff0c;spring会创建多个线程&#xff0c;而PersistenceContext就是用来为每个线程创建一个EntityMana…

如何编写一个javaAgent jar工具包超详细教程

介绍 Java Agent技术 Java Agent技术是JDK提供的用来编写Java工具的技术&#xff0c;使用这种技术生成一种特殊的jar包&#xff0c;这种jar包可以让Java程序 运行其中的代码。 Java Agent技术的两种模式 Java Agent技术实现了让Java程序执行独立的Java Agent程序中的代码…

Spring boot:3.X + Security OAuth2 自定义登录页面、登出后跳转到登录页

本文描述了基于 Spring Oauth2 的 code 模式&#xff0c;实现登陆同时授权、自定义登录界面、登出页面的功能。 1.Maven依赖&#xff1a; <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactI…