1. Build your own React 阅读笔记——createElement和render实现

本文主要记录阅读Build your own react一些记录,虽然该博客构建的ReactV16.8(所以博客中react代码都是V16的写法),和现在ReactV19版本有一定差别,但V16.8开始包含hooks组件,主体渲染流程是基本一致的,原文最后目的是通过砍掉react优化策略内容,保留最本质重要思想,并实现didact框架(200多行代码)模拟react基本渲染过程

我们可以收获:

  • 知道react写法对应的原生JS实现都做了哪些事情;
  • 知道react背后渲染流程步骤有哪些,以及这么做理由;
  • 明白上面这些,自己再实现一个简约版React(useState),加深对React的理解;

react写法对应原生JS实现

reactjsx是一种声明式写法,开发者无需直接操作dom,只需在函数组件返回中写jsx需要渲染dom结构,然后react帮助我们去更新dom,所以react是做了一层抽象封装,最后react代码也是会被编译为对dom操作,所以我们的角度(从react代码,看其背后原生js做的事情)。

tsx 复制代码
const element = <h1 title="foo">Hello</h1>
const container = document.getElementById("root")
ReactDOM.render(element, container)  //V18之前写法

注:V18使用ReactDOM.createRoot(root).render(<App />) 来渲染root节点以支持并发模式。

上面的jsx写法const element = <h1 title="foo">Hello</h1> 通过babel调用React内置的createElement方法,实际上会被转义一个js对象记录节点的type类型propschildren等基本信息 而ReactDOM.render(element, container),实际上做就是根据传入的React Element对象生成对应dom元素并挂载到容器root节点上。

js 复制代码
// jsx 对应js对象
const element = {
    type: "h1",
    props: {
        title: "foo",
        children: "Hello",
   }
},
const container = document.getElementById("root")

// ReactDOM.render对应做的事情
const node = document.createElement(element.type)
node["title"] = element.props.title
const text = document.createTextNode("")
text["nodeValue"] = element.props.children
node.appendChild(text)
container.appendChild(node)

createElement

React.createElement

为了复刻一个react,我们要做就是实现createElement,在编译时让babel调用我们实现createElement转为js对象,对着react.createElement实现看看参数列表,基本都是包含节点的type类型propschildren信息,不同时返回节点类型不同,并且把input单拎出来定义。

tsx 复制代码
 createElement('img',{src:'xxxx'})

自定义版createElement

自定义版不需要考虑很多,保留基本逻辑就行。

tsx 复制代码
function createElement(type, props, ...children) {
 return {
   type,
   props: {
     ...props,
     children: children.map((child) =>
       typeof child === "object" ? child : createTextElement(child)
     ),
   },
 };
}

function createTextElement(text) {
 return {
   // 这里将文本类型,定义TEXT_ELEMENT
   type: "TEXT_ELEMENT",
   props: {
     nodeValue: text,
     children: [],
   },
 };
}
tsx 复制代码
/** @jsx Didact.createElement */
const element = (
   <div id="foo">
       <span>图片描述</span>
       <img src='xxxx'/>
   </div>)
   
// 上面jsx会转义成下面的babel调用
const element = Didact.createElement(
   "div",
   { id:"foo"},
   Didact.createElement("span", null, "图片描述"),
   Didact.createElement("img")
),

// 对应js对象
const element= {
    type: 'div',
    props:{
        id:"foo",
        children:[{
            type: 'span',
            props:{
                children:[{
                    type:'TEXT_ELEMENT',
                     props: {
                     nodeValue: '图片描述',
                     children: [],
                   },
                }]
            },
            {
             type: 'img',
             props:{
                 children:[]
             }
            }
        }]
    }
}

render函数

有了需要确认渲染结构elements对象,我们只需要传入render函数执行document.createElement等原生相关方法

ts 复制代码
function render(element, container) {
    const dom = element.type == "TEXT_ELEMENT"
? document.createTextNode("") : document.createElement(element.type)

   const isProperty = key => key !== "children"

   Object.keys(element.props).filter(isProperty)
   .forEach(name => {
       dom[name] = element.props[name]
   })

   element.props.children.forEach(child =>
       render(child, dom)
   )
   
   container.appendChild(dom)
}

小结

有了createElementrender,我们似乎可以通过写jsx代码到渲染真正的dom元素,但是render里只有添加dom,更新和删除并没有实现,并且由于render是递归调用的,这意味着一旦开始渲染,就不会停止执行,如果元素树很大,可能会阻塞主线程太长时间。如果浏览器需要执行高优先级的操作,例如处理用户输入或保持动画流畅,则必须等到渲染完成。 所以还需要进行优化实现并发模式渲染fiber比较渲染提交

相关推荐
一袋米扛几楼989 分钟前
【Git】规范化协作:详解 GitHub 工作流中的 Issue、Branch 与 Pull Request 最佳实践
前端·git·github·issue
网络点点滴23 分钟前
前端与后端的区别与联系
前端
EnCi Zheng1 小时前
M5-markconv自定义CSS样式指南 [特殊字符]
前端·css·python
kyriewen1 小时前
你的网页慢,用户不说直接走——前端性能监控教你“读心术”
前端·性能优化·监控
广州华水科技1 小时前
北斗GNSS变形监测在大坝安全监测中的应用与优势分析
前端
前端老石人1 小时前
前端开发中的 URL 完全指南
开发语言·前端·javascript·css·html
CAE虚拟与现实1 小时前
五一假期闲来无事,来个前段、后端的说明吧
前端·后端·vtk·three.js·前后端
Sarvartha1 小时前
三目运算符
linux·服务器·前端
晓晨的博客1 小时前
ROS1录制的bag包转换为ROS2格式
前端·chrome
Wect1 小时前
LeetCode 72. 编辑距离:动态规划经典题解
前端·算法·typescript