Node.js 中使用 import 报错怎么办?配置 type:module 指南

作者:岩茶时间:2026-10-30 18:47:38浏览:320

针对 Node.js 中执行 import 语句出现 SyntaxError: Cannot use import statement outside a module 报错的排查指南,详细介绍配置 package.json 中 type: module 的具体步骤、原理、运行验证及注意事项。

在 Node.js 环境中直接书写 ES Module 语法(如 import fs from 'fs')时,终端常常会抛出 SyntaxError: Cannot use import statement outside a module 的经典报错。出现这一错误的原因在于 Node.js 默认将 .js 文件视作 CommonJS 规范模块,必须通过在项目的 package.json 中声明 "type": "module",或者将文件后缀改为 .mjs,Node.js 才能正确启用 ES 模块解析器解析代码。本文将提供清晰的排查流程、具体配置步骤及后续开发中的常见细节。

一、为什么 Node.js 运行 import 会抛出错误?

Node.js 历史上一直采用 CommonJS(CJS)作为原生模块系统,使用 require() 加载依赖、通过 module.exports 导出功能。随着 ECMAScript 官方模块标准(ES Module,简称 ESM)的确立,现代前端生态普遍采用了 import 和 export 语法。为了维持向后兼容性,Node.js 默认将未明确标记的文件解析为 CommonJS,当解析器遇到 import 关键字时就会立即报错中止。

典型报错信息示例:
(node:12345) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
SyntaxError: Cannot use import statement outside a module

实际上,错误输出中已经给出了 Node.js 的官方解决建议:要么在 package.json 中配置 "type": "module",要么使用 .mjs 扩展名。下图直观展示了 Node.js 遇到 JavaScript 文件时的模块系统决策逻辑:

Node.js 中使用 import 报错怎么办?配置 type:module 指南
图 1:Node.js 运行时对不同文件扩展名及配置的模块解析决策路径

二、核心操作:配置 package.json 的 type: module 步骤

解决此问题最标准、最符合现代工程习惯的方法,是在项目根目录的 package.json 中显式声明 "type": "module"。配置后,该目录下及所有子目录下的所有 .js 文件都将被默认解析为 ES Module。

1. 初始化或打开 package.json

如果当前目录下还没有 package.json 文件,可以在命令行终端中切换到项目目录并执行初始化命令:

npm init -y

这会在当前目录下快速生成一份基础的 package.json 文件。

2. 在配置文件中添加 "type": "module"

使用任意文本编辑器打开 package.json,在根层级属性中添加 "type": "module" 字段。请注意检查 JSON 逗号语法是否完整正确:

{
  "name": "my-node-project",
  "version": "1.0.0",
  "description": "Node.js ES Module project",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "start": "node index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

3. 编写代码并进行验证

在同目录下新建 index.js,编写使用 import 语法的代码:

// index.js
import os from 'node:os';

console.log('系统平台:', os.platform());
console.log('CPU 架构:', os.arch());
console.log('Node.js 原生 ES Module 运行成功!');

在终端运行命令:

node index.js

运行结果(示例):

系统平台: darwin
CPU 架构: arm64
Node.js 原生 ES Module 运行成功!

控制台正常打印出系统信息且没有抛出语法错误,表明 type: module 配置已经生效。

完整操作流程总结如图 2 所示:

Node.js 中使用 import 报错怎么办?配置 type:module 指南
图 2:在项目中配置 type: module 并执行验证的四个标准步骤

三、配置 type: module 后的重要差异与注意事项

一旦在项目中设置了 "type": "module",Node.js 对文件的处理规则将发生根本性转变。开发者如果不了解这些规则,很容易遇到新的二次报错:

1. 本地模块导入必须包含文件扩展名

在 CommonJS 中,我们可以写 const utils = require('./utils'),Node.js 会自动按顺序探测 .js、.json 或文件夹下的 index.js。但在原生 ES Module 规范中,为了与浏览器标准对齐,相对路径导入必须写出明确的文件扩展名:

// ❌ 错误做法:会抛出 ERR_MODULE_NOT_FOUND
import { calculateSum } from './utils';

// ✔ 正确做法:显式加上 .js 后缀
import { calculateSum } from './utils.js';

2. 不再提供 __dirname 与 __filename 全局变量

在 CommonJS 模块中,每个文件都有内置的 __dirname(当前文件所在目录绝对路径)和 __filename。在 ES Module 中,这两个变量已不存在。如果直接访问会提示 ReferenceError: __dirname is not defined in ES module scope。现代标准的替代方式是使用 import.meta.url 结合 url 模块进行转换:

import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';

// 获取当前模块的绝对文件路径与目录路径
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

console.log('当前目录:', __dirname);
console.log('当前文件:', __filename);

3. 不允许直接使用 require()

在 type: module 模式下,直接调用 require('lodash') 会提示 ReferenceError: require is not defined in ES module scope。正确的做法是使用 import 语法:

import lodash from 'lodash';

如果确实需要引入某些古老的、仅支持 CommonJS 且无法通过默认 import 正常解构的第三方包,可以使用 Node.js 提供的 createRequire 工具创建专用的 require 函数:

import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);

const legacyPkg = require('./legacy-commonjs-pkg.cjs');

4. 导入 JSON 文件需要导入断言(Import Attributes)

在 CommonJS 中可以直接 require('./data.json')。而在 ES Module 中,导入 JSON 必须显式注明类型属性(Node.js 18+ 推荐写法):

import configData from './config.json' with { type: 'json' };
console.log(configData.version);
Node.js 中使用 import 报错怎么办?配置 type:module 指南
图 3:CommonJS 与 ES Module 核心特性与常用语法对照

四、替代方案:不改 package.json 时如何使用 import?

如果处于一个大型遗留项目中,或者临时只写一个简单的测试脚本,不希望因为改动 package.json 的 type 字段而影响已有的大量 CommonJS 代码,可以选用以下方案:

方案 A:直接使用 .mjs 扩展名

Node.js 规定:无论 package.json 如何配置,凡是以 .mjs 结尾的文件都会无条件强制使用 ES Module 解析。可以将文件命名为 demo.mjs,然后编写代码:

// demo.mjs
import path from 'node:path';
console.log('解析路径:', path.resolve('.'));

终端直接运行 node demo.mjs 即可,无需任何配置文件辅助。

方案 B:混合项目中使用 .cjs 保留旧模块

如果已经把 package.json 设为了 "type": "module",但项目中仍有少数脚本需要维持 CommonJS 规范(例如某些构建工具配置文件),可以将这些特定的文件后缀命名为 .cjs。在 ESM 项目中,.cjs 文件依然可以使用 require() 和 module.exports。

五、排查清单与综合选型总结

在处理 Node.js 模块语法错误时,可以参照以下排查与解决流程进行快速定位与处置:

Node.js 中使用 import 报错怎么办?配置 type:module 指南
图 4:Node.js 模块错误排查与修复决策流程总结
结语建议:
在 Node.js 中使用 import 遇到报错,本质上是模块体系从 CommonJS 切换到 ES Module 时的解析器声明问题。通过在 package.json 中添加 "type": "module",即可直接解除 import 语法的限制。在后续开发过程中,牢记“相对路径带上 .js 后缀”以及“使用 import.meta.url 替代 __dirname”两条关键规则,就能平稳顺畅地享受现代 JavaScript 模块系统带来的开发体验。
作者最新文章
相关文章
更多