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

相关推荐
雪芽蓝域zzs7 分钟前
第三节:目录结构重构(仿若依,优化层级,layout 与 components 同级)
前端·vue.js·重构
子非鱼a1 小时前
【WEB】[NewStarCTF 公开赛赛道]UnserializeOne
前端·javascript·html
南雨北斗1 小时前
Vue3 v-model中的自动事件绑定
前端
雪芽蓝域zzs2 小时前
第八节:Element Plus 基础、封装通用组件 + 路由守卫
前端·vue.js
码视野3 小时前
基于 Spring Boot + Vue3 的【智慧公厕微负压排风除臭与客流人感空间占用导引中台】设计与实现(含PRD/三端高保真源码/大屏)
前端·vue.js·人工智能·spring boot·后端
计算机魔术师3 小时前
刚赔了15亿又来被告?索尼华纳联手把Anthropic告上法庭
前端
笑霸final3 小时前
不用上传服务器!用 Vue3 + ONNX Runtime Web 在浏览器本地实现智能抠图
运维·前端·图像处理·ai·onnx·canva可画·web性能优化
月月大王的3D日记3 小时前
Three.js 入门系列(9):从零搭一座“闹鬼小屋”
前端·javascript
TingTing3 小时前
Vue3动态组件库建设
前端
码上暴富4 小时前
Cursor / VS Code 自定义文件颜色
前端·vscode