技术探索 前端模块化规范 Breezli 2025-04-03 2025-04-03 前端模块化规范 通常来说,一个文件就是一个模块,有自己的作用域,只向外暴露特定的变量和函数 。目前流行的js模块化规范有CommonJS、AMD、CMD、UMD以及ES6的模块系统。
AMD / CMD / CommonJs 是JS模块化开发的标准规范,目前对应的实现是
RequireJs / SeaJs / nodeJs
简介 CommonJS
CommonJS用同步 的方式加载模块。在服务端,模块文件都存放在本地磁盘,读取非常快,所以这样做不会有问题。但是在浏览器端,限于网络原因,更合理的方案是使用异步加载。
NodeJS 是 CommonJS 规范的主要实践者
有4个重要的环境变量为模块化实现提供支持
module
exports
1 2 3 4 5 6 7 8 9 10 var total = 10 ;function add (a, b ) { return a + b; } module .exports = { add : add, total : total }
require
1 2 3 4 5 6 7 8 var math = require ('./math' );math.add (2 , 5 ); var http = require ('http' );http.createService (...).listen (3000 );
global
AMD
浏览器优先 , 异步加载
预加载
所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 /** 网页中引入require.js及main.js **/ <script src="js/require.js" data-main="js/main"></script> /** main.js 入口文件/主模块 **/ // 首先用config()指定各模块路径和引用名 require.config({ baseUrl: "js/lib", paths: { "jquery": "jquery.min", //实际路径为js/lib/jquery.min.js "underscore": "underscore.min", } }); // 执行基本操作 require(["jquery","underscore"],function($,_){ // some code here });
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 // 定义math.js模块 define(function () { var basicNum = 0; var add = function (x, y) { return x + y; }; return { add: add, basicNum :basicNum }; }); // 定义一个依赖underscore.js的模块 define(['underscore'],function(_){ var classify = function(list){ _.countBy(list,function(num){ return num > 30 ? 'old' : 'young'; }) }; return { classify :classify }; }) // 引用模块,将模块放在[]内 require(['jquery', 'math'],function($, math){ var sum = math.add(10,20); $("#sum").html(sum); });
CMD
异步加载
懒加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 /** AMD写法 **/ define(["a", "b", "c", "d", "e", "f"], function(a, b, c, d, e, f) { // 等于在最前面声明并初始化了要用到的所有模块 a.doSomething(); if (false) { // 即便没用到某个模块 b,但 b 还是提前执行了 b.doSomething() } }); /** CMD写法 **/ define(function(require, exports, module) { var a = require('./a'); //在需要时申明 a.doSomething(); if (false) { var b = require('./b'); b.doSomething(); } }); /** sea.js **/ // 定义模块 math.js define(function(require, exports, module) { var $ = require('jquery.js'); var add = function(a,b){ return a+b; } exports.add = add; }); // 加载模块 seajs.use(['math.js'], function(math){ var sum = math.add(1+2); });
UMD
UMD是AMD和CommonJS的一个糅合。AMD是浏览器优先,异步加载;CommonJS是服务器优先,同步加载。
先判断是否支持node.js的模块,存在就使用node.js;再判断是否支持AMD(define是否存在),存在则使用AMD的方式加载。这就是所谓的UMD
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ((root, factory) => { if (typeof define === 'function' && define.amd) { //AMD define(['jquery'], factory); } else if (typeof exports === 'object') { //CommonJS var $ = requie('jquery'); module.exports = factory($); } else { //都不是,浏览器全局定义 root.testModule = factory(root.jQuery); } })(this, ($) => { //do something... 这里是真正的函数体 });
ES6 Module 模块功能主要由两个命令构成:
export
规定模块的对外接口
import
输入其他模块提供的功能
特征
严格模式:ES6 的模块自动采用严格模式
import
read-only特性: import
的属性是只读的,不能赋值,类似于const
的特性
export/import
提升: import/export
必须位于模块顶级,不能位于作用域内;其次对于模块内的import/export
会提升到模块顶部,这是在编译阶段完成的
总结/区别 CommonJs 主要针对服务端
服务器端一般采用 同步加载 文件,也就是说需要某个模块,服务器端便停下来,等待它加载再执行。
AMD/CMD 主要针对浏览器端
而浏览器端要保证效率,需要采用 异步加载 ,这就需要一个预处理,提前将所需要的模块文件并行加载好。
AMD/CMD 都是 并行加载 js文件
AMD 是预加载 ,在并行加载js文件同时,还会解析执行该模块(因为还需要执行,所以在加载某个模块前,这个模块的依赖模块需要先加载完成)
加载快速,尤其遇到多个大文件,因为并行解析,所以同一时间可以解析多个文件
加载顺序不一定,可能会为程序埋下大坑
CMD 是懒加载 ,虽然会一开始就并行加载js文件,但是不会执行,而是在需要的时候才执行。
每个JS文件的执行顺序在代码中是有体现的,是可控的
多文件加载速度叠加
JS是单线程,所有JS文件执行时间叠加在AMD和CMD中是一样的。
但CMD是懒加载,没法利用空闲时间,而AMD是文件加载好就执行,往往可以利用一些空闲时间。
参考 前端模块化——彻底搞懂AMD、CMD、UMD、ESM和CommonJS - 知乎
【新手入门 最精炼总结】AMD/CMD/CommonJs到底是什么?它们有什么区别?_amd与cmd-CSDN博客