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

相关推荐
小磊哥er9 分钟前
【前端工程化】前端工作中的业务规范有哪些
前端
ᥬ 小月亮19 分钟前
webpack基础
前端·webpack
YongGit38 分钟前
探索 AI + MCP 渲染前端 UI
前端·后端·node.js
慧一居士1 小时前
<script setup>中的setup作用以及和不带的区别对比
前端
RainbowSea2 小时前
NVM 切换 Node 版本工具的超详细安装说明
java·前端
读书点滴2 小时前
笨方法学python -练习14
java·前端·python
Mintopia2 小时前
四叉树:二维空间的 “智能分区管理员”
前端·javascript·计算机图形学
Mintopia2 小时前
Three.js 深度冲突:当像素在 Z 轴上玩起 "挤地铁" 游戏
前端·javascript·three.js
Penk是个码农2 小时前
web前端面试-- MVC、MVP、MVVM 架构模式对比
前端·面试·mvc
MrSkye2 小时前
🔥JavaScript 入门必知:代码如何运行、变量提升与 let/const🔥
前端·javascript·面试