JavaScript模块化编程实战:require.js用法详解
本文深入解析JavaScript模块化编程中require.js的实战应用。文章从传统脚本加载的痛点出发,详细阐述了require.js如何实现异步加载与依赖管理。内容涵盖require.js的引入方式、主模块main.js的编写规范、依赖模块的加载策略,以及通过require.config()配置路径与shim处理非AMD规范库的方法。此外,还介绍了domready、text等常用插件的使用,帮助开发者构建高效、可维护的前端项目。
引言
在前两篇文章中,我们探讨了JavaScript模块的原型与理论基础。本文将聚焦于实战环节,展示如何将这些理论应用于实际开发中。
为了实现模块化编程,我们选择使用目前非常流行的require.js库。
一、为什么要用require.js?
早期开发中,所有JavaScript代码通常被封装在单一文件内,只需加载该文件即可运行。然而,随着项目规模扩大,代码量激增,单个文件已无法满足需求,必须将代码拆分为多个文件并按顺序加载。以下网页代码示例展示了这一过程,相信许多开发者都曾见过类似写法。
具体代码示例如下:
<script src="1.js"></script>
<script src="2.js"></script>
<script src="3.js"></script>
<script src="4.js"></script>
<script src="5.js"></script>
<script src="6.js"></script> 上述代码展示了依次加载多个JavaScript文件的过程。
这种传统写法存在显著缺陷。首先,在加载过程中,浏览器会暂停网页渲染,加载的文件越多,网页失去响应的时间就越长。其次,JavaScript文件之间往往存在依赖关系,因此必须严格保证加载顺序(例如上例中1.js必须在2.js之前加载),依赖度最高的模块需置于最后加载。当依赖关系复杂时,代码的编写与维护难度将大幅增加。
require.js的诞生,就是为了解决这两个问题:
(1)实现JavaScript文件的异步加载,避免网页失去响应;
(2)管理模块之间的依赖性,便于代码的编写和维护。
二、require.js的加载
require.js的引入与配置
使用require.js的第一步,是前往官方网站下载最新版本。
下载完成后,假设将其放置在js子目录下,即可进行加载。
具体加载代码如下:
<script src="js/require.js"></script> 有人可能会担心,加载该文件同样可能导致网页失去响应。对此有两个解决方案:一是将脚本置于网页底部加载;二是采用如下写法:
具体代码如下:
<script src="js/require.js" defer async="true" ></script> async属性表明该文件需异步加载,从而避免网页失去响应。由于IE浏览器不支持async属性,仅支持defer属性,因此同时写入defer以确保兼容性。
加载require.js后,下一步是加载我们自己的代码。假设我们的代码文件为main.js,同样置于js目录下。此时,只需按如下方式编写:
具体代码如下:
<script src="js/require.js" data-main="js/main"></script> data-main属性的作用是指定网页程序的主模块。在上例中,即js目录下的main.js文件,该文件将由require.js优先加载。由于require.js默认文件后缀名为js,因此可以将main.js简写为main。
三、主模块的写法
主模块main.js的编写
上一节提到的main.js,我们称之为“主模块”,意指整个网页的入口代码。它类似于C语言中的main()函数,所有代码均从此处开始运行。
接下来,我们探讨如何编写main.js。
若我们的代码不依赖任何其他模块,则可直接写入JavaScript代码。
// main.js
具体代码如下:
alert("加载成功!"); 然而,若仅如此编写,则无需使用require.js。更常见的情况是,主模块依赖于其他模块,此时需使用AMD规范定义的require()函数。
// main.js
具体代码如下:
require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
// some code here
}); require()函数接受两个参数。第一个参数为数组,表示所依赖的模块,如上例中的['moduleA', 'moduleB', 'moduleC'],即主模块依赖这三个模块;第二个参数为回调函数,当前面指定的模块全部加载成功后,该函数将被调用。加载的模块将以参数形式传入该函数,从而在回调函数内部即可使用这些模块。
require()异步加载moduleA、moduleB和moduleC,浏览器不会失去响应;其指定的回调函数,仅当所有前置模块加载成功后才会执行,从而解决了依赖性问题。
接下来,我们看一个实际案例。
假设主模块依赖jquery、underscore和backbone这三个模块,main.js可如下编写:
具体代码如下:
require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone){
// some code here
}); require.js会先加载jQuery、underscore和backbone,随后运行回调函数。主模块的代码即编写在回调函数中。
四、模块的加载
模块加载与路径配置
上一节示例中,主模块的依赖模块为['jquery', 'underscore', 'backbone']。默认情况下,require.js假定这三个模块与main.js位于同一目录,文件名分别为jquery.js、underscore.js和backbone.js,并自动加载。
通过require.config()方法,我们可以对模块的加载行为进行自定义。require.config()应置于主模块(main.js)头部,其参数为一个对象,该对象的paths属性用于指定各模块的加载路径。
具体代码如下:
require.config({
paths: {
"jquery": "jquery.min",
"underscore": "underscore.min",
"backbone": "backbone.min"
}
}); 上述代码给出了三个模块的文件名,路径默认与main.js位于同一目录(js子目录)。若这些模块位于其他目录,例如js/lib目录,则有两种写法。一种是逐一指定路径。
具体代码如下:
require.config({
paths: {
"jquery": "lib/jquery.min",
"underscore": "lib/underscore.min",
"backbone": "lib/backbone.min"
}
}); 另一种方法是直接改变基目录(baseUrl)。
具体代码如下:
require.config({
baseUrl: "js/lib",
paths: {
"jquery": "jquery.min",
"underscore": "underscore.min",
"backbone": "backbone.min"
}
}); 若某个模块位于另一台主机上,也可直接指定其网址,例如:
具体代码如下:
require.config({
paths: {
"jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min"
}
}); require.js要求每个模块为独立的js文件。因此,若加载多个模块,将发出多次HTTP请求,影响网页加载速度。为此,require.js提供了一个优化工具,在模块部署完毕后,可利用该工具将多个模块合并至一个文件中,从而减少HTTP请求数。
五、AMD模块的写法
AMD模块定义与依赖管理
require.js加载的模块遵循AMD规范。也就是说,模块必须按AMD规定编写。
具体而言,模块必须采用特定的define()函数来定义。若一个模块不依赖其他模块,则可直接在define()函数中定义。
假设现有math.js文件,它定义了一个math模块。那么,math.js应如下编写:
// math.js
具体代码如下:
define(function (){
var add = function (x,y){
return x+y;
};
return {
add: add
};
}); 加载方法如下:
// main.js
具体代码如下:
require(['math'], function (math){
alert(math.add(1,1));
}); 若该模块还依赖其他模块,则define()函数的第一个参数必须为数组,指明该模块的依赖性。
具体代码如下:
define(['myLib'], function(myLib){
function foo(){
myLib.doSomething();
}
return {
foo : foo
};
}); 当require()函数加载上述模块时,会先加载myLib.js文件。
六、加载非规范的模块
非AMD规范模块的处理
理论上,require.js加载的模块必须是按照AMD规范、用define()函数定义的模块。但实际上,尽管部分流行函数库(如jQuery)符合AMD规范,更多库并不符合。
那么,require.js是否能够加载非规范的模块呢?答案是肯定的。
此类模块在使用require()加载前,需先通过require.config()方法定义其特征。
举例而言,underscore和backbone这两个库均未采用AMD规范编写。若要加载它们,必须先定义其特征。
具体代码如下:
require.config({
shim: {
'underscore':{
exports: '_'
},
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
}
}
}); require.config()接受一个配置对象,该对象除前述的paths属性外,还有一个shim属性,专门用于配置不兼容的模块。具体而言,每个模块需定义(1)exports值(输出的变量名),表明该模块外部调用时的名称;(2)deps数组,表明该模块的依赖性。
例如,jQuery插件可如下定义:
具体代码如下:
shim: {
'jquery.scroll': {
deps: ['jquery'],
exports: 'jQuery.fn.scroll'
}
} 七、require.js插件
require.js插件应用
require.js还提供一系列插件,以实现特定功能。
domready插件可使回调函数在页面DOM结构加载完成后运行。
具体代码如下:
require(['domready!'], function (doc){
// called once the DOM is ready
}); text和image插件则允许require.js加载文本和图片文件。
具体代码如下:
define([
'text!review.txt',
'image!cat.jpg'
],
function(review,cat){
console.log(review);
document.body.appendChild(cat);
}
); 类似的插件还有json和mdown,用于加载json文件和markdown文件。
(完)
什么时候该用JS构造器:与工厂模式、字面量的选择对比
2026-11-05 16:18:22
推荐几款轻量级JS照片墙开源库及对比
2026-11-05 15:56:19
JavaScript递归写法有哪些常见陷阱及避免方法
2026-11-05 14:53:38
JS数组浅拷贝与深拷贝怎么选?场景与方法总结
2026-11-05 14:41:02
p5.js 中 setup() 和 draw() 函数的执行机制详解
2026-11-05 12:32:38
