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比较渲染提交

相关推荐
摸鱼仙人~29 分钟前
styled-components:现代React样式解决方案
前端·react.js·前端框架
sasaraku.1 小时前
serviceWorker缓存资源
前端
RadiumAg2 小时前
记一道有趣的面试题
前端·javascript
yangzhi_emo2 小时前
ES6笔记2
开发语言·前端·javascript
yanlele2 小时前
我用爬虫抓取了 25 年 5 月掘金热门面试文章
前端·javascript·面试
中微子4 小时前
React状态管理最佳实践
前端
烛阴4 小时前
void 0 的奥秘:解锁 JavaScript 中 undefined 的正确打开方式
前端·javascript
中微子4 小时前
JavaScript 事件与 React 合成事件完全指南:从入门到精通
前端
Hexene...4 小时前
【前端Vue】如何实现echarts图表根据父元素宽度自适应大小
前端·vue.js·echarts
天天扭码5 小时前
《很全面的前端面试题》——HTML篇
前端·面试·html