【问题标题】:The difference between "require(x)" and "import x"“require(x)”和“import x”的区别
【发布时间】:2018-03-22 12:15:43
【问题描述】:

我刚刚开始研究一个将与 MongoDB 交互的小型节点项目。但是,我似乎无法正确导入相关的节点模块,即使我已通过 npm 正确安装它们。

例如下面的代码抛出一个错误,告诉我“express has no default export”:

import express from "express";

但是,此代码有效:

const express = require("express");

所以我的问题是,import 和 variable/require 方法的功能有什么区别?我想解决困扰我的项目导入的任何问题,因为它似乎很可能导致其他问题。

【问题讨论】:

  • 除非您包含 express 的类型定义,否则第一种形式没有意义 - 在这种情况下您可以使用第二种形式,但变量 express 的类型将是 any。您可以包含来自此处的定义npmjs.com/package/@types/express
  • @Ryall 这是不同的问题。注意import x = require('x')var x = require('x') 不同。

标签: node.js typescript import require


【解决方案1】:

这个简单的图表将帮助您了解requireimport 之间的区别。

除此之外,

不能使用require 选择性地仅加载您需要的片段,但使用import,您可以选择性地仅加载您需要的片段,这样可以节省内存。

加载是同步(逐步)require import 可以是异步的(无需等待之前的导入),所以它可以执行比 require.

【讨论】:

  • 影响代码的最大区别是 CommonJS 模块中的导出是“计算的”,而 ESM 模块中的导出是静态的(预定义的)。 JS 仅在解析代码(尚未运行它)后就可以确定 ESM 模块中的导出。在 commonJS 模块中,导出仅在模块实际运行时才知道,当模块初始化代码完成运行时,您会看到分配给 module.exports 的内容。在尝试使单个模块同时适用于 ESM 和 CommonJS 时,仅此差异就会造成兼容性问题。
  • ESM 模块对打包者更友好,但对编码员的限制更大,因为您不能在 ESM 模块中进行计算导出。
【解决方案2】:

requireimport 的主要区别在于require 会自动扫描node_modules 以查找模块,但来自ES6 的import 不会。

大多数人使用babel 来编译importexport,这使得import 的行为与require 相同。

Node.js 的未来版本可能会支持 import 本身(实际上是 the experimental version already does),从 Node.js 的注释来看,import 将不支持 node_modules,它基于 ES6,并且必须指定模块的路径。

所以我建议你不要在 babel 中使用import,但是这个功能还没有确定,以后可能会支持node_modules,谁知道呢?


作为参考,下面是 babel 如何将 ES6 的 import 语法转换为 CommonJS 的 require 语法的示例。

假设文件app_es6.js 包含此导入:

import format from 'date-fns/format';

这是从节点包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"
                }
            }
        ]
    ]
}

package.json文件中定义的这个build-server-file脚本是babel解析app_es6.js文件并输出文件app.js的指令。

运行build-server-file 脚本后,如果你打开app.js 并查找date-fns 导入,你会看到它已经被转换成这样:

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

var _format2 = _interopRequireDefault(_format);

该文件的大部分内容对大多数人来说都是 gobbledygook,但计算机可以理解。


也作为参考,作为如何创建模块并将其导入项目的示例,如果您安装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);

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

【讨论】:

  • 啊啊啊啊啊。 Babel 还没有安装在这个特定的项目上,这使得一切都有意义。我以为 ES6 导入/导出已经可以正常工作了,但现在我明白 Babel 只是将所有内容都更改为 require 反正
  • 现在坚持要求。以后可以随时更改,没有任何问题
  • import won't support node_modules 你这是什么意思?
  • importrequire 都扫描 node_modules 以查找语句指定的包。 require 将包中分配给module.exports 的任何内容加载到分配给它的变量,或者如果没有声明左手,则加载到全局范围。但是,import 只会按名称加载 es6 默认导出,除非所有都分配给别名:import * as X from 'pkg'。您也可以使用 object destructuring 导入没有默认值的 es6 包:import { X } from 'pkg'。如果您将整个包(包括所有导出)导入到全局范围 import 'package',它将与 require 一样工作。
【解决方案3】:

让我举个例子,用 require 和 import 包含 express 模块

-需要

var express = require('express');

-导入

import * as  express from 'express';

因此,在使用上述任何语句后,我们将拥有一个名为“express”的变量。现在我们可以将“app”变量定义为,

var app = express(); 

所以我们使用 'require' 和 'CommonJS' 和 'import' 和 'ES6'。

有关“要求”和“导入”的更多信息,请阅读以下链接。

要求 - Requiring modules in Node.js: Everything you need to know

导入 - An Update on ES6 Modules in Node.js

【讨论】:

  • 这绝对是正确的答案。发帖人在使用 es6 import 语句时遇到问题,并且对 express has no default export 错误感到困惑。这个答案提供了解决方案。具有多个(甚至单个)导出但未定义 default export 的模块将需要将所有导出分配给一个命名变量,正如答案所解释的那样:import * as whatever from 'package';
  • 同意,这应该是最佳答案。作为对先前评论的修正,您可以在node_modules 中检查您所依赖的包的代码(入口点将在其package.json main 键下列出)。 module.export = whatever 之类的内容意味着您可能必须将其导入为 import * as whatever from 'package';
【解决方案4】:

我会让它变得简单,

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

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

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

而在导入中,您只能提取所需的对象/函数/变量。

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

另一个主要区别是您可以在程序中的任何位置使用require,因为import 应始终位于文件顶部

编辑:在最新的节点版本中,您可以使用解构。它看起来像这样

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

【讨论】:

  • 您可以将对象解构与require 一起使用,例如const { getDate } = require('./utils.js');
  • 是的,在我们可以使用的最新节点版本中,但在早期版本中不是。
  • 从第 6 版(2016 年 4 月)开始,Node 支持解构导入。
  • 您的整体解释过于简单,不准确。甚至关于在程序中的任何地方使用requireimport 仅在文件顶部的声明也在掩饰重要的细节。当您使用require 作用域为函数(或应用程序代码中的某处的块作用域)而不是作用域为模块/文件时,这与 ES 模块等效(又名import 语法)。虽然这是一个异步操作,但这种“动态导入”需要使用 .then()await 关键字。
【解决方案5】:

新的 ES6:

'import' 应与'export' 关键字一起使用,以在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');

【讨论】:

    【解决方案6】:

    这之间有很大的不同:

    import express from "express";
    

    还有这个:

    import * as express from "express";
    

    CommonJS 到 ES6 的正确翻译

    const express = require("express");
    

    第二次导入。

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

    【讨论】:

      猜你喜欢
      • 2019-03-03
      • 2020-07-23
      • 2015-10-01
      • 2017-09-18
      • 2021-05-07
      • 2019-07-07
      • 2016-10-05
      • 2020-07-21
      • 1970-01-01
      相关资源
      最近更新 更多