编辑历史
版本 | 日期 | 内容 |
---|---|---|
v0.1 | 2021/04/23 | 前端模块的发展历史 |
存在的问题
最早只是用来完成少量功能,都被挂载在全局对象window下面。
但是项目规模变大,会存在一些问题
1)命名冲突
2)执行顺序
因为都是挂载window下面,很容易产生命名冲突。
为了更好管理会将功能拆分到不同到JavaScript文件中,因为执行有严格的引用顺序,结果是你必须保证引入顺序和满足相互的依赖关系。
模块发展历史
模式和模块系统
为了解决这些问题出现了很多解决方案,有些可以被成为模式(解决了部分问题),有到可以被称为模块系统/规范(很好得解决了大多数问题)。其中模式包含 定义带下划线的模块命名、模块挂载在自定义对象下、使用闭包返回模块减少明命名冲突等,下面重点介绍规范。
CommonJS/CJS规范
JavaScript作为一门语言,除了在客户端被使用外,在一些服务器开发平台很早就开始使用JavaScript作为开发语言,那时候还没有Node.js。因为服务器端的JavaScript缺乏涉及到操作系统和环境的规范,在不同服务器上编写脚本上无法兼容。
2009年,一名Mozilla的员工发表了一篇关于 服务器端JavaScript 的文章,并组件了非正式的委员会,后更名为CommonJS。CommonJS Module规范被设计出来,并且在Node.js语言中实现。
// file greeting.js
var helloInLang = {
en: 'Hello world!',
es: '¡Hola mundo!',
ru: 'Привет мир!'
};
var sayHello = function (lang) {
return helloInLang[lang];
}
module.exports.sayHello = sayHello;
// file hello.js
var sayHello = require('./lib/greeting').sayHello;
var phrase = sayHello('en');
console.log(phrase);
它通过 require
和 module
来加载模块,因为它们都不是JavaScript语言的关键字,因此它们相当于是Node.js提供的辅助函数,代码交给JavaScript引擎之前都会经过如下的包装:
(function (exports, require, module, __filename, __dirname) {
// ...
// Your code injects here!
// ...
});
AMD规范
一个Mozilla开发者James积极地捍卫着异步模块的开发(他的基本想法是:使用浏览器的功能进行并行加载),这无异能加快网络应用的加载速度,并提交了AMD规范(Asynchronous Module Definition)。因为一直没有和CommonJS小组达成共识,2011年James建立一个单独的邮件列表协调所有和AMD有关的工作。
使用AMD模块的方式书写我们的例子就会变成这样,典型的库有require.js:
// file lib/greeting.js
define(function() {
var helloInLang = {
en: 'Hello world!',
es: '¡Hola mundo!',
ru: 'Привет мир!'
};
return {
sayHello: function (lang) {
return helloInLang[lang];
}
};
});
// file hello.js
define(['./lib/greeting'], function(greeting) {
var phrase = greeting.sayHello('en');
document.write(phrase);
});
UMD规范
AMD作为浏览器端模块化的低门槛的开发方式,有很多开发者使用它;随着Node.js的日益流行,CommonJS Modules的支持者越来越多。
由于AMD模块 和 CommonJS 模块互相不兼容,因此不知道在什么时候开始,UMD(Universal Module Definition)开始流行,它允许我们的代码同时在 AMD工具 和 CommonJS环境 中使用。
代码通常会这样书写:
(function(define) {
define(function () {
var helloInLang = {
en: 'Hello world!',
es: '¡Hola mundo!',
ru: 'Привет мир!'
};
return {
sayHello: function (lang) {
return helloInLang[lang];
}
};
});
}(
typeof module === 'object' && module.exports && typeof define !== 'function' ?
function (factory) { module.exports = factory(); } :
define
));
ES2015/ESM 模块
ES2015(即ES6)对语言进行了重大改变,在2015年发布:
// file lib/greeting.js
const helloInLang = {
en: 'Hello world!',
es: '¡Hola mundo!',
ru: 'Привет мир!'
};
export const greeting = {
sayHello: function (lang) {
return helloInLang[lang];
}
};
// file hello.js
import { greeting } from "./lib/greeting";
const phrase = greeting.sayHello("en");
document.write(phrase);
因为执行环境(浏览器)对新关键字import
export
的支持度不一,最普遍的做法是,使用Babel将代码从ES6转译为ES5。
相关知识
package.json的type
https://stackoverflow.com/questions/61401475/why-is-type-module-in-package-json-file
When you have ‘type’: ‘module’ in the package.json file, your source code should use import syntax. When you do not have, you should use require syntax.
Adding ‘type’: ‘module’ to the package.json enables ES 6 modules. For more info, see here.
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module
I ran your code without any problems. Check for two things:
- Node.js version >= 14. It only works with the latest version of Node.js.
- Make sure your package.json includes a line for “type”: “module”. Without this line, Node.js assumes you want to use CommonJS modules rather than ESM.
Babel Related
Babel 负责将 ES6 版本的 JavaScript 转化为 ES5,从而可以在现有环境执行。
@babel/core
核心包.babelrc
配置规则 presets 和插件 plugins,它们都需要单独进行安装,babel 执行时候会寻找此文件的配置@babel/cli
提供执行格式转化的命令行工具:npx babel FILE_OR_FOLDER_TO_BE_PARSED
@babel/node
提供了直接在命令交互:
npx babel-node
进入交互界面,输入代码,得到转换后的代码npx babel-node FILE_PATH_TO_BE_PARSED
执行文件并且在命令行输出转换后的代码
TypeScript Options
在 tsconfig.json 中有两个编译选项,分别是 target 和 module。
其中target用于指定语言从ES6编译到什么版本,如果不进行配置,ES6的语言不会转译,如果指定了 @presets/env,会被转译为 ES5。
module 用于指定代码会被输出为声明模块系统(CommonJS, AMD, UMD等)