获得意外的令牌“Export”

528

我试图在我的项目中运行一些ES6代码,但是出现了意外令牌导出错误。

export class MyClass {
  constructor() {
    console.log("es6");
  }
}

7
没有足够的信息了解你的环境或配置,无法提供任何帮助。这个错误提示表明 webpack 或 babel 没有正常工作,因为 export 只在 ES6 中可用,而这些模块提供了 ES6 支持。 - Claies
60
你应该使用 module.exports = MyClass,而不是 export class MyClass - onmyway133
1
如果您正在Node> 12中运行此代码,请将文件重命名为myFile.mjs。该“mjs”扩展名应告诉Node您正在使用ES6模块语法。 - Yair Kukielka
20个回答

528

2022年更新

你正在使用EcmaScript模块(ESM或'ES6 Modules')语法,但你的环境不支持它。

在v14.13.0之前的NodeJS版本不支持ESM(export关键字语法),而使用CommonJS模块(module.exports属性语法)。NodeJS v14.13.0及更高版本支持ESM,但必须先启用它。

解决方案:

  • 如果你正在使用支持ESM的NodeJS v14.13.0或更新版本,则可以通过在项目的package.json中设置"type": "module"来启用它。
  • 重新使用CommonJS模块语法(针对旧版本的NodeJS)
  • 考虑在.ts文件中使用TypeScriptts-nodets-node-devnpm包(用于开发时即时转译)编写TypeScript
  • 使用esbuild将ESM转换为CommonJS(npm上的esbuild包),并将其配置为将你的ES6 javascript转译为受你的环境支持的CommonJS目标。(不再推荐使用babel

51
Node.js 何时才能原生支持 import?我以为 v10.0.0 版本已经支持了,但显然并没有。 - chovy
8
可以使用标志"--experimental-modules"来进行实验性支持。文件需要具有".mjs"扩展名。 - Giovanni P.
4
我在使用具有本地模块支持的 Chrome 66 时遇到了这个错误。 - Tom Russell
3
如果有人仍然不清楚CommonJs语法,请查看此链接,可能会有所帮助。https://flaviocopes.com/commonjs/ - L.T.
28
并不是一个有帮助的评论,但对于外部世界的人来说,这一切都非常令人困惑。我只想构建一个用于Web的模块,在CLI中进行测试。我本以为成熟的Node.js环境会默认支持ES6语法。 - NeverEndingQueue
显示剩余8条评论

380

如果您遇到此错误,可能与您将JavaScript文件包含到html页面的方式有关。加载模块时,您必须明确声明这些文件。这是一个例子:

//module.js:
function foo(){
   return "foo";
}

var bar = "bar";

export { foo, bar };

当您像这样包含脚本时:

<script src="module.js"></script>

你会看到以下错误:

Uncaught SyntaxError: Unexpected token export

你需要在引入文件时设置type属性为"module":

<script type="module" src="module.js"></script>

然后它应该像预期的那样工作,您现在可以将您的模块导入到另一个模块中:

import { foo, bar } from  "./module.js";

console.log( foo() );
console.log( bar );

101
与“最受赞同”的答案不同,这个答案实际上解决了问题,并解释了为什么会出现这种情况,而不建议只使用CommonJS方法、APM方法或转换我们的代码……这也是违反w3c标准的例外,该标准期望“类型”是一个有效的媒体类型(即mime类型),所以这是一个意外的发现。谢谢! - Shaun Wilson
5
这样修复了错误,但在使用内联脚本(例如<script>import ...</script>)的 Chrome 67 中,在 import 语句所在行会得到 "Unexpected token {" 错误提示。 - PandaWood
1
@PandaWood 当你从模块中导入时,必须使用<script type="module">import ...</script>。我在最近版本的Chromium中进行了测试。 - Vladimir S.
当从外部网站(如jsDelivr)加载脚本时,您如何使用import ... - pir
1
对于任何阅读此内容的人,请注意所有 type="module" 也将被延迟加载,这在某些情况下可能会破坏您所依赖的加载顺序。 - Kobato
显示剩余3条评论

140

我的看法

导出

ES6

myClass.js

export class MyClass1 {
}
export class MyClass2 {
}

其他.js

import { MyClass1, MyClass2 } from './myClass';

CommonJS替代方案

myClass.js

class MyClass1 {
}
class MyClass2 {
}
module.exports = { MyClass1, MyClass2 }
// or
// exports = { MyClass1, MyClass2 };

其他.js

const { MyClass1, MyClass2 } = require('./myClass');

导出默认值

ES6

myClass.js

export default class MyClass {
}

other.js

import MyClass from './myClass';

CommonJS替代方案

myClass.js

module.exports = class MyClass1 {
}

其他.js

const MyClass = require('./myClass');

2
谢谢!这确实帮助我理解了ES6和CommonJS的导入/导出模式之间的区别。 - camslice
1
谢谢!我在Visual Studio 2022中尝试了上述(导出和ES6)与TypeScript(.ts)文件,但是遇到了“Uncaught SyntaxError:Unexpected token 'export”错误。还有其他人遇到过这个问题吗? - JGV

25

我通过创建一个入口文件来解决了这个问题,如下所示。

// index.js
require = require('esm')(module)
module.exports = require('./app.js')

我导入到app.js内部及其之后的任何文件都可以使用imports/exports,现在只需像这样运行它:node index.js

注意:如果app.js使用export default,则在使用入口点文件时变为require('./app.js').default


1
对于不需要使用Babel、Webpack、Parcel等工具的简单项目,最佳答案是使用Express作为项目的简单/服务器。我在monorepo项目中使用了这种方法,效果非常好。 - mattdlockyer
1
非常好的答案。通过这种方式,我项目中的许多不同模块文件可以紧密链接起来。而且这种方法非常简单! - Lancer.Yan
1
这个答案真的帮了我很多。关于这个问题,有太多复杂或错误的答案,让我简直无法相信。 - Nenad Propadović
这应该是被接受的答案。谢谢! - Stefan D
1
谢谢。这是一个非常简单而且有用的回答! - nmDat

25

此时无需使用Babel(JS已经变得非常强大),您可以简单地使用默认的JavaScript模块导出。 查看完整教程

Message.js

module.exports = 'Hello world';

app.js

var msg = require('./Messages.js');

console.log(msg); // Hello World

6
那么,你如何导出一个类呢? - Sherwin Ablaña Dapito
2
module.exports = class MyClass {} 这段代码有效。 - Marian Klühspies
1
你提到的教程是关于NodeJS而不是Javascript(原生)的。 - Eve

12

若要使用ES6,请添加babel-preset-env

并在您的.babelrc文件中:

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

由于 @ghanbari 的评论,回答已更新以应用 Babel 7.


7
作者已经使用了“babel”标签对这个问题进行了标记,Phil Ricketts的答案确实澄清了问题,这很好,但是这个答案直接解决了作者的问题。 - boycy
"@babel/preset-env" - ghanbari

7
安装Babel包@babel/core@babel/preset-env,这将把ES6转换为commonjs目标,因为node.js不能直接理解ES6目标格式。
使用以下命令安装:npm install --save-dev @babel/core @babel/preset-env 然后在项目的根目录中创建名为.babelrc的配置文件,并添加以下代码: { "presets": ["@babel/preset-env"] }

2
我还需要安装@babel/register,否则我仍然会得到“SyntaxError:Cannot use import statement outside a module”。 - molecular
我很感激有人在这个问答主题中提到了如何使它与ES6兼容!天哪。 - Hari Reddy

2

我曾经有一段时间成功运行了模块,但后来出现了Uncaught SyntaxError: Unexpected token export错误。

结果发现,我加了一个开括号但没有闭括号。类似这样:

if (true) {
/* } missing here */

export function foo() {}

尽管忘记结尾的大错误是遗漏 } 符号,但解析器首先在大括号内找到一个 export,这是不允许的。

export 关键字必须在文件的顶层。

因此:

if (true) {
    export function foo() {}
}

同时,这也是非法的。当解析器遇到这种情况时,它会停止解析,并模糊地宣布export的误用,给出与加载使用export关键字的“非模块”JavaScript文件时相同的错误。它从不报告潜在的缺少括号错误。

我花了很长时间才弄清楚这一点,所以我在这里发布帖子,以帮助任何未来的受害者。

理想情况下,解析器应该报告只允许在文件顶层使用export


1
不错。我相信这会帮助某人重新审视他们的代码。有时候解决方案就在我们面前。 :) - J.M. Janzen

0
只需使用tsx替代node作为运行时,即可使用常规的导入语句,无需将项目切换到type: module并且无需处理type: module的恶劣后果。此外,您还将获得TypeScript支持。

0

CommonJS导入ES6模块的另一种方法是将您的ES6模块文件重命名为.mjs扩展名。这是向后兼容的一种方式。然后在您的CommonJS代码中,您可以像这样导入:

文件顶部导入:

import myClass from "./MyClass.mjs"

或者在代码中的任何位置使用动态导入

const myClass = await import("./MyClass.mjs");

请注意,您的CommonJS文件不需要重命名为.mjs此处有更多信息

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