构建 react - jsx

调试准备

调试项目使用 vite 搭建,进行 react 开发环境调试,所以使用的所有 react 方法都是 dev 方法。

vite.config.js 复制代码
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";

export default defineConfig({
  plugins: [react()],
  resolve: {
    // 配置别名,导入 react 时从本地代码导入。
    alias: {
      react: path.resolve(__dirname, "./src/react"),
      shared: path.resolve(__dirname, "./src/shared"),
      "react-dom": path.resolve(__dirname, "./src/react-dom"),
      scheduler: path.resolve(__dirname, "./src/scheduler"),
      "react-reconciler": path.resolve(__dirname, "./src/react-reconciler"),
    },
  },
});
jsconfig.json 复制代码
{
  "compilerOptions": {
    "baseUrl": "./",
    
    // 配置路径
    "paths": {
      "react/*": ["src/react/*"],
      "react-dom/*": ["src/react-dom/*"],
      "scheduler/*": ["src/scheduler/*"],
      "shared/*": ["src/shared/*"],
      "react-reconciler/*": ["src/react-reconciler/*"]
    }
  },
  "exclude": ["node_modules", "dist"]
}

jsx 执行流程

graph LR A["用户编写 JSX"] --> B["Babel 编译"] B --> C["执行 React 运行时"]

babel 编译结果

js 复制代码
// jsx 代码
const element = (
  <h1>
    hello <span>test</span> children
  </h1>
);

// 编译结果 删除静态标记
import { jsxDEV as _jsxDEV } from "react/jsx-dev-runtime";

const element = _jsxDEV("h1", {
  children: [
    "hello ",
    _jsxDEV("span", { children: "test" }, undefined, false, { fileName: "example.jsx", lineNumber: 1, columnNumber: 15 }, this),
    " children"
  ]
}, undefined, true, { fileName: "example.jsx", lineNumber: 1, columnNumber: 1 }, this);

由编译结果代码可知,babel 编译完成后,会自动从react/jsx-dev-runtime 中获取 jsxDEV 函数。之前配置调试环境时,将react 文件夹指向 src/react,所以需要实现 src/react

实现

首先实现 jsx-dev-runtime 文件,这个文件在源码中功能很简单,只是导出函数。

jsx-dev-runtime.js 复制代码
export { jsxDEV } from "./src/jsx/ReactJSXElement";

实现 jsxDEV 函数

jsxDEV 函数的功能很明确,根据参数生成虚拟 DOM 并返回。

由编译后代码可知 jsxDEV 函数接受两个参数。

  • type 组件名(h1 div)
  • config 属性
js 复制代码
import hasOwnProperty from "shared/hasOwnProperty";
import { REACT_ELEMENT_TYPE } from "shared/ReactSymbols";

// 保留属性,不加入 props
const RESERVED_PROPS = {
  key: true,
  ref: true,
  __self: true,
  __source: true,
};

function hasValidKey(config) {
  return config.key !== undefined;
}

function hasValidRef(config) {
  return config.ref !== undefined;
}

function ReactElement(type, key, ref, props) {
  // 虚拟DOM
  return {
    $$typeof: REACT_ELEMENT_TYPE,
    type, // 组件名称 h1 span
    key, // key
    ref, // ref
    props, // 属性对象 children id style
  };
}

export function jsxDEV(type, config) {
  let propName; // 属性名
  let props = {}; // 属性对象
  let key = null; // 唯一标识
  let ref = null; // 引用,对应真实DOM

  // 传入的配置中 key 值有效才赋值给 key
  if (hasValidKey(config)) {
    key = config.key;
  }
  
  // 传入的配置中的 ref 有效才赋值给 ref
  if (hasValidRef(config)) {
    ref = config.ref;
  }

  // 遍历传入配置中的字段,如果字段是 config 本身的属性
  // 且字段不为保留属性,则赋值给 props
  for (propName in config) {
    if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
      props[propName] = config[propName];
    }
  }

  // 根据处理后的数据,生成虚拟 DOM
  return ReactElement(type, key, ref, props);
}
shared/hasOwnProperty 复制代码
const { hasOwnProperty } = Object.prototype;

export default hasOwnProperty;
shared/ReactSymbols 复制代码
export const REACT_ELEMENT_TYPE = Symbol.for("react.element");

结束

以上就是 react 中的 jsx 处理。

相关推荐
菜鸟‍26 分钟前
【前端学习】仿Deepseek官网AI聊天网站React
前端·学习·react.js
小光学长1 小时前
基于Vue的保护动物信息管理系统r7zl6b88 (程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
前端·数据库·vue.js
huangql5201 小时前
截图功能技术详解:从原理到实现的完整指南
前端·html5
长空任鸟飞_阿康1 小时前
Node.js 核心模块详解:fs 模块原理与应用
前端·人工智能·ai·node.js
这儿有一堆花2 小时前
网站链接重定向原理
前端
cecyci2 小时前
如何实现AI聊天机器人的打字机效果?
前端·javascript
IT_陈寒2 小时前
Vite 5个隐藏技巧让你的项目构建速度提升50%,第3个太香了!
前端·人工智能·后端
詩句☾⋆᭄南笙2 小时前
HTML的盒子模型
前端·html·盒子模型
落言2 小时前
AI 时代的工程师:懂,却非懂的时代
前端·程序员·架构
一枚攻城狮2 小时前
前端知识点大汇总
前端