JavaScript 模块化开发实战:Sea.js 与 Require.js 详解

作者:函数茶馆

时间:2026-10-23

浏览:279

本文详解 JavaScript 模块化开发中的 Sea.js 与 Require.js 库。涵盖模块定义(define)、依赖管理(require)、接口暴露(exports/module)及异步加载(require.async)。深入解析 Sea.js 的 config 配置项(alias、paths、map等)及插件使用,并对比 Require.js 的 AMD 规范实现与 shim 配置,帮助开发者解决 JS 冲突与依赖问题。

文章目录

本文通过具体实例解析 JavaScript 模块化开发的核心概念与实现方式,旨在为开发者提供实用的参考指南。

在 JavaScript 开发过程中,常面临代码冲突与依赖管理两大挑战。

  • 当多个脚本文件同时加载时,全局变量或函数名可能发生覆盖,导致命名冲突。

  • 脚本文件之间往往存在相互调用的关系,形成复杂的依赖链条。

  • 传统的 JS 引入方式直接导致文件间产生隐式依赖,难以维护。

一种常见的解决方案是使用命名空间来隔离变量,减少冲突概率。

  • 然而,仅依靠命名空间存在明显的局限性。

  • 调用时路径或名称过长,增加了代码的冗余度和维护成本。

  • 这种方法只能降低冲突风险,无法从根本上彻底避免命名冲突问题。

Sea.js 是解决上述问题的有效工具,其核心用法如下。

  1. 首先需在 HTML 中引入 sea.js 库文件。

  2. 使用 define 函数将代码封装为模块,这是 Sea.js 定义模块的标准方式。

  3. 通过 exports 对象暴露模块接口,或利用 seajs.use 方法加载并调用模块。

  4. 在模块内部,使用 require 函数来依赖其他已定义的模块。


//html:
<script src="js/sea.js" type="text/javascript" charset="utf-8"></script>

<script type="text/javascript">

//      console.log( seajs );

seajs.use('./js/main.js',function ( main ) {
  
  
  main.show();
  
});      
  
</script>

//main.js
define(function ( require,exports,module ) {
  
  var header = require('./header.js');
  
  function show () {
    
    alert(123);
    
  }
  
  
  exports.show = show;
  
});

seajs.use() 是 Sea.js 中用于启动加载模块的关键接口。

该方法支持加载单个模块或传入数组加载多个模块,并在加载完成后执行回调函数。


// 加载一个模块
seajs.use('./main.js');

// 加载一个模块,在加载完成时,执行回调
seajs.use('./main.js', function ( main ) {
 
});

// 加载多个模块,在加载完成时,执行回调
seajs.use(['./main.js', './b'], function(a, b) {
 a.doSomething();
 b.doSomething();
});

需注意,seajs.use 的执行时机与 DOM ready 事件无直接关联。若需确保 DOM 加载完成后再执行特定逻辑,应结合 jQuery 等库监听 DOM ready 事件。


seajs.use(['jquery','./main'],function ( $,main ) {
  
  $(function () {

    main.init();

  })

});

seajs.use 的第一个参数为必选,但允许传入 null 或变量名。


var app = ['app.js', 'header.css', 'header.js'];

seajs.use(app, function( app ) {
  //do something
});

seajs.use 专用于模块的初始加载启动,不应出现在 define 定义的模块代码内部。若模块内部需加载其他模块,应使用 require();若需异步加载其他模块,则使用 require.async()。

seajs.use 是模块加载器不可或缺的基础接口。

define 函数用于定义模块,其参数形式灵活多样。

当 define 仅传递一个参数时,该参数即代表模块内容,类型可以是任意值。若传递字符串,则定义为字符串模块;若传递对象,则定义为对象模块。

最常用的是传递一个函数作为参数。该函数默认包含三个参数:Require(用于引用其他模块的工厂方法)、Exports(返回的接口对象)以及 Module(模块自身的属性对象)。

当传递两个或三个参数时,逻辑有所不同。若传递两个参数,第一个为依赖模块的数组集合,最后一个为函数;若传递三个参数,第一个表示模块 ID,第二个表示依赖模块集合,第三个为函数体。


//第一个参数,当前模块的ID,也就是当前模块的地址
//第二个参数,以来模块的地址的数组

define('./main.js',['./drag.js'],function( require,exports,moduels ){
  
  
});

以下对 define 的三个参数进行详细解释。

require 函数用于获取其他模块提供的接口,是模块间通信的核心。

  • require 函数名不可简写。

  • require 不能被重新定义或赋值。具体限制包括:不能赋值给其他变量(如 var req = require)、不能重新定义为函数(function require)、不能作为子函数的参数、也不能在子函数作用域内重新定义。

    示例代码展示了错误地将 require 重命名为 req 的情况。

    示例代码展示了错误地将 require 重新赋值为函数的情况。

    示例代码展示了错误地将 require 作为函数参数的情况。

    示例代码展示了在内部作用域中重新定义 require 的错误写法。

    
     var require = function() {};

    }

  • require 的参数必须是完整的字符串字面量,例如 require('abc' + 'bcd') 这种动态拼接的方式是不被允许的。

