实现一个精简React -- 入口文件与JSX (1)

156 阅读2分钟

前段时间系统学习了React,这里做一个知识梳理和技术要点总结。本文将以React项目入口文件为切入点,深入解析框架的核心运行原理,并重点探讨JSX的本质与实现机制。

一、项目入口文件解析

在基于React 18+创建的项目中,main.js作为应用入口文件承担着初始化的重要职责。其典型结构如下:

import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'

createRoot(document.getElementById('root')).render(<App />)

这个入口文件中,在 createRoot 函数中传入根节点,并调用 render 函数,最终实现将组件挂载到根节点root上。

由此最为出发点,不难理解createRoot函数的大概逻辑是这样的

const createRoot = (container) => {
    return {
        render: (app) => {
            // to do thind...
        }
    }
}

这里我们可能会有疑问,传进 render 中的函数组件 ,是怎么处理成虚拟dom的? 这里我们就需要简单解释一下,什么是jsx?为什么需要jsx?

理解JSX设计:为什么需要.jsx文件?

一、JSX不是真正的JavaScript

在写React时会发现一个现象:用普通.js文件写组件会报错,但改成.jsx文件可以。 举个例子,当你在代码里写这样的"HTML标签"时:

let button = <Button>Click me</Button>

由于JavaScript引擎无法解析这样的代码,所以这时就需要一个"翻译官"(比如Babel或esBuild),把这种混合HTML的写法转换成标准的JavaScript函数调用:

let button = React.createElement(Button, null, "Click me");

二、JSX的变身过程

一个更直观的例子:

// 你写的JSX
<div id="app">
  <h1>Hello World</h1>
</div>

// 被转换为
React.createElement("div", { id: "app" },
  React.createElement("h1", null, "Hello World")
);

最终会生成一个描述页面结构的虚拟DOM对象:

{
  type: 'div',
  props: {
    id: 'app',
    children: [{
      type: 'h1',
      props: {
        children: 'Hello World'
      }
    }]
  }
}

到这里就能明白,传入render中的函数组件 其实就是虚拟dom结构。

三、为什么要大费周章?

为什么不直接写React.createElement呢? 试想如果要描述一个复杂界面:

// JSX写法
<Page>
  <Header />
  <MainContent>
    <Article />
    <Sidebar />
  </MainContent>
</Page>

// 原生写法
React.createElement(Page, null,
  React.createElement(Header),
  React.createElement(MainContent, null,
    React.createElement(Article),
    React.createElement(Sidebar)
  )
);

显然JSX更接近我们熟悉的HTML结构,既提升了可读性,又降低了学习成本。这就像用"中文"写代码,再自动翻译成"英文"给机器执行。

四、现代工具链的配合

虽然本文以Vite的esBuild举例,但无论你使用Webpack、Rollup还是其他构建工具,核心流程都是相似的:

  1. 识别JSX语法
  2. 转换成React.createElement调用
  3. 生成虚拟DOM结构
  4. 最终渲染为真实DOM

这整套流程就像一条自动化生产线,开发者只需要关心如何用直观的JSX描述界面,后续的复杂转换都由工具链默默完成。

项目源码:github.com/Cuimc/mini-…