如何在Node.js中使用ES6的import语法?

569

我正在尝试在Node.js中掌握ES6导入语法,并尝试使用此示例提供的语法:

Cheatsheet链接

我正在查找支持表,但我无法找到哪个版本支持新的导入语句(我尝试搜索文本import/require)。我目前正在运行Node.js 8.1.2,并且认为因为cheatsheet是指 .js 文件,它应该可以与 .js 文件一起使用。

当我运行代码(取自cheatsheet的第一个示例)时:

import { square, diag } from 'lib';
我遇到了错误:
SyntaxError: Unexpected token import.
我正在尝试导入的库的引用:
//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

我缺少什么,如何让node识别我的import语句?


5
@Larrydx 类似。Node.js v13 要求在当前目录或其父目录中有 package.json 文件,并且文件中含有 {"type": "module"},才能使用 ES6 的 import 语法。根据文档,当最近的父级 package.json 文件包含顶级字段 "type" 并且值为 "module" 时,文件名以 .js 结尾或没有扩展名的文件将被作为 ES 模块加载。更多信息请参见:https://nodejs.org/api/esm.html#esm_package_json_type_field - Lukas Liesis
1
@Madeo 看起来不再需要转译了吗? - herman
查看对于 Node.js v16 版本中 import() 的支持 - https://nodejs.org/api/packages.html - human
1
已为此创建了一个仓库:https://github.com/jasonjin220/es6-express-rest-api-boilerplate - Jason Jin
显示剩余3条评论
11个回答

605

Node.js 包含了实验性的 ES6 模块支持。

想要了解更多,请参阅:https://nodejs.org/docs/latest-v13.x/api/esm.html#esm_enabling

简而言之:

Node.js >= v13

在 Node.js 13 及以上版本中非常简单。你只需要使用以下两种方式之一:

  • 将文件保存为 .mjs 扩展名;或者
  • 在最近的 package.json 中添加 { "type": "module" }

只需执行上述操作中的一个,即可使用 ECMAScript 模块。

Node.js <= v12

如果你正在使用 Node.js 版本 9.6-12,则需将 ES6 模块保存为带有 .mjs 扩展名的文件,并按照以下方式运行:

node --experimental-modules my-app.mjs

3
有关工作示例,请参见 https://dev59.com/AFcO5IYBdhLWcg3waw6o#50641589 - Manohar Reddy Poreddy
4
我尝试了这种方法,但是在 .mjs 文件内有一个 require 语句,然后抛出错误“require 未定义”或类似的错误。 - Squareoot
15
如果在package.json中包含"type": "module"并使用node --experimental-modules my-app.js这个标志,你也可以选择使用.js文件扩展名。 - JT Houk
2
@JTHouk flag只在v13之前需要,现在已经被删除了。Nodejs v13要求在当前或父目录中有package.json,并且其中包含{"type": "module"},您可以使用ES6导入。从文档中得知:当最近的父级package.json文件包含顶级字段"type"并具有值"module"时,以.js结尾或缺少任何扩展名的文件将作为ES模块加载。在此处查看更多信息:https://nodejs.org/api/esm.html#esm_package_json_type_field - Lukas Liesis
2
只是一个备注 - 在导入模块时,重要的是要写全路径并在文件名后添加 .js 扩展名(这与浏览器的行为相同,但不同于 webpack,webpack 会自动完成扩展名和 /index.js),否则 node 将抛出错误 code:'ERR_MODULE_NOT_FOUND' - bFunc
显示剩余8条评论

347

你也可以使用名为esm的npm包,在Node.js中使用ES6模块。它不需要配置。使用esm,您将能够在JavaScript文件中使用导出/导入。

在终端上运行以下命令

yarn add esm

或者

npm install esm

接下来,在使用Node启动服务器时需要引入此软件包。例如,如果您的Node服务器运行index.js文件,则可以使用以下命令:

require('软件包名称')

完成后,您便可以在代码中使用该软件包提供的功能。

node -r esm index.js
您也可以将其添加到 package.json 文件中,如下所示。
{
  "name": "My-app",
  "version": "1.0.0",
  "description": "Some Hack",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node -r esm index.js"
  },

}

然后在终端中运行此命令以启动您的节点服务器

npm start

查看此链接以获取更多详细信息。


