构建 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 处理。

相关推荐
崔庆才丨静觅4 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了5 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅5 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅6 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅6 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊6 小时前
jwt介绍
前端
爱敲代码的小鱼7 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax