"require(x)"和"import x"之间的区别

569

我刚开始着手开发一个小的node项目,将和MongoDB进行交互。然而,尽管我已经通过npm正确安装了它们,但是我似乎无法正确地导入相关的node模块。

例如,下面的代码会抛出一个错误,告诉我"express没有默认导出":

import express from "express";

但是,这段代码有效:

const express = require("express");

所以我的问题是,import和variable/require方法在功能上有什么区别? 我想修复项目中出现的导入问题,因为这似乎很可能会在今后引起其他问题。


3
如果不包含用于express的类型定义,第一种形式将没有意义 - 在这种情况下,您可以使用第二种形式,但变量 express 的类型将是 any。您可以从这里 https://www.npmjs.com/package/@types/express 包含定义。 - fs_
@Ryall 这是一个不同的问题。请注意,import x = require('x')var x = require('x') 不同。 - Tomasz Gawel
8个回答

662

这幅简单的图片将帮助你理解 requireimport 之间的区别。

enter image description here

除此之外,

使用 require 不能选择性地仅加载需要的部分,但是使用 import 可以选择性地仅加载需要的部分,这可以节省内存。

对于 require,加载是同步的(逐步进行),而对于 import,它可以是异步的(不等待前一个导入),因此它可能会比 require 运行得更快一些。


52
影响代码的最大差异在于CommonJS模块中的exports是“计算的”,而ESM模块中的exports是静态的(预定义的)。JS可以在解析代码之后确定ESM模块中的exports(尚未运行代码)。在CommonJS模块中,仅当模块实际运行时,exports才会被识别,并且只有在模块初始化代码完成运行时,才会看到分配给module.exports的内容。仅这一差异就会导致兼容性问题,试图使单个模块适用于ESM和CommonJS。 - jfriend00
4
ESM模块对于打包工具更加友好,但对于开发者来说更加限制,因为在ESM模块中无法使用计算导出。 - jfriend00
3
"ESM"代表ECMAScript模块。我提到这一点是为了那些主要编程语言可能不是JavaScript的读者,就像我一样。以下是JavaScript模块声明的快速概述:https://dev.to/iggredible/what-the-heck-are-cjs-amd-umd-and-esm-ikm - camelCase
3
“你不能通过require选择性地仅加载所需的部分”:至少在Node.js中,你可以通过require选择性地仅加载所需的部分。例如:const { Buffer } = require('node:buffer');。” - Nagev

161
requireimport最主要的区别在于,require会自动扫描node_modules以查找模块,但import(来自ES6)不会。
大多数人使用babel来编译importexport,这使得import的行为与require相同。
未来版本的Node.js可能会支持import本身(实际上,试验性版本已经支持了),根据Node.js的说明,import不支持node_modules,它基于ES6,并且必须指定模块的路径。
因此,我建议您不要在babel中使用import,但是这个特性尚未确定,未来可能会支持node_modules,谁知道呢?

以下是一个示例,说明babel如何将ES6的import语法转换为CommonJS的require语法。
假设文件app_es6.js包含以下导入内容:
import format from 'date-fns/format';

这是一个指令,用于从Node包date-fns 导入format函数。

相关的package.json文件可能包含如下内容:

"scripts": {
    "start": "node app.js",
    "build-server-file": "babel app_es6.js --out-file app.js",
    "webpack": "webpack"
}

相关的.babelrc文件可能是这样的:

{
    "presets": [
        [
            "env",
            {
                "targets":
                {
                    "node": "current"
                }
            }
        ]
    ]
}

build-server-file 脚本在 package.json 文件中定义,用于指示 babel 解析 app_es6.js 文件并输出 app.js 文件。

运行 build-server-file 脚本后,如果打开 app.js 并查找 date-fns 导入,你会发现它已被转换成如下形式:

var _format = require("date-fns/format");

var _format2 = _interopRequireDefault(_format);

对于大多数人来说,那个文件的大部分内容都是无法理解的,但计算机可以理解。


作为一个示例,以演示如何创建并导入模块到您的项目中,如果您安装了 date-fns 并打开 node_modules/date-fns/get_year/index.js,您可以看到它包含:

var parse = require('../parse/index.js')

function getYear (dirtyDate) {
  var date = parse(dirtyDate)
  var year = date.getFullYear()
  return year
}

module.exports = getYear

使用上述 Babel 过程后,您的 app_es6.js 文件可能包含以下内容:

import getYear from 'date-fns/get_year';

// Which year is 2 July 2014?
var result = getYear(new Date(2014, 6, 2))
//=> 2014

然后Babel将把这些导入转换为:

var _get_year = require("date-fns/get_year");

var _get_year2 = _interopRequireDefault(_get_year);

相应地处理对该函数的所有引用。


2
啊啊啊啊。这个项目没有安装Babel,这就解释了一切。我以为ES6的导入/导出已经可以使用了,但现在我明白Babel只是将所有东西都改成了require - austinthemassive
现在坚持使用require。将来您可以随时更改它,而不会有任何问题。 - Juan
6
“import 不支持 node_modules”是什么意思? - PrivateOmega
6
importrequire都会扫描node_modules以查找语句中指定的包。 require将加载分配给包中的module.exports的任何内容到其分配的变量中,如果没有声明左侧,则加载到全局作用域中。但是,import仅按名称加载es6默认导出,除非所有内容都分配给别名:import * as X from 'pkg'。您也可以使用对象解构导入没有默认值的es6包:import { X } from 'pkg'。如果将整个包(包括所有导出项)导入全局作用域,则与require相同:import 'package' - Rik
在最近的 node 版本中,似乎不再需要在 package.json 中使用 build-server-file babel 命令。 - Timo

64

让我举一个例子来展示如何使用 require 和 import 导入 express 模块

-require

var express = require('express');

-导入

import * as  express from 'express';
使用上述任意语句后,我们将拥有一个名为“express”的变量。现在我们可以定义'app'变量为:
var app = express(); 

因此,我们在“CommonJS”中使用“require”,在“ES6”中使用“import”。

有关“require”和“import”的更多信息,请阅读以下链接。

require - 在Node.js中要求模块:您需要了解的一切

import - Node.js中ES6模块的最新消息


8
这绝对是正确的答案。海报在使用es6的import语句时遇到问题,并被express没有默认导出的错误所困惑。这个答案提供了解决方案。模块有多个(甚至单个)导出,没有定义default export都需要将所有导出分配给一个命名变量,就像这个答案所解释的:import * as whatever from 'package'; - Rik
同意,这应该是最佳答案。作为对先前评论的修正,您可以在node_modules中检查您所依赖的包的代码(入口点将列在其package.json main键下)。类似于module.export = whatever的东西意味着您可能需要将其导入为import * as whatever from 'package'; - zr0gravity7

37

我来简要解释一下,

  • 导入和导出是ES6(下一代JS)的特性。
  • require是从其他文件导入代码的老派方法。

主要区别在于require会调用或包含整个JS文件,即使你不需要其中某些部分。

var myObject = require('./otherFile.js'); //This JS file will be included fully.

相对于导入(import),你可以仅提取所需的对象/函数/变量。

import { getDate }from './utils.js'; 
//Here I am only pulling getDate method from the file instead of importing full file

另一个重要的区别是,您可以在程序中的任何位置使用 require,而 import 必须始终位于文件顶部。

编辑:在最新的Node版本中,您可以使用解构方式,代码如下所示:

const { getDate } = require('./date.js');

8
你可以使用对象解构配合 require 来进行操作,例如:const { getDate } = require('./utils.js'); - d512
1
在最新的 Node 版本中是可以使用的,但在早期版本中不行。 - pranav shinde
1
自从版本6(2016年4月)起,Node开始支持解构导入。 - Developer Dave
2
你的总体解释过于简单,不够准确。即使是关于在程序中任何地方使用 require 而只在文件顶部使用 import 的说法也掩盖了重要细节。当你使用 require 作用于函数范围(或应用程序代码中的块范围)而不是模块/文件范围时,这与 ES 模块(又称 import 语法)具有等效性。但这是一个异步操作,需要使用 .then()await 关键字来使用此“动态导入”。 - Developer Dave
require 不是“调用”或“包含”文件,它加载并执行一个模块。当然,它会评估整个模块的代码,而不仅仅是一部分 - 就像 import 一样! - Bergi
-1,这就是为什么:“(Next gen JS)”不是一个答案。其次,如果你这样做“import { getDate }from './utils.js';”,你也可以这样做“const { getDate } = require('./utils.js');”,所以最好解释一下这两者之间的区别,而不是直接跳到“Next gen JS”。 - JAN

