You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Node与网页端共用ES6导入实现React+HTM的SSR/CSR兼容

问题原因

浏览器原生ES模块不支持裸模块标识符(比如直接写import React from 'react'),它要求模块路径必须是相对路径(.//../)、绝对路径(/开头)或完整URL。而Node.js环境下能解析'react'是因为Node会自动去node_modules目录查找对应的包,这是Node的模块解析机制,浏览器没有这个能力,所以抛出Failed to resolve module specifier "react"错误。

解决方案

下面提供几种让React、htm同时在Node服务端和浏览器客户端正常运行的方案:

方案1:使用CDN ES模块 + 条件导入

服务端保持原有导入逻辑(Node能解析node_modules),客户端改用CDN提供的ES模块版本,可通过环境判断复用代码:

通用逻辑代码(可同时在两端运行)

let React, htm;
// 判断当前环境是服务端还是客户端
if (typeof window === 'undefined') {
  // 服务端:从node_modules导入
  const reactModule = await import('react');
  const htmModule = await import('htm');
  React = reactModule.default;
  htm = htmModule.default;
} else {
  // 客户端:从CDN导入ES模块
  const reactModule = await import('https://cdn.skypack.dev/react');
  const htmModule = await import('https://cdn.skypack.dev/htm');
  React = reactModule.default;
  htm = htmModule.default;
}
const html = htm.bind(React.createElement);

// 示例组件
function App() {
  return html`<h1>Hello from React + htm</h1>`;
}

export default App;

客户端HTML引入

<div id="root"></div>
<script type="module">
  import App from './app.js';
  import ReactDOM from 'https://cdn.skypack.dev/react-dom/client';
  
  const root = ReactDOM.createRoot(document.getElementById('root'));
  root.render(App());
</script>

方案2:使用轻量打包工具处理客户端代码

如果需要生产环境优化(比如tree-shaking、代码压缩),可以用esbuild、Rollup这类轻量打包工具,将客户端代码中的裸模块转换成浏览器可识别的路径:

1. 安装esbuild

npm install esbuild --save-dev

2. 编写打包脚本(build.js)

import esbuild from 'esbuild';

await esbuild.build({
  entryPoints: ['client.js'], // 你的客户端入口文件
  bundle: true,
  outfile: 'dist/client.bundle.js',
  format: 'esm',
  platform: 'browser',
  treeShaking: true,
});

3. 运行打包

node build.js

4. 客户端HTML引入打包后的文件

<div id="root"></div>
<script type="module" src="/dist/client.bundle.js"></script>

服务端代码无需修改,依旧直接从node_modules导入React和htm即可。

方案3:使用浏览器Import Map(现代浏览器支持)

通过importmap在HTML中定义裸模块到CDN地址的映射,这样客户端代码可以和服务端完全一致,无需修改导入语句:

1. 在HTML头部添加importmap

<head>
  <script type="importmap">
    {
      "imports": {
        "react": "https://cdn.skypack.dev/react",
        "react-dom/client": "https://cdn.skypack.dev/react-dom/client",
        "htm": "https://cdn.skypack.dev/htm"
      }
    }
  </script>
</head>

2. 客户端代码(与服务端完全一致)

import React from 'react';
import htm from 'htm';
import ReactDOM from 'react-dom/client';

const html = htm.bind(React.createElement);

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(html`<h1>Hello from React + htm</h1>`);

注意:Import Map仅支持现代浏览器(Chrome 89+、Firefox 108+等),如果需要兼容旧浏览器,需配合polyfill。

内容的提问来源于stack exchange,提问作者berto.dev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 06:00:31