React源码解析之createElement和render方法

这篇具有很好参考价值的文章主要介绍了React源码解析之createElement和render方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

参考资料

请注意,这是React16.8的源码解析,当然他完全可以作为你阅读源码的参考,他还没有落后。

Step1

开始之前,要先了解一个知识点⬇️
我们都知道,要在JSX中写React语法,那为什么不能在js文件中写呢?也可以,但是你要使用相关的Babel转一下react语法,转成JS认识的语法。

换句话说,必须得有Babel将JSX转成JS,你的代码才能正常运行。

Step2

看一下这段代码

const element = <h1 title="foo">Hello</h1>

JS肯定无法识别这句话,但是用到Step1中的Babel转一下,JS就能识别了,那么转成了什么样子呢?让我们打印一下element⬇️
React源码解析之createElement和render方法
没错,变成了一个对象,记住这个type和props

你可以把type理解成标签名,props理解成这个node的所有属性

要注意:这个对象并不是Babel生成的,是React.createElement的方法生成的。
Babel只是告诉node要把这个react语法通过React.createElement方法转成js能识别的对象(这是我猜的)

Step3

让我们来重写一下精简版的React的createElement方法

function createTextElement(text) {
  return {
    type: "TEXT_ELEMENT",
    props: {
      nodeValue: text,
      children: []
    }
  };
}

function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      children: children.map((chil) =>
        typeof chil === "object" ? chil : createTextElement(chil)
      )
    }
  };
}

这里的文本(TEXT_ELEMENT)对象好像和现在的略有不同(我没有看到现在React的TEXT_ELEMENT),不过它也不影响源码阅读。

让我们看几个dom节点生成了怎样的对象吧⬇️

const element = <h1 title="foo">Hello</h1>
⬇️⬇️⬇️
const element = React.createElement(
  "h1",
  { title: "foo" },
  "Hello"
)
⬇️⬇️⬇️
const element = {
  type: "h1",
  props: {
    title: "foo",
    children: "Hello",
  },
}
const element = (
  <div id="foo">
    <a>bar</a>
    <b />
  </div>
)
⬇️⬇️⬇️
const element = React.createElement(
  "div",
  { id: "foo" },
  React.createElement("a", null, "bar"),
  React.createElement("b")
)
⬇️⬇️⬇️
const element = {
  type: "div",
  props: {
    title: "id",
    children: [
		{
			type:"a",
			props:{
				children:"bar"
			}
		},
		{
			type:"b",
			props:{}
		},
	],
  },
}

Step4

接下来就是render了,将渲染完的对象element,渲染成真正的dom节点⬇️。
让我们重写一下精简版的ReactDom的render的方法,在18的版本中移除了它,也可以继续参考。

function render(element, container) {
  const { type, props } = element;
  // 根据type创建节点
  const dom =
    type !== "TEXT_ELEMENT"
      ? document.createElement(type)
      : document.createTextNode("");

  // 将属性添加到节点
  Object.keys(props)
    .filter((key) => key !== "children")
    .forEach((name) => (dom[name] = props[name]));

  // 递归增加子节点
  props.children.forEach((chil) => render(chil, dom));
  // 添加节点
  container.appendChild(dom);
}

Step5

接下来,你只需要像初始化react项目时,把你的App挂载到root上就可以了。

const element = (
  <div style="background: salmon">
    <h1>Hello World</h1>
    <h2 style="text-align:right">from Didact</h2>
  </div>
);

const container = document.getElementById("root");
Lee.render(element, container);

你可以看一下这个sandbox来验证你的学习成果。
这是我的完整练习代码⬇️文章来源地址https://www.toymoban.com/news/detail-415135.html

function createTextElement(text) {
  return {
    type: "TEXT_ELEMENT",
    props: {
      nodeValue: text,
      children: []
    }
  };
}

function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      children: children.map((chil) =>
        typeof chil === "object" ? chil : createTextElement(chil)
      )
    }
  };
}

function render(element, container) {
  const { type, props } = element;
  const dom =
    type !== "TEXT_ELEMENT"
      ? document.createElement(type)
      : document.createTextNode("");

  Object.keys(props)
    .filter((key) => key !== "children")
    .forEach((name) => (dom[name] = props[name]));

  props.children.forEach((chil) => render(chil, dom));
  container.appendChild(dom);
}

const Lee = {
  render,
  createElement
};

/** @jsx Lee.createElement */
const element = (
  <div style="background: salmon">
    <h1>Hello World</h1>
    <h2 style="text-align:right">from Didact</h2>
  </div>
);