似乎我在导入带星号的内容时遇到了问题。例如:import * as log from 'loglevel' - exoslav
我希望在尝试使用“import * as log”之前,您能够从“loglevel”中导出您的函数。 - Seunope
3
太棒了!不过对Jest不起作用,遗憾。 - ptim
4
对于使用Typescript来说,这是最佳方案,因为ts-node加载器使得.mjs扩展名不可行。命令为node -r esm -r ts-node/register index.ts - Samuel Jaeschke
1
注意:中断 TS 反射元数据:https://github.com/standard-things/esm/issues/809 - morpheus05

178

我只想在JavaScript文件中使用importexport。每个人都说这是不可能的。但是,从2018年5月开始,可以在普通Node.js中使用上述功能,无需任何模块,如Babel等。

下面是一种简单的方法。

创建下面的文件,运行并查看输出结果。

还要记得查看下面的Explanation

文件myfile.mjs

function myFunc() {
    console.log("Hello from myFunc")
}

export default myFunc;

文件 index.mjs

import myFunc from "./myfile.mjs"  // Simply using "./myfile" may not work in all resolvers

myFunc();

运行

node  --experimental-modules  index.mjs

输出

(node:12020) ExperimentalWarning: The ESM module loader is experimental.

Hello from myFunc

说明:

  1. 由于这是实验性模块,因此.js文件被命名为.mjs文件。
  2. 在运行时,您需要将--experimental-modules添加到node index.mjs中。
  3. 使用实验性模块运行时,输出将显示:“(node:12020) 实验警告:ESM模块加载器是实验性的。”
  4. 我有当前版本的Node.js,因此如果我运行node --version,它会给出“v10.3.0”,尽管LTE /稳定/推荐版本为8.11.2 LTS。
  5. 在未来某一天,当功能变得稳定而不再是实验性时,您可以使用.js代替.mjs。
  6. 有关实验性功能的更多信息,请参见:https://nodejs.org/api/esm.html

1
传递这个标志不允许我从样板中运行./bin/www。 - Stepan Yakovenko
2
你确定第五点吗?我认为 .mjs 不是用来表示实验性质的,而是因为模块规范没有提供一种有效的方法来判断一个文件是否应该使用模块加载器或者 require 加载器,只能通过读取代码来判断。 - Erin
4
这在我的Node 10.15.1版本中有效。 - Vineeth Bhaskaran
4
我认为不能强调足够的是,为了使这个程序能够工作(在Windows、Node 10.15.3下),文件后缀名确实必须是.mjs - Raketenolli
2
使用过 Node v12.6.0。但很遗憾看到它仍然是实验性的。 - Mayank
显示剩余3条评论

82

使用 Node.js v12.2.0,我可以像这样导入所有标准模块:

import * as Http from 'http'
import * as Fs from 'fs'
import * as Path from 'path'
import * as Readline from 'readline'
import * as Os from 'os'

与我之前所做的相比:

const
  Http = require('http')
  ,Fs = require('fs')
  ,Path = require('path')
  ,Readline = require('readline')
  ,Os = require('os')

只要在 ECMAScript 模块的 package.json 文件中有这个字段,就可以导入任何模块而无需使用 .mjs 扩展名:

"type": "module"

确保在创建模块的同一文件夹中放置包含package.json文件的文件夹。

如果要导入没有更新为ECMAScript模块支持的模块,可以像这样操作:

// Implement the old require function
import { createRequire } from 'module'
const require = createRequire(import.meta.url)

// Now you can require whatever
const
  WebSocket = require('ws')
  ,Mime = require('mime-types')
  ,Chokidar = require('chokidar')

当然,不要忘记这是使用模块导入实际运行脚本所必需的(在v13.2之后不再需要):

node --experimental-modules my-script-that-use-import.js

同时父文件夹需要这个package.json文件,否则该脚本会因导入语法问题而出错:

{
  "type": "module"
}

如果你想使用的模块还没有更新以支持使用import语法导入,则除了使用require之外别无选择(但使用我上面的解决方案就不是问题了)。

我还想分享这段代码,它实现了模块中缺少的__filename__dirname常量:

import {fileURLToPath} from 'url'
import {dirname} from 'path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)

1
v12.11.1不支持无调整导入。 - Alexey Sh.
我也测试过了,它不起作用。 - Alexey Sh.
2
如果您仔细阅读我的初始评论,您会看到以下单词“无需调整导入”。这意味着没有标志和配置。这是我评论的重点。 - Alexey Sh.
6
@AlexeySh。很奇怪你对我所写的东西发表评论,因为我没有陈述其他任何事情。 - Joakim L. Christiansen
3
从Node v. 13版本开始,不再需要实验性标志。https://nodejs.org/api/esm.html - Robin
显示剩余4条评论