exports 对象用于定义模块的对外接口。

  • 它是模块与外部交互的桥梁,用于暴露模块内部的状态或方法。

  • 第一种方式:直接向 exports 添加属性,例如 exports.color = 'red'。

  • 第二种方式:通过 module 对象定义,例如 module.exports.color = 'red'。

  • 第三种方式:通过 module 对象直接赋值新对象,例如 module.exports = {color: 'orange'}。

  • 第四种方式:在函数末尾使用 return 语句返回对象,例如 return {color: 'green'}。

  • 在一个模块中定义接口时,必须统一使用一种方式,严禁混用不同方式。

  • 特别注意,不能直接对 exports 变量赋值新对象,因为这样会切断与 module.exports 的联系,导致接口无法正确暴露。

module 对象包含模块的多种属性信息。

  • 这些属性提供了关于模块本身的元数据。

  • id 属性表示模块的唯一标识符。

  • uri 属性表示该模块文件对应的完整 URL 地址。

  • exports 属性表示模块最终返回的接口集合。

  • dependencies 属性以数组形式表示该模块依赖的其他模块集合。

  • deps 属性以对象形式表示该模块依赖的其他模块集合。

CMD(Common Module Definition)是 Sea.js 遵循的模块定义规范。

在 CMD 规范中,一个模块通常对应一个独立的文件。

define 是一个全局函数,专门用于定义模块。

define 函数的基本调用形式为 define(factory)。

factory 参数可以是函数、对象或字符串类型。


definde( { color: 'tan' } );

当 factory 为函数时,默认会注入三个参数:require、exports 和 module。


definde( function ( require,exports,module ) {
  
} );

define 函数也可接受更多参数,形式为 define([id, deps], factory)。

define 还可以接受两个以上参数,其中字符串 id 表示模块标识,deps 表示模块依赖列表。


define('main',['mina.js'],function ( require,exports,module ) {
  //模块代码
});

id 和 deps 参数若省略,可通过构建工具自动生成。需要注意的是,这两个参数并非 CMD 规范的核心部分。

require 函数用于获取模块接口。

require 是一个方法,接受模块标识作为唯一参数,用于获取其他模块提供的接口。


deifne(function ( requrie,exports ) {
  
  //获取模块 main 的接口
  var main = require('./main');

  //调用 模块 main 中的定义方法
  main.init();

});

require.async(id, [cb]) 用于在模块内部异步加载模块,并在加载完成后执行指定的回调函数。


require.async('./a',function(){});
//加载多个异步模块
require.async(['./a','./b'],function(){});  


//异步加载 所需要的模块
define(function ( require,expotrs,module ) {
  
  var loadSync = false;

  if ( loadSync ) {

    var skin = require.async('./skin');

  } else {

    var header = require.async('./header');

  }

});

exports 用于向外提供模块接口。

通过 exports 对象可以逐步构建模块的对外接口。


define(function ( require, exports,module ) {

 // 对外提供 foo 属性
 exports.color = 'pink';

 // 对外提供 doSomething 方法
 exports.doSomething = function() {};

});

另一种提供接口的方式是使用 return 语句直接返回对象。


define(function () {
  
  //通过 return 直接提供接口
  return {
    color: 'deeppink',
    sayName: function () {}
  }

});

在 CMD 模块中,经常使用的 API 包括:define、require、require.async、exports 以及 module.exports。

Sea.js 提供了丰富的配置选项,通过 seajs.config() 进行设置。

seajs.config() 用于配置 Sea.js 的运行参数。

alias 配置项用于定义模块的别名。

当引用基础库且涉及版本升级时,只需在 alias 中修改别名配置,无需更改所有引用代码。


seajs.config({
  alias: {
    jquery: 'lib/jquery.1.10.1'
  }
});

在模块中通过别名引用模块,例如 require('jquery')。


require('jquery');

paths 配置项用于定义模块的基础路径。

当模块目录结构较深时,可将深层目录定义为简写 path,引用时直接使用 pathName 加模块名称即可。

配置示例展示了如何设置 paths 对象。


paths: {
  hd: './modules/header'
}

});

在模块中通过路径别名引用模块,例如 require('hd/header.js')。


require('hd/header.js');

vars 配置项用于处理运行时确定的模块路径。

在某些情况下,模块路径需在运行时才能确定,此时可使用 vars 变量进行动态配置。


seajs.config({
  vars: {
    skin: 'header/skin'
  }
});

在模块中使用 vars 变量,例如 require('{skin}/skin.js'),需使用 {} 标识变量。


