JavaScript模块化编程实战:require.js用法详解

作者:闭包旅人

时间:2026-10-27

浏览:406

本文深入解析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文件。


(完)

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章