实现自己的mini-React 1(手摸手系列)

写在开头,本系列篇幅较长包含大量代码及思考逻辑,主要实现涉及React中的:

  • Function Component
  • useEffect
  • useState
  • 点击事件处理及props绑定
  • 任务调度器
  • Fiber架构(树结构到链表结构转换)

文章会从main.jsx结合jsx处理后的dom树及源码带入react的大概实现逻辑,先上代码链接(后续会更新优化,方便阅读),文章产出源自@阿崔cxrmini-react活动课程。

Step 1 项目搭建

本文使用vite搭建的js空项目,后续可以直接上jsx语法,结合react项目的入口文件开始反推;

如上图所示,关键代码在最后一行,意在通过ReactDOMcreateRoot传入一个根节点,返回一个render函数处理App组件;可以猜想到是App组件在通过jsx转换后给到render函数渲染vDom,最后挂载到根节点上。

拆分任务

  • 先上js语法,从最基础的dom创建、vDom生成
  • 修改函数名及层级结构,对齐react
js 复制代码
// 创建 vdom

function createTextNode(text){
    return {
        type: 'TEXT_ELEMENT',
        props: {
            nodeValue: text,
            children: []
        }
    }
}
function createElement(type, props, ...children){
    return {
        type,
        props: {
            ...props,
            children: children.map(el => typeof el === 'string'?createTextNode(el):el)
        }
    }
}
/**
 * 
 * @param {创建元素} el 
 * @param {容器元素} container 
 */
function myRender(el,container) {
    // 创建元素
    const dom = el.type === 'TEXT_ELEMENT' 
    ? document.createTextNode('')
    : document.createElement(el.type)
    // 处理props
    Object.keys(el.props).map(key =>{
        if(key !== 'children'){
            // 属性赋值
            dom[key] = el['props'][key]
        }
    })
        // 处理children
        el['props']['children'].map(child =>{
            // 插入父节点
            myRender(child, dom)
        })
    container.appendChild(dom)
}

const root = document.querySelector('#root')
// root.appendChild(dom)

// ?1 优化
const App = createElement('div',{id:'app'},'test 03')
// myRender(App,root)

const ReactDOM = {
    createRoot(container){
        return {
            render(el){
                myRender(el,container)
            }
        }
    }
}

ReactDOM.createRoot(root).render(App)

vDom创建与函数名对齐

根据官网的const element = createElement(type, props, ...children),我们知道创建dom需要传递的数据及类型,而后区分节点类型是文本节点还是其他dom节点。

关于vDom的数据结构,其中props嵌套children,可以拉平也可以嵌套,嵌套是为了和react对齐。

文件层级划分及拆分React.js、ReactDom.js

拆分结果ReactDom.js图:

App.js 代码结构:

js 复制代码
import React from './core/React.js';
const App = createElement('div',{id:'app'},'test 03')
export default App

main.js 的代码结构图:

React.js 代码结构,修改myRender=>render对齐react:

js 复制代码
/ 创建 vdom

function createTextNode(text){
    return {
        type: 'TEXT_ELEMENT',
        props: {
            nodeValue: text,
            children: []
        }
    }
}
function createElement(type, props, ...children){
    return {
        type,
        props: {
            ...props,
            children: children.map(el => typeof el === 'string'?createTextNode(el):el)
        }
    }
}
/**
 * 
 * @param {创建元素} el 
 * @param {容器元素} container 
 */
function render(el,container) {
    // 创建元素
    const dom = el.type === 'TEXT_ELEMENT' 
    ? document.createTextNode('')
    : document.createElement(el.type)
    // 处理props
    Object.keys(el.props).map(key =>{
        if(key !== 'children'){
            // 属性赋值
            dom[key] = el['props'][key]
        }
    })
        // 处理children
        el['props']['children'].map(child =>{
            // 插入父节点
            myRender(child, dom)
        })
    container.appendChild(dom)
}
const React = {
 render,
 createElement
}

到此第一步的工作已经完成,mini-react的形有了,下一步丰富内在。

相关推荐
无咎.lsy几秒前
vue之vuex的使用及举例
前端·javascript·vue.js
fishmemory7sec8 分钟前
Electron 主进程与渲染进程、预加载preload.js
前端·javascript·electron
fishmemory7sec11 分钟前
Electron 使⽤ electron-builder 打包应用
前端·javascript·electron
豆豆1 小时前
为什么用PageAdmin CMS建设网站?
服务器·开发语言·前端·php·软件构建
twins35202 小时前
解决Vue应用中遇到路由刷新后出现 404 错误
前端·javascript·vue.js
qiyi.sky2 小时前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~2 小时前
分析JS Crash(进程崩溃)
java·前端·javascript
安冬的码畜日常2 小时前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺
l1x1n03 小时前
No.3 笔记 | Web安全基础:Web1.0 - 3.0 发展史
前端·http·html
昨天;明天。今天。3 小时前
案例-任务清单
前端·javascript·css