React–渲染(render&&createElement)的简单实现

前言

当前公司的技术栈还是以React为主,看了很多 jd 都要求需要对源码有了解甚至需要会写简单的源码实现,说干就干

初始化react项目

  1. cnpm i create-react-app -g
  2. create-react-app myReact
  3. 删除所有无关文件 仅留入口文件

image.png

4.我的react版本是17.0.2

  • 4.1 17以后的jsx编译不再生成React.createElement
  • 4.2 是在babel编译阶段 会自动import {jsx} from “react/jsx-runtime” jsx()替代createElement ,目的可能是为了解耦 ,项目里可以不做import React from “react”;
  • 4.3 script里将jsx() 禁用 因为要自己实现 createElement方法

禁用新的jsx转化.png

  • 4.4 npm run eject的坑,没事不要做弹出配置操作,原因是react17更改了jsx的转化规则 需要在script里set DISABLE_NEW_JSX_TRANSFORM=true ,来禁用jsx(),但弹出了配置后会一直禁用不成功 打包会报错(也留了一个坑,我没有找到为什么弹出后会一直禁用不成功)

React.createElement实现

  1. createElement作用

    生成vdom用于做render参数

  2. 确定createElement入参

babel编译工具

image.png

  1. createElement出参
  • type:标签类型
  • props:标签属性和children元素
  1. createElement 过程
 * @param type 元素类型
 * @param config 元素的配置对象
 * @param children 元素的子元素们
 */

function createElement(type, config, children) {
  // 解构config
  let props = { ...config };
  //判断方法入参 长度,
  // 大于3 则存在子元素(有标签的子元素)
  if (arguments.length > 3) {
    //获取子元素
    children = Array.prototype.slice.call(arguments, 2);
  }
  props.children = children;
  return {
    type,
    props,
  };
}
const React = { createElement };
export default React;
复制代码

React.render的实现

  1. 思路
  • vdom变真实dom
  • 虚拟dom属性同步给真实dom
  • 递归childrenDom
  • append到目标容器
  1. 实现过程
  • render方法要做的事:vdom转化真实DOM
/**
 * @param vdom 要渲染的虚拟对象
 * @param container append的目标容器节点
 */
function render(vdom, container) {
  //vdom转化真实DOM
  const dom = function(vdom){
  //vdom的类型:字符串,数字,直接返回文本节点
  if(typeof vdom === "string" || typeof vdom === "number"){
    document.createTextNode(vdom)
   }
   //否则就是一个虚拟对象---即:react元素 返回对应标签
     let { type, props } = vdom;
     let dom = document.createElement(type);
     //为dom设置样式 style,className
     //判断props.children 类型
    if (
      typeof props.children === "string" ||
      typeof props.children === "number"
    ){
       //children 只是文档
    dom.textContent = props.children;
    }else if (typeof props.children === "object" && props.children.type) {
    //children也是一个vdom
    //递归render,目标容器变成自己做后面的append
    render(props.children, dom);
    } else if (Array.isArray(props.children)) {
    //多个子元素的处理
    //for循环 依次调用render();
  }
     return dom;
  };
  container.appendChild(dom);
}
复制代码

调用render,reateElement

import ReactDOM from "./react-dom";

ReactDOM.render(
  React.createElement(
    "div",
    {
      className: "title",
      style: {
        color: "red",
      },
    },
    React.createElement("span", null, "hello"),
    "world"
  ),
  document.getElementById("root")
);

复制代码

效果

image.png

源码git地址

最后如果觉得本文有帮助 记得点赞三连哦 十分感谢

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享