23

新的ES6:

应该使用'export'关键字与'import'一起来在js文件之间共享变量/数组/对象:

export default myObject;

//....in another file

import myObject from './otherFile.js';

老派的写法:

应该使用'require'与'module.exports'一起使用

 module.exports = myObject;

//....in another file

var myObject = require('./otherFile.js');

13

这两者有很大的区别:

import express from "express";

以及这个:

import * as express from "express";

将CommonJS转换为ES6的正确翻译

const express = require("express");

这是第次导入。

基本上,这是因为在第一次导入中,您正在寻找模块express中名为express的导出。 而第二次导入则使用名称express导入整个express模块。


2
这就是给了我“啊哈时刻”的东西。你解释的方式非常清晰易懂。 - iosifv

2

这些工具属于不同的时代。

require 仅存在于 CommonJS 中(Node.js 创建的一种在应用程序中导入和导出模块的方式),而 import 是 ES6,即一种新的工具,浏览器 JavaScript 和服务器 JavaScript(Node.js)都可以使用。

除了这个历史差异之外,还有使用上的差异,其中 importrequire 更加灵活、现代化和强大。

然而,需要注意的是,一些浏览器仍不支持 ES6,因此在使用之前可能需要编译。

require 使用 module.exports,这是导出模块的“旧”(但仍然有效)语法,可以是任何我们想要的东西,比如对象、字符串等。

import 同时使用 module.exportsexport,它允许您导出更多或更少像 module.export 的代码片段。 import 的一个优点是它只能导入所导出的部分:

示例:

导出文件:

// A.js file

// CommonJS syntax
module.exports = {
     foo: function(){ return 'bar';},
     baz: 123
}

// ES6 syntax
export function foo(){ return 'bar';}
export const baz = 123;

// or

function foo(){ return 'bar';}
const baz = 123;

export default {foo, baz};

导入文件:

// B.js file

// CommonJS syntax
const A = require('./A.js');
const foo = A.foo;
const baz = A.baz;

// ES6 syntax
import * as A from './A.js';
const foo = A.foo;
const baz = A.baz;

// or only
import {foo, baz} from './A.js';

当您使用export default(ES6语法)时,它意味着每个文件只导出一个内容。如果它是一个对象,则import只能导入其中的一部分。但是,如果它是一个函数,那么您可以直接使用import foo from './A.js';而不需要{}* as foo
来源:https://pt.stackoverflow.com/a/213938/317251

1

ES6 中的 import 是对旧版 CommonJs 模块的更新,其中源自 require。沿着这条线,我将区分语法差异,但现在,让我们了解为什么他们进行了更新。

require 是在运行时执行的函数,这意味着如果您在脚本中间定义它,则上面的部分不会识别它,或者如果将其放在 if 语句内部,它只会在 if 表达式为 true 时执行,或者如果将其放在另一个函数中,则只有在该函数被执行时才会执行,等等。

另一方面,import 在静态级别上执行,并且它具有一些规则,应始终处于根级别并且不应位于任何条件语句或函数内部。由于 JavaScript 对导入的静态分析,因此如果这样做,它将抛出编译时错误。

这些是更改导入包方式为 ES6 的优点。

那么为什么在 ES6 更好的情况下 Node 仍在使用 CommonJs 模块?

有一个巨大的代码库在node中使用CommonJs模块,由于多年的支持,非常难以转换为ES6。但是有许多工具可以让我们在node中编写ES6代码,但是这些工具会将其转换为CommonJs。

语法差异:

导入完全取决于从包中导出事物的方式。

如果在导出函数或变量时使用默认的导出方式,例如在CommondJs中使用module.export = functionName或在ES6中使用export default functionName,则导入将如下所示。

在CommonJs和ES6中的导入

const functionName = require("package/exampleFile"); // CommonJs

import functionName from "package/expampleFile.js"; // ES6.
// here you can see that we need to add .js at the end of the file

如果在 CommonJS 中使用 module.exports = {functionName1, functionName2} 或在 ES6 中使用 export functionName1 export functionName2 导出多个函数,则导入方式如下。
const {functionName1, functionName2} = require("package/exampleFile"); // CommonJs

import {functionName1, functionName2} from "package/expampleFile.js"; // ES6.

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