前情提要
我们平时使用的npm 第三方包一般基于这两种规范开发的,很容易遇到一个项目里既有 CommonJS 又有 ES Module 的情况,那么我们应该如何解决这种CommonJS 和 ES Module 混合开发的问题呢?
CommonJS是什么?
- 2009年,美国程序员Ryan Dahl创造了node.js项目,将javascript语言用于服务器端编程。这标志"Javascript模块化编程"正式诞生。在服务器端,需要有模块,与操作系统和其他应用程序互动。
- CommonJS 是为了提供一个类似Python,Ruby和Java标准库,让开发者可以使用CommonJS API编写应用程序,可以运行在不同的JavaScript解释器和不同的主机环境中。
- NodeJS,webpack 我们通常会以CommonJS的形式来书写。
CommonJS可以开发以下程序:
(1)、服务器端JavaScript应用程序
(2)、命令行工具
(3)、图形界面应用程序
(4)、混合应用程序(如,Titanium或Adobe AIR)
代码形式
通俗大白话,特点就是: 使用require关键词来引入依赖,举个例子:
const path = require('path')
const AutoLoad = require('@fastify/autoload')
以及 module.exports
module.exports = async function (fastify, opts) {fastify.get('/', async function (request, reply) {return 'this is an example'})
}
ES module是什么?
ES Modules(ESM)是用于处理模块的 ECMAScript 标准。 虽然 Node.js 长期使用 CommonJS 标准,但浏览器从未有过模块系统。 每个主要决策(如模块系统)必须首先由 ECMAScript 标准化,然后由浏览器实施。
ES modules(ESM) 是 JavaScript 官方的标准化模块系统。
ES 模块是如何运作的
当使用模块来开发的时候,会建立一个模块模块依赖图。不同依赖之间联系来自于你使用的任何 import 语句。
区别
- CommonJS 按需加载,可以动态加载、条件加载、循环加载
- ESM 静态加载,方便解析依赖,优化运行效率。 import() 目前也可以动态加载
- CommonJS 模块输出值的拷贝,ESM输出值的引用。
- CommonJS 运行时加载,ESM编译时输出接口。
- CommonJS 模块的require()同步加载,ESM的import异步加载,有独立模块依赖解析阶段。
- ESM的import会提升,变量const只读。
区别举例
在CommonJS中我们可以直接使用__dirname,就像下面这样:
fastify.register(AutoLoad, {dir: path.join(__dirname, 'plugins'),options: Object.assign({}, opts)})
但是在ESM中不再支持,我们需要改为
import {dirname} from 'path';
import { fileURLToPath } from 'url';function getDirname(url) {const __filename = fileURLToPath(url);return dirname(__filename);
}
解决方案
方案1:按照 ES Module 规范,指定 “type”
单独指定某些文件使用 CommonJS 模块,或者ES Module 模块
- 正常情况下,我们不做配置的话,项目默认是 CommonJS 规范
- 在 package.json 文件中指定 “type”:“module” 后,就会按照 ES Module 规范
- 强制的指定文件后缀为 .cjs后,此文件会遵守CommonJS 规范
ReferenceError: require is not defined in ES module scope, you can use import instead
This file is being treated as an ES module because it has a '.js' file extension and 'test/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
ReferenceError: 在 ES 模块范围中没有定义要求,您可以使用 import 代替
这个文件被视为 ES 模块,因为它有一个’。Js 的文件扩展名和‘ test/package.json’包含“ type”: “ module”。若要将其视为 CommonJS 脚本,请将其重命名为使用’。Cjs 的文件扩展名。
“type”:"module"情况下,如何解决这个报错呢?
我们可以单独给某个文件设置后缀名来使用CommonJS 脚本,强制的指定文件后缀为 .cjs
方案2:按照CommonJS 规范,不做任何配置情况下
指定需要使用ES Module 的文件的后缀名为 .mjs,那么这个文件会被强制指定使用 ES Module 规范
方案3:使用 Babel 完美配置 CommonJS 和 ES Module 随心使用(这个目前有更新的配置方案,等我忙完了写到方案4,方案3也可以用哦)
安装依赖
npm install --save-dev babel-cli babel-preset-env babel-register babel-preset-stage-0
npm install --save babel-polyfill # babel转码时不能识别一些全局对象的API,例如Object.assign,使用它可以解决这个问题
- babel-polyfill babel转码时不能识别一些全局对象的API
- babel-preset-stage-0 es阶段性提案语法 stage-0 包含stage1,2,3
- babel-register 钩子,在程序入口文件引入即可实现转码
在根目录新建 .babelrc
{"presets": ["env","stage-0"]}
配置命令入口,在根目录新建 main.js
require('babel-polyfill');
require('babel-register');
require('./app.js'); // 引入您的项目的启动文件
配置命令行
package.json中加入
"scripts": {"start": "node main.js",},
启动项目
npm start
大功告成,接下来项目内写任何类型都不会有问题
方案4:等我忙完了,继续补充,确实有更好的方式,可以使用更少的依赖
- 今天就写到这里啦~
- 小伙伴们,( ̄ω ̄( ̄ω ̄〃 ( ̄ω ̄〃)ゝ我们明天再见啦~~
- 大家要天天开心哦
欢迎大家指出文章需要改正之处~
学无止境,合作共赢