react遍历对象的值_React 原理之实现 createElement 和 render 方法

前言

在 React 中,我们都知道可以写 jsx 代码会被编译成真正的 DOM 插入到要显示的页面上。这具体是怎么实现的,今天我们就自己动手做一下。

实现 createElement 方法

这个方法平时开发我们并不会用到,因为它是经 babel 编译后的代码,我们新建一个 React 项目,index.js 最简单的代码结构如下:

import React from 'react'
import ReactDOM from 'react-dom'
ReactDOM.render(<h1 className='title'>Hello Reacth1>, document.getElementById('root'))

这里就 jsx 会变编译成真正的 DOM ,把 html 代码拿到 babel 官网编译

f39255d8ab1496a79eae2ec9d491348a.png

于是我们就看到了 React.createElement() 方法,但这只是调用这个方法,它具体做了什么返回什么我们还不知道,我们可以打印这个函数运行的结果:

console.log(
  React.createElement(
    'h1',
    {
      className: 'title',
    },
    'Hello React'
  )
)

8df72f74a1510f86715fcfc06c75e81e.png返回的这个对象就是虚拟 DOM 了。

我们来分析它返回的对象参数,首先第一个是

  • $$typeof: REACT_ELEMENT_TYPE

这个是 React 元素对象的标识属性

REACT_ELEMENT_TYPE 的值是一个 Symbol 类型,代表了一个独一无二的值。如果浏览器不支持 Symbol 类型,值就是一个二进制值。

为什么是 Symbol?主要防止 XSS 攻击伪造一个假的 React 组件。因为 JSON 中是不会存在 Symbol 类型的。

  • key:这个比如循环中会用到这个 key 值
  • props:传入的属性值,比如 id, className, style, children 等
  • ref:DOM 的引用
  • 剩下的是私有属性(本篇不展开讨论)

在本篇我们会用自己简单的方式实现这两个方法,而不是根据源码,所以实现上的方法只要能实现它的基本功能即可;有个基本概念在,以后再循序渐进学习源码。

而 createElement 中有三个参数,更确切说是 n 个参数:

  • type:表示要渲染的元素类型。这里可以传入一个元素 Tag 名称,也可以传入一个组件(如 div span 等,也可以是是函数组件和类组件)
  • props:创建 React 元素所需要的 props。
  • childrens(可选参数):要渲染元素的子元素,这里可以向后传入 n 个参数。可以为文本字符串,也可以为数组

初步 createElement 方法:

