编辑历史

版本 日期 内容
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语言中实现。

  1. // file greeting.js
  2. var helloInLang = {
  3. en: 'Hello world!',
  4. es: '¡Hola mundo!',
  5. ru: 'Привет мир!'
  6. };
  7. var sayHello = function (lang) {
  8. return helloInLang[lang];
  9. }
  10. module.exports.sayHello = sayHello;
  11. // file hello.js
  12. var sayHello = require('./lib/greeting').sayHello;
  13. var phrase = sayHello('en');
  14. console.log(phrase);

它通过 requiremodule 来加载模块,因为它们都不是JavaScript语言的关键字,因此它们相当于是Node.js提供的辅助函数,代码交给JavaScript引擎之前都会经过如下的包装:

  1. (function (exports, require, module, __filename, __dirname) {
  2. // ...
  3. // Your code injects here!
  4. // ...
  5. });

AMD规范

一个Mozilla开发者James积极地捍卫着异步模块的开发(他的基本想法是:使用浏览器的功能进行并行加载),这无异能加快网络应用的加载速度,并提交了AMD规范(Asynchronous Module Definition)。因为一直没有和CommonJS小组达成共识,2011年James建立一个单独的邮件列表协调所有和AMD有关的工作。

使用AMD模块的方式书写我们的例子就会变成这样,典型的库有require.js:

  1. // file lib/greeting.js
  2. define(function() {
  3. var helloInLang = {
  4. en: 'Hello world!',
  5. es: '¡Hola mundo!',
  6. ru: 'Привет мир!'
  7. };
  8. return {
  9. sayHello: function (lang) {
  10. return helloInLang[lang];
  11. }
  12. };
  13. });
  14. // file hello.js
  15. define(['./lib/greeting'], function(greeting) {
  16. var phrase = greeting.sayHello('en');
  17. document.write(phrase);
  18. });

UMD规范

AMD作为浏览器端模块化的低门槛的开发方式,有很多开发者使用它;随着Node.js的日益流行,CommonJS Modules的支持者越来越多。

由于AMD模块 和 CommonJS 模块互相不兼容,因此不知道在什么时候开始,UMD(Universal Module Definition)开始流行,它允许我们的代码同时在 AMD工具 和 CommonJS环境 中使用。

代码通常会这样书写:

  1. (function(define) {
  2. define(function () {
  3. var helloInLang = {
  4. en: 'Hello world!',
  5. es: '¡Hola mundo!',
  6. ru: 'Привет мир!'
  7. };
  8. return {
  9. sayHello: function (lang) {
  10. return helloInLang[lang];
  11. }
  12. };
  13. });
  14. }(
  15. typeof module === 'object' && module.exports && typeof define !== 'function' ?
  16. function (factory) { module.exports = factory(); } :
  17. define
  18. ));

ES2015/ESM 模块

ES2015(即ES6)对语言进行了重大改变,在2015年发布:

  1. // file lib/greeting.js
  2. const helloInLang = {
  3. en: 'Hello world!',
  4. es: '¡Hola mundo!',
  5. ru: 'Привет мир!'
  6. };
  7. export const greeting = {
  8. sayHello: function (lang) {
  9. return helloInLang[lang];
  10. }
  11. };
  12. // file hello.js
  13. import { greeting } from "./lib/greeting";
  14. const phrase = greeting.sayHello("en");
  15. 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

https://stackoverflow.com/questions/61670459/importing-in-node-js-error-must-use-import-to-load-es-module

I ran your code without any problems. Check for two things:

  1. Node.js version >= 14. It only works with the latest version of Node.js.
  2. 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等)

参考文章