最近在研究nodejs
,尝试在nodejs
中使用fetch
来发起请求,从网上抄下来这么一段代码:
// demo01.js
import fetch from "node-fetch";
fetch("https://csdn.net")
.then(res=>res.text())
.then(json=>console.log(json));
直接使用肯定是不行的,这里一看就需要依赖 node-fetch
这个模块,于是我没有想别的,使用 npm 安装了这个模块(为了方便,我直接将其安装称为了全局模块了):
npm install -g node-fetch
做完以上步骤后,我的工程内目前只有一个 demo01.js
装着代码的一个文件,没有其他任何资源。
然后在代码文件夹中打开终端,尝试在终端中运行该代码:
node .\demo01.js
SyntaxError: nodejs Cannot use import statement outside a module
作为一个小白,请原谅我只在浏览器中运行过 fetch
,报这样的错误我哪见过这样的错误,看起来好像是不支持 import
难道是我的 nodejs
版本有问题?
应用 | 版本 |
---|---|
nodejs |
12.20.1 |
node-fetch |
3.0.0 |
看起来好像也是很很新的版本了啊。其实这里的错误提示已经给的很明显了,只是我刚开始学习 nodejs
导致没有明白其中报错的含义,以为是nodejs
版本的原因。
但实际原因是 import
关键字是 ES6 的一个特性,而 nodejs 默认场景下是支持 AMD,CommonJS,
首先我们可确认的是我们使用到了一个模块 node-fetch
,而在 nodejs
中对于模块的调用方式有两种,分别对应 ES5 规范 和 ES6 规范,其中 ES5 规范对应的是 require
函数,通过调用 require 函数的方式引入模块;而 ES6 规范引入了 import
关键字,通过该关键字引入模块。
// ES5 的模块引入方式
const fetch = require("node-fetch");
// ES6 的模块引入方式
import fetch from "node-fetch";
因此从网上找到的一些nodejs
调用 fetch
的示例代码一般都使用到了这两种方式,如 node-fetch
的示例代码使用的是 import
关键字:
还有一些教程则使用的是 require
关键字:
出于版本升级的原因,nodejs
中默认是通过 require
关键字的方式加载模块的,也就是如果你直接通过 import 调用模块执行的话,就会出现最开始我遇到的错误:
报错信息中指出当前如果直接使用 node demo01.js
的方式执行代码的话,是一种非模块话的方式调用,在这种调用方式下无法直接使用 import
关键字。如果要使用的话,需要在 package.json
文件中指定当前工程是一个模块,在能够在模块中使用 import
关键字。
这里说明一下 package.json
是一个包配置文件,可以由 npm init
在当前工程的根目录下自动生成,也可以自己手动编写。npm
通过该配置文件可以实现对该工程中依赖的模块进行管理,比如希望在当前工程中添加对 node-fetch
的依赖,首先使用 npm init
在当前目录下创建 package.json
文件,然后执行命令 npm install node-fetch --save
就会将对 node-fetch
的依赖写入到 package.json
中。
其中 --save
选项用于表示将对 node-fetch
的依赖写入到 package.json
当中去,在新版本中即使不指定 --save
选项也会默认写入的。
在执行完上述操作后可以看到当前工程下会存在一个配置文件(package.json
):
{
"name": "dem01",
"version": "1.0.0",
"description": "",
"main": "demo01.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
有了这个文件后我们的代码其实就可以打包使用了,但这不是我这里想要讲的内容,想要深入了解的同学可以去看下面的参考资料。
现在我们有了 package.json
,我们尝试按要求往里面添加一个 "type":"module"
,标记当前工程为一个模块:
重新安装一下我们的依赖 node-fetch
再次尝试运行我们的脚本:
node demo01.js
可以看到我们的代码已经可以正常执行了。
import
方式执行代码首先确认下import
方式引入 node-fetch
是在 node-fetch
的最新版本 v3.x
出现的,而 node-fetch
的 v3.x
版本要求 nodejs
版本大于 12.20.0
。
因此在使用 import
方式导入 node-fetch
模块不仅要求 node-fetch
版本为 v3.x
还要求 nodejs
版本 大于 12.20.0
,需要注意一下。
# 新建一个工程目录;
mkdir demo01 && cd demo01
# 初始化当前模块信息
npm init
# 指定模块类型为 module
vim package.json
# 查看 node-fetch 信息
npm info node-fetch
# 查看 node-fetch 所有版本
npm view node-fetch versions
# 安装 node-fetch 并指定版本为 3.0.0
npm install [email protected]
编写 js 脚本
import fetch from "node-fetch";
fetch("https://csdn.net")
.then(res=>res.text())
.then(json=>console.log(json));
运行:
node demo01.js
运行成功。
require
方式执行代码再深究一步,在网上可以看到很多使用 require
方式引入node-fetch
的,为了更进一步研究其中的机制,决定重新使用 require
的方式将上面的代码重新写一遍:
// demo01.js
// import fetch from "node-fetch";
const fetch = require("node-fetch");
fetch("https://csdn.net")
.then(res=>res.text())
.then(json=>console.log(json));
运行:
可以看到发生了错误,第一行错误表示必须使用 import
关键字来加载一个 ES 模块。
第二行错误说明了为什么会将当前工程标识为一个 ES
模块,并不仅仅是根据当前工程下 package.json
文件中存在 "type":"module"
,来进行标记的,而是根据当前工程下依赖的模块 node-fetch
模块中 package.json
中存在"type":"module"
,因此判断当前工程是一个 ES 模块。也就是说只要工程或者 工程所依赖的代码中有一个被标记为 ES 模块,那么整个工程就会被认为是一个 ES 模块。模块之间的调用也就只能够使用 import
关键字而不能够使用 require
函数。
那是网上找到的参考教程出错了吗?其实也不是,这里涉及到一个 node-fetch
版本升级的问题,也就是 node-fetch
存在一个大的版本升级,具体点讲就是node-fetch
从 v2.x
升级到 v3.x
的时候,其引入方式存在着大的更新,从之前的 require
修改为了 import
,并且要求 nodejs 版本需要大于 12.20.0
。
如果发现你的 node-fetch
不能够使用的话,请检查一下你的 node-fetch
版本是否为 v3.x
且 nodejs
版本是否大于 v12.20.0
。如果nodejs
版本小于 v12.20.0
,那么需要指定 node-fetch
的版本在 v2.x
上,然后使用 require
方式调用:
# 新建一个工程目录;
mkdir demo02 && cd demo02
# 查看 node-fetch 信息
npm info node-fetch
# 查看 node-fetch 所有版本
npm view node-fetch versions
# 安装 node-fetch 并指定版本为 2.6.2
npm install [email protected]
编写 js 脚本
const fetch = require("node-fetch");
fetch("https://csdn.net")
.then(res=>res.text())
.then(json=>console.log(json));
运行:
node demo02.js
运行成功。
处于一些原因,我的请求中需要禁用 SSL
验证,则需要安装 https
模块,并将脚本修改如下:
const fetch = require("node-fetch");
const https = require("https");
const httpsAgent = new https.Agent({rejectUnauthorized: false});
fetch("https://csdn.net",{
agent: httpsAgent
})
.then(res=>res.text())
.then(json=>console.log(json));
参考Node-fetch: Disable SSL verification
参考javaScript中的await和Promise
PS C:\Users\ghimi\> npm view node-fetch
[email protected] | MIT | deps: 3 | versions: 86
A light-weight module that brings Fetch API to node.js
https://github.com/node-fetch/node-fetch
keywords: fetch, http, promise, request, curl, wget, xhr, whatwg
dist
.tarball: https://registry.npmmirror.com/node-fetch/-/node-fetch-3.2.10.tgz
.shasum: e8347f94b54ae18b57c9c049ef641cef398a85c8
.integrity: sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==
.unpackedSize: 106.7 kB
dependencies:
data-uri-to-buffer: ^4.0.0 fetch-blob: ^3.1.4 formdata-polyfill: ^4.0.10
maintainers:
- timothygu <[email protected]>
- bitinn <[email protected]>
- endless <[email protected]>
- akepinski <[email protected]>
- node-fetch-bot <[email protected]>
dist-tags:
beta: 4.0.0-beta.4 cjs: 2.6.7 latest: 3.2.10 next: 3.0.0-beta.10
published 2 months ago by node-fetch-bot <[email protected]>
出现问题的原因是自己学艺不精,没有及时认清到 AMD 和 CMD 的区别,对于 node-fetch
,认识也不够深刻,但是整个过程下来解决问题的过程还是很满意的。最终也找到了问题的原因,接下来的一步就是继续研究下 ES5 和 ES6 之间在 nodejs 上的区别了。从整体来看这篇博客技术含量并不是很高,但希望能够帮助和我遇到同样问题的同学,也欢迎大佬前来指教。
nodejs 中如何使用fetch
nodejs package.json详解
七天学会NodeJS
Uncaught SyntaxError: Cannot use import statement outside a module的解决方法
CommonJS包规范与NodeJS的包管理工具NPM
node新版本对ES Module的支持与注意事项
Uncaught SyntaxError: Cannot use import statement outside a module
Fetch API 教程
解决node环境下SyntaxError: Cannot use import statement outside a module的问题
使用 Fetch
NodeJs:“require” 函数详解,懂这个你就懂NodeJs了
Nodejs的模块机制及require用法
AMD、CMD、CommonJs、ES6的对比
Node.js v16.9.0 文档
NodeJs的CommonJS模块规范
nodejs使用fetch抓取geojson