// 创建 JSX 对象
function createElement(type, props, ...childrens) {
    return {
        type,
        props: {
          ...props,
          children: childrens.length <= 1 ? childrens[0] || '' : childrens,
        },
}

参数中 props 和 childrens 是并列关系,然后返回的 props 对象,里面包含了 children,所以我们需要再 props 里面添加 children 参数,然后根据 children 参数为一个或多个的可能在进行取值处理。

调用该方法:

console.log(
  createElement(
    'h1',
    {
      className: 'title',
    },
    'Hello React'
  )
)

06eca7d97120ccd68478abd4dcc06193.png除去其它本篇我们不讨论的属性,目前算是实现了一半;我们观察原来 React 自身方法输出的结果有 key, ref, 同输出的 props 也是并列关系,于是我们进一步作出处理

function createElement(type, props, ...childrens) {
  let ref, key
  if ('ref' in props) {
    ref = props['ref']
    props['ref'] = undefined
  }
  if ('key' in props) {
    key = props['key']
    props['key'] = undefined
  }
  return {
    type,
    props: {
      ...props,
      children: childrens.length <= 1 ? childrens[0] || '' : childrens,
    },
    ref,
    key,
  }
}

同样的方式调用结果如下:

2f44952f7c1e5dcd3fa804bc9997fa0f.png如果添加多一些属性,我们来看看结果

console.log(
  createElement(
    'div',
    { id: 'box', className: 'box', style: { color: 'red' }, key: '20' },
    'this is text',
    createElement('h2', { className: 'title' }, 'hello'),
    createElement('div', { className: 'content' }, 'Hi')
  )
)

9285e46d4651a2ab3979281d553175f4.png用了这种比较粗鲁的方式添加,设置为 undefined 在实现 render 方法的时候我们会根据这个忽略 props 内部的 key 和 props 属性,这里就实现了最基本的 createElement 方法了。

实现 render 方法

render 方法的第一个参数接收的是 createElement 返回的对象,也就是虚拟 DOM;第二个参数则是挂载的目标 DOM。同样的做法,我们用 babel 编译来看:

73f47b963694230f498e3b449e0ffc57.png执行后,就被挂在到页面了

8ae996674fef5494b935f6579116d393.png

实现代码如下:

/*
 * 功能:把创建的对象生成对应的DOM元素,最后插入到页面中
 * objJSX:createElement 返回的 JSX 对象
 * container:挂载的容器,如 document.getElementById('root')
 */
function render(objJSX, container) {
  let { type, props } = objJSX
  let newElement = document.createElement(type)
  for (let attr in props) {
    // 遍历传入的 props 属性
    if (!props.hasOwnProperty(attr)) break // 不是私有的直接结束遍历
    let value = props[attr] // >如果当前属性没有值,直接不处理即可
    if (value == undefined) continue // NULL OR UNDEFINED

    // 对几个特殊属性单独设置
    switch (attr.toUpperCase()) {
      case 'ID':
        newElement.setAttribute('id', value)
        break
      case 'CLASSNAME':
        newElement.setAttribute('class', value)
        break
      case 'STYLE': // 传入的行内样式 style 是个对象,故需遍历赋值
        for (let styleAttr in value) {
          if (value.hasOwnProperty(styleAttr)) {
            newElement['style'][styleAttr] = value[styleAttr]
          }
        }
        break
      case 'CHILDREN':
        /*
         * 可能是一个值:可能是字符串也可能是一个JSX对象
         * 可能是一个数组:数组中的每一项可能是字符串也可能是JSX对象
         */
        // 首先把一个值也变为数组,这样后期统一操作数组即可
        !(value instanceof Array) ? (value = [value]) : null
        value.forEach((item, index) => {
          // 验证ITEM是什么类型的:如果是字符串就是创建文本节点,如果是对象,我们需要再次执行RENDER方法,把创建的元素放到最开始创建的大盒子中
          if (typeof item === 'string') {
            let text = document.createTextNode(item)
            newElement.appendChild(text)
          } else {
            render(item, newElement)
          }
        })
        break
      default:
        newElement.setAttribute(attr, value)
    }
  }
  container.appendChild(newElement)
}

欢迎关注我掘金账号和Github技术博客:

  • 掘金:https://juejin.im/user/1257497033714477
  • Github:https://github.com/Jacky-Summer
  • 觉得对你有帮助或有启发的话欢迎 star,你的鼓励是我持续创作的动力~
  • 如需在微信公众号平台转载请联系作者授权同意,其它途径转载请在文章开头注明作者和文章出处。

2f4273792bb2fb84e9658e56a92da263.png

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

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

相关文章

成熟就是深谙世故却不世故

对于一个打小在农村长大的孩子而言&#xff0c;被夸作“早熟”是一种荣耀。它意味着你足够懂事&#xff0c;可以分担父母的一些忧愁。但这也是一个怪圈&#xff0c;因为你常要背负这种光环去做一些超乎自己年龄的事&#xff0c;强迫自己变得坚忍、不可战胜。那年&#xff0c;我…

什么叫工作到位?很深刻!

1、汇报工作说结果不要告诉老板工作过程多艰辛&#xff0c;你多么不容易&#xff01;老板不傻&#xff0c;否则做不到今天。举重若轻的人老板最喜欢&#xff0c;一定要把结果给老板&#xff0c;结果思维是第一思维。2、请示工作说方案不要让老板做问答题&#xff0c;而是要让老…

react 给一个引用的组件添加新属性_高阶组件在React中的应用

高阶组件的定义接受React组件作为输入&#xff0c;输出一个新的React组件。概念源自于高阶函数&#xff0c;将函数作为参数&#xff0c;或者输出一个函数&#xff0c;如map&#xff0c;reduce&#xff0c;sort。 用haskell的函数签名来表示&#xff1a; hocFactory:: W: React.…

好好的活,简简单单过!

生命&#xff0c;每个人只有一次&#xff0c;或长或短&#xff1b;生活&#xff0c;每个人都在继续&#xff0c;或悲或欢&#xff1b;人生&#xff0c;每个人都在旅途&#xff0c;或起或伏。人无完人&#xff0c;事无完美&#xff0c;有些小人&#xff0c;你不须计较&#xff0…

lunixs 退出mysql_MySQL的基本操作

1、数据库登录格式&#xff1a;mysql -h主机地址 -u用户名 -p用户密码 -P端口 -D数据库 -e “SQL内容”[rootwulaoer ~]# mysql -uroot -p2、修改密码格式&#xff1a;mysqladmin -u用户名 -p旧密码 password 新密码[rootwulaoer ~]# mysqladmin -uroot password 123456注&…

交际中你所不知道的说话的12个技巧!

1.“有一说一”和“自以为是”不同&#xff0c;别把粗鲁当成真性情与 他人相处&#xff0c;要遵循一个基本原则&#xff1a;己所不欲&#xff0c;勿施于人。你可以真挚地描述自己的感受&#xff0c;前提是不要带有攻击性&#xff0c;至于对他人做出评价和判断&#xff0c;则需要…

宽客的人amp;amp;事件映射

看完《宽客》这本书&#xff0c;叙事介绍20世纪华尔街对冲基金、股票、投资者依赖股市从直觉交易数学家的早期演化、物理学家用数学模型开发过程中的交易&#xff0c;这些进入金融数学家、物理学家依靠大数据分析、稍纵即逝的交易机会来买入卖出&#xff1b;同一时候找出交易模…

社交中的黄金法则,你要细细体会品味

1&#xff0c;不要急着用你的嘴&#xff0c;来为你的眼睛辩护什么。因为天知道你的嘴说出什么来。2&#xff0c;假如有人朝你扔石头&#xff0c;就收起来。因为那会是你日后建高楼的基石。3&#xff0c;能忍则忍&#xff0c;忍不了就改变&#xff0c;改变不了就认了哇。4&#…

这些略带幽默句子,笑的同时多感悟一下吧(收好)

1、穷&#xff0c;并不可怕&#xff0c;我怕穷着穷着就习惯了。2、世界上唯一不用努力&#xff0c;就能得到的只有年龄。3、我是你转身就忘的路人甲&#xff0c;凭什么陪你蹉跎年华到天涯&#xff1f;4、世界上最快乐的事情就是吃&#xff0c;第二快乐的是待会再吃。5、琴棋书画…

java 异常机制_深入理解Java异常处理机制

一、引子try…catch…finally恐怕是大家再熟悉不过的语句了&#xff0c;而且感觉用起来也是很简单&#xff0c;逻辑上似乎也是很容易理解。不过&#xff0c;我亲自体验的“教训”告诉我&#xff0c;这个东西可不是想象中的那么简单、听话。不信&#xff1f;那你看看下面的代码&…

做人:失信是最大的破产!

一个人最大的破产是信用的破产&#xff01;哪怕你一无所有&#xff0c;但只要信用还在&#xff0c;就还有翻身的本金。保护好信用&#xff0c;珍惜别人给你的每一次信任&#xff01;因为有时候我们只有一次机会&#xff01;朋友有时候就像钞票&#xff0c;有真也有假。我们需要…

【干货】微信公众号运营必备工具(完整版)

做微信公众号运营最基本的要素有两点&#xff1a;一是内容要强大&#xff08;内&#xff09;&#xff0c;二是排版要美观&#xff08;外&#xff09;。做好前者&#xff0c;你需要有充足的知识储备以及精彩独到的文笔作为支撑&#xff0c;而做好后者则相对简单许多&#xff0c;…

成功,要“借力”,不要“尽力”(深刻!)

01每个人都喜欢成功&#xff0c;却又时常感觉自己力不从心一个小男孩在院子里搬一块石头&#xff0c;父亲在旁边鼓励&#xff1a;“孩子&#xff0c;只要你全力以赴&#xff0c;一定搬得起来&#xff01;”但是石头太重&#xff0c;最终孩子也没能搬起来。他告诉父亲&#xff1…

从0开始学Java——JSPServlet——HttpServletRequest相关的几个路径信息

在HttpServletRequest中有几个获取路径的接口&#xff1a;getRequestURI/getContextPath/getServletPath/getPathInfo 这些接口互相之间有什么区别&#xff0c;通过下面这段代码就可以分辨清楚了&#xff1a; 1 WebServlet("/hello.view")2 public class FirstServle…

C#编译和运行过程图例

一张图&#xff0c;描述C#编译和运行过程&#xff0c;比较容易记忆理解

C#中的堆和栈理解

引言&#xff1a;程序运行时&#xff0c;它的数据必须存在内存中&#xff0c;一个数据需要多大内存、存储在什么地方以及如何存储都依赖于该数据的数据类型。1、什么是栈栈是一个内存数组&#xff0c;是一个LIFO&#xff08;Last-In-First-Out 后进先出&#xff09;的数据结构。…

C# 基础:Sealed、new、virtual、abstract、override的理解

目录 1、sealed 2、new 3、virtual 4、abstract 5、override 1、sealed 密封类不能被继承&#xff0c;密封方法可以重写基类中的方法&#xff0c;但其本身不能在任何派生类&#xff08;子类&#xff09;中 进一步重写。当应用于属性或者方法时&#xff0c;sealed 修饰符必须始终…

U3D 脚本添加和获得对象

有时候&#xff0c;一开始可能没有对象&#xff0c;而是由于某种触发&#xff0c;产生的一个对象&#xff0c;这里讲解下&#xff0c;如何通过脚本来创建一个对象&#xff1a; 这是通过脚本创建一个立方体&#xff1a; using UnityEngine; using System.Collections;public cla…

50条超精辟的经典语录:哗众,可以取宠,也可以失宠!

在人生道路上给自己定位很重要&#xff0c;不要苛求自己达到不可能达到的高度。我们能把每一件平凡的事做好就是不平凡&#xff0c;把每一件简单的事做成功就是不简单。1.我们只有一个地球&#xff0c;所以你要爱护地球&#xff1b;地球上只有一个我&#xff0c;所以你也要爱护…