前言
最近在升级公司脚手架时发现一些包由于版本问题,出现了mjs文件类型无法被解析、Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension
、 不能使用require或者不能使用import导入等等一系列错误,经过查询资料,发现他们的差别正是由于使用了不同的CommonJs与ESModule模块化方法。那么他们到底有什么区别呢,为什么会采取这两种不同的处理方式呢?本文将带你一探究竟。
为什么需要CommonJs
CommonJS规范
JavaScript除了在浏览器中运行,也可以在服务器端运行,这就是 Node.js 所做的事情。Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,它可以让我们在服务器端使用 JavaScript 来编写应用程序。为了使 JavaScript 在服务器端更易于管理和维护,我们需要将代码分割成多个独立的模块。这就是 CommonJS 规范的作用。
module.exports、exports、require
在 Node.js 中使用 CommonJS 规范时,我们可以使用 require() 函数来异步加载模块。这个函数会返回一个模块对象,我们可以使用这个对象来访问模块中的数据和函数。同时,我们还可以使用 module.exports 或 exports 对象来导出模块中的数据和函数,以便其他模块可以使用它们。 我们可以像下面这样去使用它: 假设我们有一个名为 math.js 的模块,它包含两个函数:add() 和 subtract()。我们可以使用 CommonJS 规范导出这些函数,并在其他文件中导入它们。 以下是使用module.exports的方式导出的例子: //math.js
javascript
// 导出一个 add 函数
module.exports.add = function(a, b) {
return a + b;
};
// 导出一个 subtract 函数
module.exports.subtract = function(a, b) {
return a - b;
};
我们可以在app.js中导入和使用对应的方法
javascript
// 导入 math.js 模块
const math = require('./math');
// 调用 add 函数并打印结果
console.log(math.add(1,2)); // 输出 3
// 调用 subtract 函数并打印结果
console.log(math.subtract(1,2)); // 输出 -1
使用exports
Node为每个模块提供一个exports变量,指向module.exports。这等同在每个模块头部,有一行这样的命令。
ini
var exports = module.exports;
因此上面的代码可以改造如下: //math.js
css
exports.add = (a, b) => a + b;
exports.subtract = (a, b) => a - b;
在app.js中使用方式不变
javascript
// 导入 math.js 模块
const math = require('./math');
// 调用 add 函数并打印结果
console.log(math.add(1,2)); // 输出 3
// 调用 subtract 函数并打印结果
console.log(math.subtract(1,2)); // 输出 -1
require
require命令用于加载文件,后缀名默认为.js,根据参数的不同格式,require命令去不同路径寻找模块文件。如果参数字符串不以./或 /开头,而且是一个路径,比如require('example-module/path/to/file'),则将先找到example-module的位置,然后再以它为参数,找到后续路径。
异步加载
Node.js 是一个基于事件驱动的非阻塞 I/O 模型,因此需要一种能够异步加载模块的方式。CommonJS 提供了 require() 函数来异步加载模块,并在加载完成后执行回调函数。require在进行加载模块的时候,会执行一遍里面的代码,但是当该模块中多次引入该模块,则require只会执行一次,因为存在缓存。
EsModule
ESModule (esm) 是 ES6 的模块化方案,ES Module的主要特点包括静态导入/导出、命名空间、依赖管理、文件结构和编译时处理等。ES Module支持将不同类型的文件(如JavaScript、CSS、HTML)放在同一个目录下形成一个多页面应用,也可以将不同类型的文件分别放在不同的目录下形成一个单页面应用。
ES Module特点
1、ES Module 支持静态导入和导出,即可以在编译时确定要使用的模块,而不需要在运行时动态加载。 2、ES Module 支持命名空间,可以将不同的模块组织在一个命名空间下,避免了命名冲突的问题。 3、ES Module 在编译时会进行一些处理,例如解析模块依赖关系、生成抽象语法树等,从而提高代码的执行效率。
export、import
我们新建一个module.mjs文件,并通过export导出函数和对象
javascript
// 导出一个函数
export function add(a, b) {
return a + b;
}
// 导出一个对象
export const myObject = {
name: 'Alice',
age: 25
};
新建index.html 文件,注意,需要type="module"
使用import 导入
xml
<script type="module">
// 在另一个文件中导入并使用导出的函数
import { add,myObject } from './module.mjs';
console.log(add(1, 2)); // 输出 3
// 在另一个文件中导入并使用导出的对象
console.log(myObject.name); // 输出 'Alice'
console.log(myObject.age); // 输出 25
</script>
两者区别
1、导入方式不同:CommonJS 使用 require() 函数来导入模块,而 ES Module 使用 import 语句来导入模块。
2、导出方式不同:CommonJS 使用 module.exports 对象来导出模块,而 ES Module 使用 export 关键字来导出模块。
3、命名空间不同:CommonJS 中,每个文件都是一个独立的命名空间,因此需要使用绝对路径来引用其他文件中的变量或函数。而 ES Module 中,每个文件都是一个独立的命名空间,但是可以使用相对路径来引用其他文件中的变量或函数。
4、处理方式不同:CommonJS 在加载模块时会将整个模块打包成一个单独的文件,因此需要先加载主模块再加载依赖模块。而 ES Module 是基于 ES6 的 import 语法实现的,它支持动态导入(异步加载)和按需加载,可以提高应用程序的性能和响应速度。
5、兼容性不同:CommonJS 是 Node.js 的默认模块化方案,因此在 Node.js 环境中可以直接使用。而 ES Module 是 ECMAScript 标准的一部分,可以在所有支持 ES Module 的环境中使用,包括现代浏览器和 Node.js。
总结
在理解完这一堆理论知识后,在处理脚手架报错时,也明白了为什么会存在问题。通过配置babel,项目就可以完美支持ES Module了。