语法错误:无法在模块外部使用导入语句。

639

我有一个 ApolloServer 项目出了些问题,所以我想更新它,并在使用最新的 Babel 时遇到了一些问题。我的 "index.js" 文件如下:

require('dotenv').config()
import {startServer} from './server'
startServer()

当我运行它时,我收到了错误信息:

SyntaxError: Cannot use import statement outside a module

首先,我尝试了一些方法来说服TPTB*这是一个模块(但没有成功)。所以我把"import"改成了"require",这样就可以了。

但现在我有大约两打其他文件中的"imports"也给我同样的错误。

*我确定我的问题的根源是我甚至不知道是什么在抱怨这个问题。我有点假设它是Babel 7(因为我从Babel 6过来,我必须改变预设),但我不是100%确定。

我找到的大部分解决方案似乎都不适用于纯Node。就像这个:

ES6 module Import giving "Uncaught SyntaxError: Unexpected identifier"

说通过添加"type=module"来解决,但这通常会放在HTML中,而我没有HTML。我还尝试使用我的项目的旧预设:

"presets": ["es2015", "stage-2"],
"plugins": []

但是这给我带来了另一个错误:“错误:插件/预设文件不允许导出对象,只能导出函数。”

以下是我开始使用的依赖项:

"dependencies": {
  "@babel/polyfill": "^7.6.0",
  "apollo-link-error": "^1.1.12",
  "apollo-link-http": "^1.5.16",
  "apollo-server": "^2.9.6",
  "babel-preset-es2015": "^6.24.1",

3
你好,我现在遇到了同样的问题。你能否分享一下你的依赖关系?或者甚至提供一下你更新前后的差异。这样我就可以检查一下我的依赖关系,看是否存在可能引起问题的类似软件包。 - stewo
6
我刚刚将所有的“imports”替换为“requires”,现在一切都很好了。虽然有点傻,但目前没必要费力去弄明白它。不过我会更新原来的代码,并加入依赖项。如果你有任何线索,我会与我的原始代码进行核对。 - user3810626
4
CommonJS语法(require和module.exports)是Node的原始格式,Webpack也支持它,但ES6模块语法(export,import)是较新的方式,现在Node和Webpack都支持它。我看到Node现在也支持import,但许多教程仍旧展示require用于纯Node内容,因此在Node中最好使用那个语法。 - Ted Fitzpatrick
8
"type":"module"并不能解决我的问题,而且有超过一百个importsrequire的语法与imports不同,不是很容易替换。你能否给出一个将imports替换为requires的示例? - Jeb50
权力的巨头:这是一种(略微)滑稽的说法,意味着“我不知道这件事在哪里被决定”。 - user3810626
显示剩余6条评论
36个回答

746

请确认您已安装最新版本的Node.js(或至少13.2.0+)。然后按照文档中所述执行以下操作之一:

选项1

在最近的父级package.json文件中,添加顶级"type"字段,并将其值设置为"module"。这将确保所有.js.mjs文件都被解释为ES模块。您可以通过使用.cjs扩展名将单个文件解释为CommonJS

// package.json
{
  "type": "module"
}

选项2

使用 .mjs 扩展名显式命名文件。所有其他文件,例如 .js,将被解释为 CommonJS,如果在 package.json 中未定义 type,则为默认设置。


3
如果我使用这个,然后更改路径以包括所需文件的“js”,然后更改所需文件中的导出语句格式,并且将所有我从“import”更改的“require”语句取出——因为现在“require”是未知的——这将起作用,所以我会接受这个答案。 - user3810626
154
如果问题出现在 node_modules/ 目录下,这不是一个真正的选项,对吗?有什么想法来解决这种情况吗? - Brandon
5
module.exports = { presets: ['@babel/preset-env'], };这段代码的意思是将 JavaScript 代码通过 Babel 转换成向后兼容的代码。其中,@babel/preset-env 是一个预设,它根据你的目标浏览器或运行环境,自动确定需要使用哪些插件来进行转换。 - Cocuba
9
如果你在运行 .ts 文件,则此解决方案无效。如果你能按照这个回答 https://dev59.com/l1MH5IYBdhLWcg3wxiY9#65058291 下面所述,仅使用 nodemon 替代 node,那将有望节省人们浪费的半天时间。另外,根据 https://dev59.com/l1MH5IYBdhLWcg3wxiY9#61947868 安装 ts-node 也是一种解决方法。 - JimmyTheCode
1
我在一个源目录中有一个隐藏的 package.json 文件。我不得不运行 find . -name package.json 命令,然后我的导入操作才开始工作。 - James M. Lay
显示剩余9条评论

192

如果有人在使用TypeScript时遇到了这个问题,解决方法就是改变

    "target": "esnext",
    "module": "esnext",

    "target": "esnext",
    "module": "commonjs",

在我的tsconfig.json中,我曾经认为"esnext"是最好的选项,但那只是一个错误的想法。

1
如果您正在使用 babel-node,那么您还需要使用 --extensions 选项,例如 babel-node --extensions \".ts,.tsx\" src/index - Matt Browne
3
该死,它让我免去了很多麻烦。我犯了同样的错误,以为“esnext”是最好的。 - Jake
4
你的回答是可行的。你能解释一下为什么我们需要进行这样的调整吗?@Dr-Bracket 谢谢 - Dolphin
1
这对我解决了问题。谢谢。您能否详细说明为什么 esnext 不是最佳选项? - shaangon
1
你能再详细解释一下吗,哈哈。 - gunslingor
显示剩余3条评论

114

如果你像我一样在阅读答案时感到困惑,在你的package.json文件中,添加以下代码:

"type": "module"

放在上面的位置,如下所示:

{
  "name": "my-app",
  "version": "0.0.0",
  "type": "module",
  "scripts": { ...
  },
  ...
}

1
tx,你知道在哪里可以找到package.json吗?我正在使用NetBeans。我还在我的MacBook上搜索了package.json,但是看到了很多package.json文件。有什么建议吗? - alex
1
嗨,Alex,自从我上次参与Java项目以来已经有一段时间了,但我希望这个链接可以给你一个线索,告诉你在哪里找到package.json文件:https://dev59.com/lp3ha4cB1Zd3GeqPcfk3 - L. Theodore Obonye
7
但是当我添加 type: module 后,如果我使用 require,我会得到一个错误:ReferenceError: require 在 ES 模块范围内未定义,您可以使用 import 代替 - Nam Lê Quý

83
根据官方文档

仅在ES模块中允许使用import语句。对于类似的CommonJS功能,请参见import()。

为使Node.js将您的文件视为ES模块,您需要 (启用):
  • 将"type": "module"添加到package.json中
  • 向Node.js调用添加"--experimental-modules"标志

47
2020 年更新:--experimental-modules 不再需要。 - Cullub
8
不是说这个答案是错误的,我看过相同的文档。但我不明白使用import()访问CommonJS中的es6模块建议如何有用。它是异步的,因此无法在文件级别上导入任何内容。这使得尝试从CommonJS访问es6模块非常痛苦。考虑到主要的单元测试框架Jasmine、Jest等都无法很好地处理这个问题,这让我觉得,在没有更好的互操作支持之前,整个节点es6的情况对我来说似乎是半成品的,但我很想证明自己是错的。 - Neutrino
加到哪里?我们能否得到一个代码示例? - john k
1
但是当我添加 type: module 后,如果我使用 require,我会得到一个错误:ReferenceError: require 在 ES 模块范围内未定义,您可以使用 import 代替 - Nam Lê Quý
请查看此回答,其中使用了import()的代码示例。 - Andrew Philips
显示剩余2条评论

47

我遇到了同样的问题,而且更糟的是:我需要同时使用“import”和“require”。

  1. 一些更新的ES6模块只能使用import
  2. 一些CommonJS可以使用require

以下是对我有效的方法:

  1. 将您的js文件转换为.mjs,如其他答案所建议。

  2. 由于ES6模块中未定义“require”,因此可以通过以下方式定义它:

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

    现在可以按照惯常方式使用'require'。

  3. ES6模块使用import,CommonJS使用require

    1. 一些有用的链接:Node.js官方文档import和require的区别Mozilla关于import的详细文档


阅读Node.js文档,import将处理ESCommonJS模块; require仅处理CommonJS模块。https://nodejs.org/api/esm.html#import-expressions - Thomas David Kehoe

19

我曾经遇到过同样的问题,以下方法解决了它(使用 Node.js 12.13.1):

  • .js 文件扩展名更改为 .mjs
  • 在运行应用程序时添加 --experimental-modules 标志。
  • 可选:在您的 package.json 中添加 "type": "module"

更多信息:https://nodejs.org/api/esm.html


16

首先,我们将安装@babel/cli, @babel/core和@babel/preset-env

npm install --save-dev @babel/cli @babel/core @babel/preset-env

然后我们将创建一个 .babelrc 文件来配置 Babel
touch .babelrc

这将托管我们可能想要配置 Babel 的任何选项:

{
  "presets": ["@babel/preset-env"]
}

最近对Babel进行了更改,您需要在Node.js运行之前对您的ES6代码进行转译。因此,我们将在文件“package.json”中添加我们的第一个脚本——build。
"scripts": {
  "build": "babel index.js -d dist"
}

然后我们将在文件package.json中添加启动脚本。

"scripts": {
  "build": "babel index.js -d dist", // replace index.js with your filename
  "start": "npm run build && node dist/index.js"
}

现在让我们开始启动服务器。

npm start

在开发中,这是否与nodemon热重载一起工作? - Fed

16

我尝试了所有的方法,但都没有成功。

我从GitHub上得到了一个参考资料。

为了在Node.js中使用TypeScript导入,我安装了以下包。

1. npm i typescript --save-dev

2. npm i ts-node --save-dev

不需要在 package.json 中指定 type: module

例如,

{
  "name": "my-app",
  "version": "0.0.1",
  "description": "",
  "scripts": {

  },
  "dependencies": {
    "knex": "^0.16.3",
    "pg": "^7.9.0",
    "ts-node": "^8.1.0",
    "typescript": "^3.3.4000"
  }
}

我曾经遇到过同样的问题。作为替代方案,只需使用nodemon运行package.json脚本即可,而不是使用node。这也可以避免安装另一个包(假设您已经在运行nodemon)。来源:https://dev59.com/l1MH5IYBdhLWcg3wxiY9#65058291 - JimmyTheCode
他们应该放到devDependencies中。 - OSGI Java

10

Node v14.16.0
对于那些尝试过 .mjs 但遇到问题的人:

Aviator@AW:/mnt/c/Users/Adrian/Desktop/Programming/nodejs_ex$ node just_js.mjs
file:///mnt/c/Users/Adrian/Desktop/Programming/nodejs_ex/just_js.mjs:3
import fetch from "node-fetch";
       ^^^^^
    
SyntaxError: Unexpected identifier

尝试过 import fetch from "node-fetch";
和尝试过 const fetch = require('node-fetch'); 的人们。

Aviator@AW:/mnt/c/Users/Adrian/Desktop/Programming/nodejs_ex$ node just_js.js
(node:4899) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
/mnt/c/Users/Adrian/Desktop/Programming/nodejs_ex/just_js.js:3
import fetch from "node-fetch";
^^^^^^

SyntaxError: Cannot use import statement outside a module  

对于那些尝试将"type": "module"添加到package.json中,但仍然看到错误的人,

{
  "name": "test",
  "version": "1.0.0",
  "description": "to get fetch working",
  "main": "just_js.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "MIT"
}

我能够毫无问题地切换到axios。

import axios from 'axios'; <-- 放在文件顶部。
示例:

axios.get('https://www.w3schools.com/xml/note.xml').then(resp => {
    console.log(resp.data);
});

这对我有用。node-fetch 怎么回事? - abc

9
我发现这个链接中2020年的更新对回答这个问题很有帮助,同时还告诉你为什么会这样做: 使用Node.js require vs. ES6 import/export 以下是摘录:
2020年更新 自Node v12以来,默认启用了对ES模块的支持,但在撰写本文时它仍处于试验阶段。包括节点模块的文件必须以.mjs结尾,或者最近的package.json文件必须包含"type":"module"。Node文档提供了更多信息,也涉及CommonJS和ES模块之间的互操作性。”

3
我发现这个答案对OP来说是最清晰的解释。只是,假设我正在编写一个应用程序,而不是一个模块,那么“无法在模块外部使用import语句”是否意味着我不能使用import语句导入任何东西,因为我在模块外部,对吧?这仍然令人困惑。对于“包括节点模块的文件必须以.mjs结尾,或者最近的package.json文件必须包含"type":"module"”,我正在编写一个应用程序,而不是一个模块,为什么我需要将我的应用程序命名为mjs或将我的包设置为模块? - xpt
1
@xpt 嗨。所以,你不能将应用程序和模块进行比较。我认为你可能会将其视为节点模块,这似乎是你的程序中的程序。这并不是模块的真正含义。让我澄清一下什么是模块,我认为这样可以解决你的困惑: - David S
1
@xpt 这样想,你正在编写一个应用程序,并且正在处理名为main-file.js的文件。 你想从另一个名为some-other-file.js的文件中导入一个函数。 some-other-file.js非常大,而你并不需要整个文件。 你只需要文件中的一个函数。 现在,如果some-other-file.js和main-file.js是模块(.mjs),你可以“导入”所需的函数,例如:import someFunction from './some-other-file'。 总之,模块是可以通过其函数分离的js文件,因此它可以导出特定的函数而不是整个文件。 这有意义吗? - David S
1
谢谢你的解释,David。所以我理解some-other-file.js是一个模块,可以导出特定函数而不是整个文件。但是,main-file.js也是一个模块吗?不是的,它只是我的应用程序的主文件。由于它不是一个模块,"不能在模块之外使用import语句"规则是否适用于main-file.js呢? - xpt
无论如何,我认为我最好还是提一个新问题--https://stackoverflow.com/questions/71240736/ - xpt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接