23
如果您在服务器端使用模块系统,则根本不需要使用Babel。要在Node.js中使用模块,请确保:
  1. 使用支持--experimental-modules标志的node版本
  2. 然后将*.js文件重命名为*.mjs
就是这样。
但是,这是一个大问题,虽然您闪亮的纯ES6代码将在像Node.js(例如9.5.0)这样的环境中运行,但仍然需要进行翻译才能进行测试。请记住,Ecma已经声明JavaScript的发布周期将更快,新功能将更加定期地提供。尽管对于像Node.js这样的单个环境来说这没有问题,但对于浏览器环境来说则略有不同。显然,测试框架还有很多工作要做。您仍然需要为测试框架进行翻译。我建议使用Jest
还要注意捆绑框架。您将遇到问题。

2
你有这个的文档吗? - js1568
这是针对Node 9.X的,但我认为它也适用于v8.6。 https://nodejs.org/dist/latest-v9.x/docs/api/all.html#esm_enabling - Zardoz
为什么您建议使用Jest?我也曾遇到过Jest同样的问题。例如,无法将此库与Jest一起使用:https://github.com/mrichar1/jsonapi-vuex/issues/104。您有办法让它能够工作吗? - geoidesic

23

使用:

  "devDependencies": {
    "@babel/core": "^7.2.0",
    "@babel/preset-env": "^7.2.0",
    "@babel/register": "^7.0.0"
  }

文件 .babelrc

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

Node.js应用程序的入口点:

require("@babel/register")({})

// Import the rest of our application.
module.exports = require('./index.js')

请参见如何在Node.js中启用ES6导入


谢谢。我不确定其他答案如何帮助任何人,因为在使用的50% node_modules 项目中打开“modules:true”会失败。 esm 假定您直接从节点调用入口脚本,而不是通过另一个库的命令等。这个答案只是让所有痛苦消失,并给出您想要的语法。 - Dan

15

您可以尝试使用esm

以下是一些介绍:esm


1
这是一个不错的选择,尤其是在TC39仍未确定时。 - Zardoz

12

使用建议的.mjs扩展名可以启用ECMAScript模块,但是在Node.js v12中,您还可以在package.json文件中全局启用此功能。

官方文档指出:

如果最近的父级package.json包含"type":"module",则导入.js和无扩展名文件的语句将被视为ES模块。

{
  "type": "module",
  "main": "./src/index.js"
}

当然,在启动应用程序时仍须提供标志--experimental-modules


8

回到Jonathan002的原问题:

"......哪个版本支持新的ES6 import语句?"

根据Axel Rauschmayer博士的文章,计划在Node.js 10.x LTS中默认支持它(不需要使用实验性命令行标志)。 根据node.js的发布计划截至2018年3月29日,它有可能在2018年4月之后发布,而其LTS将从2018年10月开始。


2
它已经被添加了吗? :? - Rana Tallal
5
@RanaTallal:Node 10 的 API 仍然显示该标志。 - tbking
@RanaTallal: 我也在想同样的问题。Node确实还有这个标志。但是我目前正在学习Angular 6课程,我一直都在使用像import { FormsModule } from '@angular/forms';这样的导入语句。而且Angular是运行在Node上的。我感到困惑。 - Michael
这种被称为ES6的导入方式从10x版本开始得到支持。现在已经是LTS版本,所以你可能正在使用10x或11x版本。这就是你可以无需任何复杂操作就能使用这些导入方式的原因...所以我猜测它们在10.x版本中被移除了。 - Rana Tallal

8

Solution

https://www.npmjs.com/package/babel-register

// This is to allow ES6 export syntax
// to be properly read and processed by node.js application
require('babel-register')({
  presets: [
    'env',
  ],
});

// After that, any line you add below that has typical ES6 export syntax
// will work just fine

const utils = require('../../utils.js');
const availableMixins = require('../../../src/lib/mixins/index.js');

下面是文件*mixins/index.js的定义

export { default as FormValidationMixin } from './form-validation'; // eslint-disable-line import/prefer-default-export

这在我的Node.js CLI应用程序中完美执行。


虽然这样做可以起作用,但这不是答案。最终,这是不必要的。 - Zardoz

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