使用esm.run CDN+importmap引入React18时出现useEffect读取错误求助
问题:使用esm.run CDN + Importmap导入React时出现useEffect读取null错误
我在用jsdelivr支持的ES模块CDN——esm.run,想直接从远程CDN导入React。通过importmap,浏览器应该把import React from 'react'转换成import React from 'https://esm.run/react@18',但运行代码时出现错误:TypeError: Cannot read properties of null (reading 'useEffect'),求解决。
完整代码
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <div id="root"></div> <script type="importmap"> { "imports": { "react": "https://esm.run/react@18", "react-dom": "https://esm.run/react-dom@18" } } </script> <script src="/dist/index.js" type="module"></script> </body> </html>
/dist/index.js (打包后)
(function(){"use strict";try{if(typeof document!="undefined"){var e=document.createElement("style");e.appendChild(document.createTextNode("")),document.head.appendChild(e)}}catch(t){console.error("vite-plugin-css-injected-by-js",t)}})(); import require$$0 from "react"; import ReactDom from "react-dom"; var jsxRuntimeExports = {}; var jsxRuntime = { get exports() { return jsxRuntimeExports; }, set exports(v) { jsxRuntimeExports = v; } }; var reactJsxRuntime_production_min = {}; /** * @license React * react-jsx-runtime.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ var f = require$$0, k = Symbol.for("react.element"), l = Symbol.for("react.fragment"), m = Object.prototype.hasOwnProperty, n = f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner, p = { key: true, ref: true, __self: true, __source: true }; function q(c, a, g) { var b, d = {}, e = null, h = null; void 0 !== g && (e = "" + g); void 0 !== a.key && (e = "" + a.key); void 0 !== a.ref && (h = a.ref); for (b in a) m.call(a, b) && !p.hasOwnProperty(b) && (d[b] = a[b]); if (c && c.defaultProps) for (b in a = c.defaultProps, a) void 0 === d[b] && (d[b] = a[b]); return { $$typeof: k, type: c, key: e, ref: h, props: d, _owner: n.current }; } reactJsxRuntime_production_min.Fragment = l; reactJsxRuntime_production_min.jsx = q; reactJsxRuntime_production_min.jsxs = q; (function(module) { { module.exports = reactJsxRuntime_production_min; } })(jsxRuntime); const jsx = jsxRuntimeExports.jsx; const App = () => { require$$0.useEffect(() => { console.log("我是demo"); }, []); return /* @__PURE__ */ jsx("div", { children: "我是demo rendssder" }); }; ReactDom.render(/* @__PURE__ */ jsx(App, {}), document.getElementById("root")); export { App as default };
解决方案
核心原因
错误源于React 18 ESM模块的导出结构与代码导入方式不匹配:
- React 18的ESM构建中,
useEffect等钩子是命名导出,并非挂载在默认导出对象上; - 代码中手动实现了JSX Runtime,和React官方的导出逻辑冲突,导致
require$$0(即默认导出)无法正确获取React内部属性,最终出现null读取错误。
修复步骤
调整Importmap配置
明确指定需要的导出项,确保esm.run返回正确的导出结构:<script type="importmap"> { "imports": { "react": "https://esm.run/react@18?exports=default,useEffect", "react-dom/client": "https://esm.run/react-dom@18/client?exports=default" } } </script>通过
?exports=参数指定default和useEffect,保证能正确导入所需API。修改导入与渲染逻辑
直接导入命名导出的钩子,同时改用React 18推荐的createRoot渲染方式:import React, { useEffect } from "react"; import { createRoot } from "react-dom/client"; const App = () => { useEffect(() => { console.log("我是demo"); }, []); return <div>我是demo renderer</div>; }; const root = createRoot(document.getElementById("root")); root.render(<App />);移除手动JSX Runtime代码
代码中打包的自定义JSX Runtime会干扰React的正常导出,直接删除这部分逻辑,依赖CDN提供的React环境即可。优化打包配置
若使用Vite等工具打包,需关闭自动注入JSX Runtime的配置,避免打包时混入多余代码,确保完全依赖CDN提供的React模块。
内容的提问来源于stack exchange,提问作者Hanwen D.
相关产品推荐
相关产品推荐

