在使用Express、EJS的Node.js项目中,如何解决客户端JS模块导入函数的兼容问题?
解决方案
你完全不需要为了客户端静态JS的导入需求,强行在package.json里加全局type: module配置——服务端的Node模块系统和浏览器端的JS执行环境是两套完全独立的逻辑,二者没有强绑定关系,乱开配置反而会搞坏现有服务端的运行。
零改动现有服务端代码的实现方式
- 保持你现有的
app.js中所有基于require的CommonJS写法完全不变,不修改package.json的任何配置,服务端的Express、EJS逻辑可以正常运行,不受任何影响。 - 你的客户端JS文件属于静态资源,是被Express静态中间件读取后直接响应给浏览器的,Node本身不会加载、解析这些文件里的
import/export语法,完全不需要适配Node的模块规则。 - 两个互相导入的客户端JS文件,正常写ES Module的导出、导入逻辑即可:
- 导出的文件用
export关键字暴露需要共享的函数 - 导入的文件用
import语法引入对应函数
- 导出的文件用
- 页面中引入入口JS时,使用
<script type="module">标签替代普通的<script>标签,浏览器就会自动将该资源识别为ES模块,正常解析内部的导入逻辑。
举个实际操作的例子:
- 在你当前挂载的静态资源目录下新建两个文件:
public/tool.js内容为:export function formatTime(time) { return new Date(time).toLocaleString() }public/index.js内容为:import { formatTime } from './tool.js' console.log(formatTime(Date.now()))- 在对应的HTML/EJS模板里加一行:
<script type="module" src="/public/index.js"></script>- 整个流程不需要改任何服务端代码,打开页面控制台就能看到函数正常执行。
- 你当前配置的Express静态资源中间件,默认对.js格式文件返回的
Content-Type是application/javascript,完全符合浏览器对ES模块的响应头要求,不需要额外调整静态资源配置。
后续需要混合用CommonJS和ESM的折中方案
如果后续你确实有部分服务端代码想写ESM语法,也不需要全局开启type: module:
- 将需要使用ESM语法的服务端文件后缀改为
.mjs,Node解析时会自动将这类文件识别为ES Module,不受package.json的type配置影响。 - 原有基于CommonJS的服务端文件保持
.js后缀,Node会按默认的CommonJS规则解析,两类文件之间可以正常互相导入,不会出现兼容问题。
常见踩坑提示
- 浏览器端ES模块的导入路径必须写全相对路径和文件后缀,比如必须写
./tool.js,不能简写为./tool,和Node的模块解析逻辑不同,路径缺省会直接触发404错误。 - 不要为了客户端的需求全局开启
type: module,开启后所有未改后缀的.js服务端文件都会被Node识别为ESM,原有的require写法会直接报错,完全属于多余操作。
内容的提问来源于stack exchange,提问作者user15237776
相关产品推荐
相关产品推荐

