几种模块化规范
CommonJS:Nodejs,运行在服务端环境
CMD(Common Module Definition - 通用模块定义):主要通过
sea.js
流行起来,是国内大佬的作品。写法模仿CommonJS规范,不过是运行在浏览器环境
AMD(Asynchronous Module Definition - 异步模块定义):采用异步加载,运行在浏览器环境,使用广泛。常用的库是
require.js
UMD(Universal Module Definition - 通用模块定义):该模式主要用来解决CommonJS模式和AMD模式代码不能通用的问题。
在开发中,产物模块规范变成umd后,可以在commonjs、AMD、CMD等多种环境下使用,应用广泛,功能强大,是线上产物常用规范。ESM(ES Modules):ES6 规范,目前也可以运行在高版本的浏览器和Nodejs中
参考文章
IIFE
CMD、AMD、UMD的实现,本质上就是利用函数立即执行表达式(immediately invoked function expression),在不同环境下的全局对象上,挂入模块。
Sea.js 实现原理(不推荐使用)
魔法在哪里?
1 | define('a', function (require,exports,modules){ |
和 require.js
显式声明依赖不一样,写法类似 CommonJS 规范。
由于没有去提前声明/配置(回调函数只有定义,没有运行),那么如何在「加载期」知道依赖哪个模块呢?
实现原理
核心是利用 Function.toString
方法,拿到「函数的字符串定义」。
「加载期」:
- 通过回调函数的Function.toString函数,使用正则表达式来捕捉内部的require字段,找到require(‘jquery’)内部依赖的模块jquery
- 根据配置文件,找到jquery的js文件的实际路径;如果有多个依赖,也会根据依赖树确定先后顺序
- 在dom中插入script标签,载入模块指定的js,绑定加载完成的事件,使得加载完成后将js文件绑定到require模块指定的id(这里就是jquery这个字符串)上
「运行期」:
- 回调函数内部依赖的js全部加载(暂不调用)完后,调用回调函数
- 当回调函数调用require(‘jquery’),即执行绑定在’jquery’这个id上的js文件,即刻执行,并将返回值传给var b
参考文章
UMD
魔法在哪里?
支持AMD、CMD规范,也支持原生的直接在window上定义的做法,还支持CommonJS。
实现原理
利用 IIFE 的写法,内部区别处理即可。
下面是考虑到「有前置依赖」的UMD的实现思路:
1 | (function(root, factory) { |
在线案例演示了在sea.js上下文中,引入了一个UMD规范的文件,其可以被正常加载:
参考文档
配套工具
NPM 入口
在monorepo / SDK开发中,经常会对不同的运行环境,输出不同的产物。这些产物的「入口文件」在 package.json 中可以声明,如下所示:
目前支持3种字段:
- browser:在 browser 下的入口文件
- module:在 browser & node 下的入口文件,符合 ESM 规范
- main:在 browser & node 下的入口文件
同时声明这3个字段时,在不同的环境下,解析优先级不一样:
- 在非browser下:module > main
- 在browser下:browser > module > main
Webpack
- 环境:可以通过
target
字段来声明产物运行环境。 每个环境,解析依赖包的入口文件时,优先级都不同。 - 解析顺序:通过
mainFields
字段可以自定义解析顺序,比如[main, module]
参考文档
用法示例
下面文章都挺好的,没必要重复拷贝,直接看原文吧。