const container = document.getElementById("root");
Lee.render(element, container);

到了这里,关于React源码解析之createElement和render方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [React源码解析] React的设计理念和源码架构 (一)

    任务分割 异步执行 让出执法权 1.React的设计理念 Fiber: 即对应真实dom, 又作为分隔单元。 Scheduler: 用js实现一套时间片运行的机制, 使得requestIdleCallback()的浏览器的兼容性和触发不稳定的问题解决。 Lane: 异步调度有了, 需要细粒度的管理各个任务的优先级, 让高优先级的先执行

    2024年02月07日
    浏览(30)
  • React基础源码解析

    前端魔术师卡颂的react学习视频(1 搭建项目架构_哔哩哔哩_bilibili)中提到了Rodrigo Pombo的一篇react源码教程:Build your own React 本文档分组旨在翻译和记录这篇文章的学习心得,作为react源码学习入门。 原文档目录 Step I: The createElement Function Step II: The render Function Step III: Concurr

    2024年02月06日
    浏览(28)
  • React Hooks 源码解析:useEffect

    React Hooks 源码解析(4):useEffect React 源码版本: v16.11.0 源码注释笔记:airingursb/react 1.1 为什么要有 useEffect 我们在前文中说到 React Hooks 使得 Functional Component 拥有 Class Component 的特性,其主要动机包括: 在组件之间复用状态逻辑很难 复杂组件变得难以理解 难以理解的 class 对

    2024年01月21日
    浏览(31)
  • React源码解析18(6)------ 实现useState

    在上一篇文章中,我们已经实现了函数组件。同时可以正常通过render进行渲染。 而通过之前的文章,beginWork和completeWork也已经有了基本的架子。现在我们可以去实现useState了。 实现之前,我们要先修改一下我们的index.js文件: 由于我们这一篇并不会实现React的事件机制,所以

    2024年02月13日
    浏览(22)
  • React re-render

    react的渲染分为两个阶段: render,组件第一次出现在屏幕上的时候触发 re-render, 组件第一次渲染之后的渲染 当app的数据更新时(用户手动更新、或异步请求)。 re-render发生有四种可能: state改变: 所有re-render的根源 父组件渲染 上下文改变: 全局变量的改变会引起所有使用此变量

    2024年02月10日
    浏览(28)
  • React源码解析18(7)------ 实现事件机制(onClick事件)

    在上一篇中,我们实现了useState的hook,但由于没有实现事件机制,所以我们只能将setState挂载在window上。 而这一篇主要就是来实现事件系统,从而实现通过点击事件进行setState。 而在React中,虽然我们是将事件绑定在JSX上的某个元素上,但是其实最终的执行者是最外层的容器

    2024年02月12日
    浏览(30)
  • React源码解析18(2)------ FilberNode,FilberRootNode结构关系

    在上一篇,我们实现了通过JSX转换为ReactElement的方法,也看到了转换后React元素的结构。但是这个React元素,并不能很清楚的表达组件之间的关系,以及属性的处理。 所以在React内部,会将所有的React元素转换为Filber树。而这一章节,主要就是简单描述一下FilberNode的结构。 首

    2024年02月13日
    浏览(25)
  • React源码解析18(10)------ 实现多节点的Diff算法

    在上一篇中,实现了多节点的渲染。但是之前写得diff算法,只能适用于单节点的情况,例如这种情况: 如果对于多节点的情况: 之前实现的diff算法就不会有效果了,所以在这一篇中,我们主要实现针对于多节点的diff算法。 实现之前,我们先将index.js修改一下: 在reconcile

    2024年02月12日
    浏览(24)
  • react中render阶段做了什么

    首先说明一个概念: render阶段对应的是Reconciler(协调器), commit阶段对应的的是Renderer(渲染器) render阶段开始于performSyncWorkOnRoot或performConcurrentWorkOnRoot方法的调用。这取决于本次更新是同步更新还是异步更新。 我们知道Fiber Reconciler是从Stack Reconciler重构而来,通过遍历的

    2024年02月20日
    浏览(32)
  • React源码解析18(3)------ beginWork的工作流程【mount】

    OK,经过上一篇文章。我们调用了: 生成了FilberRootNode和HostRootFilber。 并且二者之间的对应关系也已经确定。 而下一步我们就需要调用render方法来讲react元素挂载在root上: 所以我们现在要实现ReactDOM.createRoot中返回的render方法。 首先我们思考一下,在React中,除了通过上面的

    2024年02月13日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包