记录每天自我学习转前端的碎碎念,仅作加强记忆的笔记和自我监督打卡使用。
CMD(Common Module Definition)
是一种用于浏览器端 JavaScript 的模块化规范,具有以下核心特性
1.按需加载
CMD 采用懒加载机制,只有在需要时才加载依赖模块,显著优化性能。例如:
define(function(require) {
// 需要时再加载依赖
const increment = require('./increment');
increment.add(1);
});
2.依赖就近
与 AMD 的依赖前置不同,CMD 允许在代码中就近声明依赖,提高可维护性:
define(function(require, exports) {
// 模块逻辑...
const utils = require('./utils'); // 依赖在调用处声明
exports.result = utils.calculate();
});
实现方式
通过全局函数 define(factory) 定义模块,其中 factory 支持函数/对象/字符串形式:
// 函数形式(主流)
define(function(require, exports, module) {
module.exports = { ... };
});
// 对象/字符串形式
define({ data: 'value' }); // 直接导出对象
执行机制
模块定义后不会立即执行,仅在调用时执行,减少资源占用。
代表库与定位 由国内开发者玉伯提出,SeaJS 是其典型实现,专为解决浏览器异步模块加载设计,兼容 CommonJS 规范。
AMD(Asynchronous Module Definition)
是一种异步模块定义规范,专为解决浏览器端 JavaScript 模块化开发而设计,核心特性如下
1.异步加载
模块按需异步加载,避免阻塞页面渲染和脚本执行。
require(['moduleA', 'moduleB'], function(A, B) {
// 依赖加载完成后执行
});
2.依赖前置
在模块定义时显式声明所有依赖,依赖加载完毕后才执行回调函数:
define(['dep1', 'dep2'], function(dep1, dep2) {
return {}; // 模块接口
});
3.标准 API
define(id?, deps?, factory):定义模块(id 为模块名,deps 为依赖数组,factory 为工厂函数)。
require(deps, callback):动态加载模块
与 CMD 规范的区别
| 特性 | AMD (RequireJS) | CMD (SeaJS) |
|---|---|---|
| 依赖声明 | 前置声明(模块头部定义) | 就近声明(代码中按需加载) |
| 执行时机 | 依赖加载后立即执行 | 延迟到 require 时执行 |
| 代表库 | RequireJS | SeaJS |
适用场景
- 浏览器环境
- 解决传统脚本同步加载的阻塞问题,尤其适合大型 Web 应用。
- 复杂依赖管理
- 明确声明依赖关系,提升代码可维护性。
- 历史项目兼容
- 支持旧版浏览器模块化开发。
局限性
- 代码冗余:依赖前置语法增加代码量。
- 服务器支持弱:主要针对浏览器设计,Node.js 环境不适用。
- 已逐渐被替代:ES6 模块(import/export)成为现代开发主流。
典型实现:RequireJS 是 AMD 最广泛使用的库,通过 define.amd 属性标识兼容性。
关于使用webpack打包,umd规范
UMD规范可以同时兼容AMD和CommonJs规范,下面代码是通过webpack打包且设置umd格式,设置mode为development得到的代码,改模式下代码没有压缩
(function webpackUniversalModuleDefinition(root, factory) {
if (typeof exports === 'object' && typeof module === 'object') {
// CommonJS模块环境(如Node.js),
// 在 Vue 项目中将上述 UMD 格式的库作为 npm 依赖使用时,会进入这个分支,
// 原因解析:Vue CLI 或 Webpack 构建的项目在 Node.js 环境中执行模块加载,该环境同时存在 exports 和 module 对象,满足第一个分支的条件
// 当通过 import 或 require 引入 npm 包时,Node.js 的 CommonJS 模块系统会自动激活,触发 module.exports 导出逻辑
// 当执行 import 语句时,底层会调用 CommonJS 的 require() 函数加载模块。该函数执行目标模块的代码,并返回其 module.exports 对象的值
module.exports = factory();
}
else if (typeof define === 'function' && define.amd) {
// 此分支检测AMD模块环境(如RequireJS)
define([], factory);
}
else if (typeof exports === 'object') {
// 此分支用于兼容仅存在exports对象的环境(例如某些CommonJS环境,但不完全符合Node.js规范)
exports["weixin"] = factory();
}
else {
// 当以上条件都不满足时,说明处于非模块化环境(如浏览器全局作用域)
root["weixin"] = factory();
}
})(self, () => {
return (() => {
"use strict";
var __webpack_modules__ = {
"./src/weixin.js": ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"age\": () => (/* binding */ age),\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__),\n/* harmony export */ \"name\": () => (/* binding */ name)\n/* harmony export */ });\nconst name = \"li\";\nconst age = 18;\nconst height = 180;\n\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (height);\n\n\n//# sourceURL=webpack://weixin/./src/weixin.js?");
})
};
var __webpack_require__ = {};
/*
// 立即执行函数写法1-箭头函数
(() => {
})();
// 立即执行函数写法2-普通函数
(function (){
})()
**/
(() => {
__webpack_require__.d = (exports, definition) => {
for (var key in definition) {
if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
}
}
};
})();
(() => {
__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
})();
(() => {
__webpack_require__.r = (exports) => {
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};
})();
var __webpack_exports__ = {};
__webpack_modules__["./src/weixin.js"](0, __webpack_exports__, __webpack_require__);
/*
此时的__webpack_exports的值是
{
age: 18,
default: 180,
name: "li",
__esModule: true,
Symbol(Symbol.toStringTag): "Module"
}
*/
// 如果配置了libraryExport: 'default',则多了下面这行代码,不配置就没有这行代码
__webpack_exports__ = __webpack_exports__["default"];
// 上面代码执行后,__webpack_exports的值变成了180
return __webpack_exports__;
})();
});
/*
在此demo中,__webpack_exports__最终的值为
{
age: 18,
default: 180,
name: "li",
__esModule: true,
Symbol(Symbol.toStringTag): "Module"
}
如果在webpack的配置中设置了
output:{
libraryExport: 'default',
}
则在处理返回数据前加上 __webpack_exports__ = __webpack_exports__["default"];
这行代码会重新赋值__webpack_exports__,只取了里面的default值
__webpack_exports__变成了180
这样在使用时是无法访问具名导出的name和age,只能访问默认导出的变量
这里需要注意,使用默认导出时,得到的直接是导出变量的值,没有key
不配置libraryExport: 'default'时,得到的数据是
{
age: 18,
default: 180,
name: "li",
__esModule: true,
Symbol(Symbol.toStringTag): "Module"
}
为什么这里的height键变成了default?
因为webpack在执行__webpack_require__.d = (exports, definition) => {})函数,这个函数会遍历definition的key给
__webpack_exports__赋值,传入的definition里就是固定的key:default,这个default的值取的就是export.default的值
所以生成的 __webpack_exports__会包含default,没有export.default的那个key:height
**/
Webpack 配置中的 library 和 libraryTarget 是用于控制库(library)打包方式的核心参数,其作用和配置规则如下:
libraryTarget 的作用
决定库的导出形式,支持多种模块规范: 1.var(默认值)
var MyLibrary = ... // 作为全局变量暴露
this / window / global
挂载到指定全局对象(浏览器中 window.MyLibrary,Node.js 中 global.MyLibrary)。
2.commonjs / commonjs2
commonjs:导出为 exports["MyLibrary"]
commonjs2:导出为 module.exports(Node.js 标准)
3.amd
// 通过 AMD 规范定义模块(需配合 RequireJS):
define("MyLibrary", [], factory);
3.umd(推荐)
通用模块定义,自动兼容 CommonJS、AMD 和全局变量环境。
library 的作用
指定库的全局变量名或模块名
1.当 libraryTarget 为 var/this/window 时:
output: {
library: "MyLib", // 生成 window.MyLib
libraryTarget: "var"
}
2.当 libraryTarget 为 umd 时:
output: {
library: "MyLib", // 浏览器: window.MyLib | Node.js: require('MyLib')
libraryTarget: "umd"
}
3.当 libraryTarget 为 amd 时:
output: {
library: "MyAMDModule", // define("MyAMDModule", ...)
libraryTarget: "amd"
}