require('{skin}/skin.js'); //需要使用 {} 来标识

map 配置项用于文件映射处理。

开发完成后若需打包或压缩,文件名可能改变。通过 map 映射可将原始文件名映射到实际文件名,从而正确加载文件。


seajs.config({
  map: [
    ['main.js','main-min.js']
  ]
});

seajs.use(['main'],function ( main ) {
  // do ...
});

base 配置项用于设置模块的根目录。

若使用 Sea.js 时页面不在模块根目录下,可通过 base 重新定义该页面模块文件的根目录,简化引用路径。

charset 配置项用于设置模块加载时的编码格式。

通过 charset 配置可指定加载模块时使用的字符编码,如 utf-8。


seajs.config({
  charset: 'utf-8'
});


seajs.config({
 charset: function(url) {

  // xxx 目录下的文件用 gbk 编码加载
  if (url.indexOf('http://example.com/js/xxx') === 0) {
   return 'gbk';
  }

  // 其他文件用 utf-8 编码
  return 'utf-8';

 }
});

常用的配置项包括 alias、paths 和 base,它们能显著提升模块管理的灵活性。

Sea.js 提供了一系列插件以扩展功能。

seajs-preload.js 插件用于预加载资源。

通过引入 seajs-preload.js 脚本,可以实现 JS 文件的预加载。


seajs.config({
  //预加载jquery文件
  preload: ['jquery.js']
}); 

在模块中利用预加载插件加载文件。


definde(function ( require,exports,module ) {
  $(document).css('background','tan');
});

seajs-css.js 插件用于加载 CSS 文件。

该插件支持在 Sea.js 环境中加载样式表文件。

通过引入 seajs-css.js 脚本,可启用 CSS 加载功能。


definde(function ( require,expotrs,module ) {
  require('../css/css.css');
});

Require.js 是另一种流行的 JavaScript 模块加载器,遵循 AMD 规范。

引入模块文件时,Require.js 根据引用了 require 文件的 script 标签中 data-main 属性对应的文件所在目录来确定模块路径。

使用 define 函数定义模块。

define 可传递一个对象,也可传递一个函数(CommonJS 规范的实现)。主要区别在于 module 参数的处理方式。

id 表示模块 ID,无后缀名(注意:Sea.js 有后缀名)。若无依赖模块集合,则不传递 deps 参数。config 配置属性用于额外设置。

模块接口的暴露方式。

在 AMD 规范定义的模块中,没有 exports 参数,只能通过 return 语句将接口暴露出来。

  • return 对象:这种方式可以将模块内部的多个属性和方法暴露出来。

  • return 方法:这种方式将一个方法返回,引用该模块即引用此接口方法。该方法在父模块(引用它的模块)中可直接调用,且处于自身模块的作用域中。

Require.js 的配置选项。

baseUrl 配置项用于设置根目录。

用途:当启动文件不在预期位置时,可通过 baseUrl 设置根目录,确保模块路径解析正确。


require.config({
  baseUrl: 'lib'
});

paths 配置项用于定义简写路径。

通过 paths 配置,可以简化长路径的引用,提高代码可读性。


require.config({
  paths: {
    hd: 'module/header'
  }
});

map 配置项用于处理模块映射。

当存在多个页面且每个页面引用的代码库不同时,可通过 map 配置,为每个文件配置不同的代码库版本。


require.config({
  map: {
    //modules 模块下的dom模块引用的是lib/dom
    'modules': {
      'dom': 'lib/dom'
    },
    //modules-new 模块下的dom模块引用的是lib/dom.2.0
    'modules-new': {
      'dom': 'lib/dom.2.0'
    }
  }

});

使用 CSS 插件加载样式文件。

Require.js 提供了加载 CSS 的插件,支持样式模块化。


//配置
require.config({
  map: {
    '*': {
      'css': 'lib/css'
    }
  }
}); 


//使用 

define(['css!css/index.css'],function () {
});

shim 配置项用于定义非 AMD 模块的依赖关系。

shim 表示配置模块依赖关系。对于一些定义在全局作用域下的代码库,常因无法获取模块对象而难以管理。通过定义 shim 的模块依赖关系,可在模块中获取到这类全局代码库。


require.config({
  shim: {
    'lib/jquery': {
      //依赖的模块集合
      deps: [],
      expotrs: '$'  
    }
  }
});

感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun测试上述代码运行效果。

更多关于 JavaScript 相关内容感兴趣的读者可查看本站专题:《javascript 面向对象入门教程》、《JavaScript 错误与调试技巧总结》、《JavaScript 数据结构与算法技巧总结》、《JavaScript 遍历算法与技巧总结》及《JavaScript 数学运算用法总结》。

希望本文所述的模块化开发技巧对大家 JavaScript 程序设计有所帮助。

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章