AMD模块系统基础
AMD(Asynchronous Module Definition,异步模块定义)是一种为浏览器环境设计的JavaScript模块格式。它支持异步加载、依赖管理和模块化开发,是早期解决浏览器端模块化问题的重要方案。
什么是AMD
AMD是一个用于定义模块的JavaScript API,具有以下特点:
- 异步加载: 支持模块的异步加载,不阻塞页面渲染
- 依赖管理: 明确声明模块依赖,自动处理加载顺序
- 浏览器友好: 专为浏览器环境设计,无需构建工具
- 插件系统: 支持加载各种类型的资源
- 配置灵活: 提供丰富的配置选项
AMD API
1. define函数
define是AMD的核心函数,用于定义模块:
// 基本语法
define(id?, dependencies?, factory);
无依赖模块
// math.js - 简单的数学工具模块
define(function() {
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
// 返回模块的公共接口
return {
add: add,
multiply: multiply,
PI: 3.14159
};
});
有依赖的模块
// calculator.js - 依赖math模块的计算器
define(['math'], function(math) {
function calculate(operation, a, b) {
switch(operation) {
case 'add':
return math.add(a, b);
case 'multiply':
return math.multiply(a, b);
case 'circle-area':
return math.PI * a * a;
default:
throw new Error('Unknown operation: ' + operation);
}
}
return {
calculate: calculate
};
});
命名模块
// 指定模块ID
define('utils/string', [], function() {
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
function trim(str) {
return str.replace(/^\s+|\s+$/g, '');
}
return {
capitalize: capitalize,
trim: trim
};
});
2. require函数
require用于动态加载和使用模块:
// 异步加载模块
require(['calculator', 'utils/string'], function(calc, stringUtils) {
var result = calc.calculate('add', 5, 3);
var message = stringUtils.capitalize('result is: ' + result);
console.log(message); // "Result is: 8"
});
错误处理
require(['module1', 'module2'],
function(mod1, mod2) {
// 成功回调
console.log('Modules loaded successfully');
},
function(error) {
// 错误回调
console.error('Failed to load modules:', error);
}
);
3. require.config配置
require.config({
// 基础路径
baseUrl: 'js/lib',
// 路径映射
paths: {
'jquery': 'jquery-3.6.0.min',
'underscore': 'underscore-1.13.1.min',
'backbone': 'backbone-1.4.0.min',
'utils': '../utils',
'components': '../components'
},
// 模块依赖配置(用于非AMD库)
shim: {
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
'underscore': {
exports: '_'
}
},
// 超时设置
waitSeconds: 15,
// URL参数
urlArgs: 'bust=' + (new Date()).getTime()
});
常见模式
1. 简单工厂模式
// logger.js
define(function() {
function createLogger(name) {
return {
name: name,
log: function(message) {
console.log('[' + this.name + '] ' + message);
},
error: function(message) {
console.error('[' + this.name + '] ERROR: ' + message);
}
};
}
return {
create: createLogger
};
});
2. 单例模式
// config.js
define(function() {
var instance = null;
function Config() {
if (instance) {
return instance;
}
this.settings = {
apiUrl: '/api',
timeout: 5000,
debug: false
};
instance = this;
return this;
}
Config.prototype.get = function(key) {
return this.settings[key];
};
Config.prototype.set = function(key, value) {
this.settings[key] = value;
};
return new Config();
});
3. 模块继承
// base-view.js
define(function() {
function BaseView(element) {
this.element = element;
this.initialize();
}
BaseView.prototype.initialize = function() {
// 基础初始化逻辑
};
BaseView.prototype.render = function() {
throw new Error('render method must be implemented');
};
BaseView.prototype.destroy = function() {
this.element = null;
};
return BaseView;
});
// user-view.js
define(['base-view'], function(BaseView) {
function UserView(element, user) {
BaseView.call(this, element);
this.user = user;
}
// 继承BaseView
UserView.prototype = Object.create(BaseView.prototype);
UserView.prototype.constructor = UserView;
UserView.prototype.render = function() {
this.element.innerHTML = '<h1>' + this.user.name + '</h1>';
return this;
};
return UserView;
});
4. 插件模式
// text插件 - 加载文本文件
define('text', function() {
return {
load: function(name, req, onload, config) {
var xhr = new XMLHttpRequest();
xhr.open('GET', name, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
onload(xhr.responseText);
} else {
onload.error(xhr);
}
}
};
xhr.send();
}
};
});
// 使用text插件
define(['text!templates/user.html'], function(userTemplate) {
return {
render: function(user) {
return userTemplate.replace('{{name}}', user.name);
}
};
});
实际应用示例
1. 模块化应用结构
// app.js - 应用入口
require.config({
baseUrl: 'js',
paths: {
'jquery': 'lib/jquery-3.6.0.min',
'router': 'app/router',
'views': 'app/views',
'models': 'app/models',
'utils': 'app/utils'
}
});
require(['jquery', 'router'], function($, Router) {
$(document).ready(function() {
var router = new Router();
router.start();
});
});
// app/router.js
define(['jquery', 'views/home', 'views/user'], function($, HomeView, UserView) {
function Router() {
this.routes = {
'': 'home',
'user/:id': 'user'
};
this.views = {
home: HomeView,
user: UserView
};
}
Router.prototype.start = function() {
this.bindEvents();
this.navigate(window.location.hash);
};
Router.prototype.bindEvents = function() {
var self = this;
$(window).on('hashchange', function() {
self.navigate(window.location.hash);
});
};
Router.prototype.navigate = function(hash) {
var route = hash.replace('#', '');
var viewName = this.matchRoute(route);
if (viewName && this.views[viewName]) {
var View = this.views[viewName];
new View().render();
}
};
Router.prototype.matchRoute = function(route) {
// 简化的路由匹配
for (var pattern in this.routes) {
if (pattern === route || pattern === '') {
return this.routes[pattern];
}
}
return null;
};
return Router;
});
2. 组件化开发
// components/modal.js
define(['jquery'], function($) {
function Modal(options) {
this.options = $.extend({
title: '',
content: '',
closable: true,
width: 400,
height: 300
}, options);
this.element = null;
this.isVisible = false;
}
Modal.prototype.create = function() {
if (this.element) {
return this;
}
this.element = $('<div class="modal">')
.css({
position: 'fixed',
top: '50%',
left: '50%',
width: this.options.width,
height: this.options.height,
marginTop: -this.options.height / 2,
marginLeft: -this.options.width / 2,
backgroundColor: 'white',
border: '1px solid #ccc',
boxShadow: '0 4px 8px rgba(0,0,0,0.1)',
zIndex: 1000,
display: 'none'
});
var header = $('<div class="modal-header">')
.css({
padding: '10px',
borderBottom: '1px solid #eee',
fontWeight: 'bold'
})
.text(this.options.title);
var content = $('<div class="modal-content">')
.css({
padding: '10px',
height: this.options.height - 80
})
.html(this.options.content);
this.element.append(header, content);
if (this.options.closable) {
var closeBtn = $('<button>×</button>')
.css({
position: 'absolute',
top: '5px',
right: '10px',
border: 'none',
background: 'none',
fontSize: '18px',
cursor: 'pointer'
})
.click(this.hide.bind(this));
header.append(closeBtn);
}
$('body').append(this.element);
return this;
};
Modal.prototype.show = function() {
if (!this.element) {
this.create();
}
this.element.fadeIn();
this.isVisible = true;
return this;
};
Modal.prototype.hide = function() {
if (this.element) {
this.element.fadeOut();
}
this.isVisible = false;
return this;
};
Modal.prototype.destroy = function() {
if (this.element) {
this.element.remove();
this.element = null;
}
this.isVisible = false;
return this;
};
return Modal;
});
// 使用Modal组件
require(['components/modal'], function(Modal) {
var modal = new Modal({
title: 'Welcome',
content: '<p>Welcome to our application!</p>',
width: 500,
height: 200
});
$('#open-modal').click(function() {
modal.show();
});
});
3. 数据模型
// models/user.js
define(['utils/ajax'], function(ajax) {
function User(data) {
this.id = data.id || null;
this.name = data.name || '';
this.email = data.email || '';
this.createdAt = data.createdAt || null;
}
User.prototype.save = function() {
var url = this.id ? '/api/users/' + this.id : '/api/users';
var method = this.id ? 'PUT' : 'POST';
return ajax.request({
url: url,
method: method,
data: this.toJSON()
}).then(function(response) {
this.id = response.id;
return this;
}.bind(this));
};
User.prototype.destroy = function() {
if (!this.id) {
return Promise.reject(new Error('Cannot delete user without ID'));
}
return ajax.request({
url: '/api/users/' + this.id,
method: 'DELETE'
});
};
User.prototype.toJSON = function() {
return {
id: this.id,
name: this.name,
email: this.email,
createdAt: this.createdAt
};
};
// 静态方法
User.findById = function(id) {
return ajax.request({
url: '/api/users/' + id,
method: 'GET'
}).then(function(data) {
return new User(data);
});
};
User.findAll = function() {
return ajax.request({
url: '/api/users',
method: 'GET'
}).then(function(data) {
return data.map(function(userData) {
return new User(userData);
});
});
};
return User;
});
AMD vs 其他模块系统
1. AMD vs CommonJS
// CommonJS (同步)
var math = require('./math');
var result = math.add(2, 3);
// AMD (异步)
define(['math'], function(math) {
var result = math.add(2, 3);
});
2. AMD vs ES模块
// ES模块
import { add } from './math.js';
const result = add(2, 3);
// AMD
define(['math'], function(math) {
var result = math.add(2, 3);
});
优缺点分析
优点
- ✅ 异步加载: 不阻塞页面渲染,提升用户体验
- ✅ 依赖管理: 自动处理模块依赖顺序
- ✅ 浏览器原生: 无需预编译,直接在浏览器中运行
- ✅ 插件系统: 支持加载各种资源类型
- ✅ 配置灵活: 丰富的配置选项满足不同需求
缺点
- ❌ 语法复杂: 相比ES模块语法较为繁琐
- ❌ 回调嵌套: 复杂依赖可能导致回调地狱
- ❌ 性能开销: 运行时依赖解析有一定性能成本
- ❌ 调试困难: 异步加载增加调试复杂度
- ❌ 生态衰落: 现代开发更倾向于ES模块
总结
AMD作为早期的模块化解决方案,为浏览器端JavaScript模块化开发做出了重要贡献:
- 🎯 适用场景: 需要浏览器端异步加载的传统项目
- 🎯 学习价值: 理解模块化发展历程和设计思想
- 🎯 现状: 逐渐被ES模块和现代构建工具取代
虽然AMD在现代开发中使用较少,但理解其设计原理对于掌握JavaScript模块化发展历程仍然很有价值。
下一章: RequireJS详解 →