为什么新版本React可以忽略全局导入

460 阅读2分钟

为什么新版本React可以忽略全局导入

  • 很多时候,我们只写一个纯组件,并没有用到React的一些api,但是仍然需要在模块中导入React
  • 而不导入会报错,React找不到
// 纯组件示例
const App = () => {
  return (
    <h1>
      hello<span style={{ color: "red" }}>world</span>
    </h1>
  );
};

export default App;

看起来没什么问题,为什么会报错

  • 下载依赖:npm i @babel/core @babel/plugin-transform-react-jsx -D
  • 报错通常是在编译过程中产生的,我们使用babel编译看下
const babel = require("@babel/core");
const sourceCode = `
<h1>
    hello<span style={{color:'red'}}>world</span>
</h1>
`;
const result = babel.transform(sourceCode, {
  plugins: [["@babel/plugin-transform-react-jsx", { runtime: "classic" }]],
});

console.log(result.code);

// 编译结果
//   /*#__PURE__*/
//   React.createElement("h1", null, "hello", /*#__PURE__*/React.createElement("span", {
//    style: {
//        color: 'red'
//    }
//    }, "world"));

  • 可以看到编译完成,将我们的jsx语法转换成了React.createElement这种方式,这个函数会返回虚拟dom
  • 那么现在我们应该可以了解为什么会报错了,因为使用到了React上的createElement方法,而React并未引入,那么报错是理所当然的

如何使其在不导入的情况下也不报错

  • 将上述例子中的runtime的值改为automatic
// 结果

import { jsx as _jsx } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";

/*#__PURE__*/
_jsxs("h1", {
  children: ["hello", /*#__PURE__*/_jsx("span", {
    style: {
      color: 'red'
    },
    children: "world"
  })]
});
  • 可以看到,它将createElement转换成了jsx,且自动帮我们引入了jsx这个方法,所以不会出现找不到的情况

简单实现下jsx转义

  • 源码目录:
    • 在18中,将babel编译将key放到了第三个参数中
// react/jsx-dev-runtime.js
export { jsxDEV } from "./src/jsx/ReactJSXElement";

// react/src/react/src/jsx/jsx-dev-runtime.js
import { REACT_ELELEMT_TYPE } from "shared/ReactSymbols";
import hasOwnProperty from "shared/hasOwnProperty";
const RESERVED_PROPS = {
  key: true,
  ref: true,
  __self: true,
  __source: true,
};
function hasValidRef(config) {
  return config.ref !== undefined;
}
function ReactElement(type, key, ref, props) {
  //虚拟dom
  return {
    $$typeof: REACT_ELELEMT_TYPE,
    type,
    key,
    ref,
    props, // 属性
  };
}
export function jsxDEV(type, config, maybeKey) {
  let propName; // 属性名
  const props = {}; // 属性对象
  let key = null; // 每一个虚拟dom可以有一个可选的key属性,用来区分一个父节点下的不同子节点
  let ref = null; // 引入,后面可以通过这实现获取真实dom的需求
  if (maybeKey !== undefined) {
    key = maybeKey;
  }
  if (hasValidRef(config)) {
    ref = config.ref;
  }
  for (propName in config) {
    if (
      hasOwnProperty.call(config, propName) &&
      !RESERVED_PROPS.hasOwnProperty(propName)
    ) {
      props[propName] = config[propName];
    }
  }
  return ReactElement(type, key, ref, props);
}