优秀的编程知识分享平台

网站首页 > 技术文章 正文

requireJS 实战(requirejs define)

nanyue 2024-10-24 11:44:55 技术文章 3 ℃

require 实战

下面我将化整为零的去讲解requireJS在一个项目的具体使用方式以及需要注意的事项。


引入requireJS

通过 <script> 标签,将require.js 文件引入到当前的 HTML 页面中


<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>RequireJS 实战</title>
</head>
<body>
 <script src="js/require.js"></script>
</body>
</html>




参数配置

requireJS 常用的方法与命令也就两个, 因此requireJS使用起来非常简单。

require

define

其中define是用于定义模块, 而require是用于载入模块以及载入配置文件。


在requireJS中一个文件就是一个模块, 并且文件名就是该模块的ID, 其表现则是以key/value的键值对格式, key即模块的名称(模块ID), 而value则是文件(模块)的地址,

因此多个模块便有多个键值对值, 这些键值对再加上一些常用的参数, 便是require的配置参数, 这些配置参数我们通常会单独保存在一个JS文件中, 方便以后修改、调用, 所以这个文件我们也称之为"配置文件"。


下面是requireJS的基本参数配置:


//index.html
<script>
require.config({
 baseUrl:'js/',
 paths:{
 'jquery':'http://xxxx.xxx.com/js/jquery.min',
 'index':'index'
 }
});

require(['index']);
</script>



require.config() 是用于配置参数的核心方法, 它接收一个有固定格式与属性的对象作为参数, 这个对象便是我们的配置对象。

在配置对象中 baseUrl 定义了基准目录, 它会与 paths中模块的地址自动进行拼接, 构成该模块的实际地址, 并且当配置参数是通过script标签嵌入到html文件中时, baseUrl默认的指向路径就是该html文件所处的地址。

paths 属性的值也是一个对象, 该对象保存的就是模块key/value值。其中key便是模块的名称与ID, 一般使用文件名来命名, 而value则是模块的地址, 在requireJS中, 当模块是一个JS文件时,

是可以省略 .js 的扩展名, 比如 "index.js" 就可以直接写成 "index" 而当定义的模块不需要与 baseUrl 的值进行拼接时, 可以通过 "/" 与 http:// 以及 .js 的形式来绕过 baseUrl的设定。

示例:


require.config({
 baseUrl:'js/',
 paths:{
 'jquery':'http://xxx.xxxx.com/js/jquery.min',
 'index':'index'
 }
});
require(['index']);



实际上, 除了可以在require.js加载完毕后, 通过require.config()方法去配置参数, 我们也可以在require.js加载之前, 定义一个全局的对象变量 require 来事先定义配置参数。

然后在require.js被浏览器加载完毕后, 便会自动继承之前配置的参数。

<script>
 var require = {
 baseUrl: 'js/',
 paths: {
 'jquery': 'http://xxx.xxxx.com/js/jquery.min',
 'index': 'index'
 },
 deps:[index]
 };
</script>
<script src="js/require.js"></script>


不论是在require.js加载之前定义配置参数, 还是之后来定义, 这都是看看我们需求而言的, 这里我们举例的配置参数都是放入到script标签中, 然后嵌入到HTML页面的内嵌方式,

在实际使用时, 我们更多的则是将该段配置提取出来单独保存在一个文件中, 并将其取名为 app.js, 而这个 app.js 便是我们后面常说到的配置文件。


另外还有一个"接口文件"的概念, requireJS中, 所谓接口文件指的便是require.js加载完毕后第一个加载的模块文件。


加载配置文件

现在我们知道require的配置有两种加载方式,一种是放入到script标签嵌入到html文件中,另一种则是作为配置文件 app.js 来独立的引入。

独立的引入配置文件也有两种方式,一种是通过script标签加载外部JS文件形式:

<script src="js/require.js"></script>
<script src="js/app.js"></script>



另一种方式则是使用 require 提供的 data-main 属性, 该属性是直接写在引入require.js的script标签上, 在require.js 加载完毕时, 会自动去加载配置文件 app.js。

<script data-main="js/app" src="js/require.js"></script>

通过 data-main 去加载入口文件, 便会使配置对象中的 baseUrl 属性默认指向地址改为 app.js 所在的位置, 相比之下我更加推荐这种方式, 因为它更可能的方便快捷。




当我们的项目足够的庞大时, 我也会推荐将入口文件作为一个普通的模块, 然后在这个模块中, 根据业务的不同再去加载不同的配置文件。


//define.js
define(['app1','app2','app3','app4'],function(app1,app2,app3,app4){
 if(page == 'app1'){
 require.config(app1);
 }else if(page == 'app2'){
 require.config(app2);
 }else if(page == 'app3'){
 require.config(app3);
 }else{
 require.config(app4);
 }
})


定义模块

在我们选择requireJS来模块化开发我们的项目或者页面时, 就要明确的知道我们以后所编写的代码或者是某段功能, 都是要放在一个个定义好的模块中。

下面是requireJS定义模块的方法格式:

define([id,deps,] callback);


ID:模块的ID, 默认的便是文件名, 一般无需使用者自己手动指定。

deps:当前模块所以依赖的模块数组, 数组的每个数组元素便是模块名或者叫模块ID。

callback:模块的回调方法, 用于保存模块具体的功能与代码, 而这个回调函数又接收一个或者多个参数, 这些参数会与模块数组的每个数组元素一一对应, 即每个参数保存的是对应模块返回值。


根据 define() 使用时参数数量的不同, 可以定义以下几种模块类型:

简单的值对

当所要定义的模块没有任何依赖也不具有任何的功能, 只是单纯的返回一组键值对形式的数据时, 便可以直接将要返回的数据对象写在 define方法中:


define({
 'color':'red',
 'size':'13px',
 'width':'100px'
});


这种只为保存数据的模块,我们称之为“值对”模块,实际上值对模块不仅可以用于保存数据,还可以保存我们的配置参数,然后在不同的业务场景下去加载不同的配置参数文件。

示例:

//app1.js
define({
 baseUrl:'music/js/',
 paths:{
 msuic:'music',
 play:'play'
 }
});

//app2.js
define({
 baseUrl:'video/js/',
 paths:{
 video:'video',
 play:'play'
 }
});



非依赖的函数式定义

如果一个模块没有任何的依赖, 只是单纯的执行一些操作, 那么便可以直接将函数写在 define方法中:

define(function(require,exports,modules){
 // do something
 return {
 'color':'red',
 'size':'13px'
 }
});


依赖的函数式定义

这种带有依赖的函数式模块定义, 也是我们平时常用到的, 这里我们就结合实例, 通过上面所举的 index 模块为例:

//index.js
define(['jquery','./utils'], function($) {
 $(function() {
 alert($);
 });
});

从上面的示例中我们可以看出 index 模块中, 依赖了 'jquery' 模块, 并且在模块的回调函数中, 通过 $ 形参来接收 jquery模块返回的值, 除了 jquery 模块, index模块还依赖了 utils 模块,

因为该模块没有在配置文件中定义, 所以这里以附加路径的形式单独引入进来的。



载入模块

在说载入模块之前, 我们先聊聊"模块依赖"。模块与模块之间存在着相互依赖的关系, 因此就决定了不同的加载顺序, 比如模块A中使用到的一个函数是定义在模块B中的,

我们就可以说模块A依赖模块B, 同时也说明了在载入模块时, 其顺序也是先模块A, 再模块B。

在require中, 我们可以通过 require() 方法去载入模块。其使用格式如下:

require(deps[,callback]);

deps:所要载入的模块数组。

callback:模块载入后执行的回调方法。


这里就让我们依然使用上述的 index 模块为例来说明

示例:

require.config({
 paths:{
 'index':'index'
 }
});

require(['index']);



requireJS 通过 require([]) 方法去载入模块, 并执行模块中的回调函数, 其值是一个数组, 数组中的元素便是要载入的模块名称也就是模块ID,

这里我们通过 require(['index']) 方法载入了 index 这个模块, 又因为该模块依赖了 jquery 模块, 所以接着便会继续载入jquery模块, 当jquery模块加载完成后,

便会将自身的方法传递给形参 $ 最后执行模块的回调方法, alert出$参数具体内容。



这里我们可以小小的总结一下,实现模块的载入除了 require([],fn) 的主动载入方法,通过依赖也可以间接载入对应的模块,但是相比较而言require方式载入模块在使用上更加灵活,它不仅可以只载入模块不执行回调,也可以载入模块然后执行回调,还可以在所定义的模块中,按需载入所需要用到的模块,并且将模块返回的对象或方法中保存在一个变量中,以供使用。


这种按需载入模块, 也叫就近依赖模式, 它的使用要遵循一定的使用场景:

当模块是非依赖的函数式时,可以直接使用


define(function(require,exports,modules){
 var utils = require('utils');
 utils.sayHellow('hellow World')
})

当模块是具有依赖的函数式时, 只能够以回调的形式处理。

define(['jquery'], function($) {
 $(function() {
 require(['utils'],function(utils){
 utils.sayHellow('Hellow World!');
 });
 });
});



当然聪明伶俐的你, 一定会想到这样更好的办法:

define(['jquery','require','exports','modules'], function($,require,exports,modules) {
 $(function() {
 //方式一
 require(['utils'],function(utils){
 utils.sayHellow('Hellow World!');
 });
 //方式二:
 var utils = require('utils');
 utils.sayHellow('hellow World')
 });
});



模块的返回值

require中定义的模块不仅可以返回一个对象作为结果,还可以返回一个函数作为结果。实现模块的返回值主要有两种方法:

return 方式

// utils.js
define(function(require,exports,modules){
 function sayHellow(params){
 alert(params);
 }

 return sayHellow
});
// index.js
define(function(require,exports,modules){
 var sayHellow = require('utils');
 sayHellow('hellow World');
})



如果通过return 返回多种结果的情况下:

// utils.js
define(function(require,exports,modules){
 function sayHellow(params){
 alert(params);
 }
 function sayBye(){
 alert('bye-bye!');
 }
 return {
 'sayHellow':sayHellow,
 'sayBye':sayBye
 }
});

// index.js
define(function(require,exports,modules){
 var utils = require('utils');
 utils.sayHellow('hellow World');
})


exports导出

// utils.js
define(function(require,exports,modules){
 function sayHellow(params){
 alert(params);
 }
 exports.sayHellow = sayHellow;
})
// index.js
define(function(require,exports,modules){
 var utils = require('utils');
 utils.sayHellow('hellow World');
});


这里有一个注意的地方, 那就是非依赖性的模块, 可以直接在模块的回调函数中, 加入以下三个参数:

require:加载模块时使用。

exports:导出模块的返回值。

modules:定义模块的相关信息以及参数。



非标准模块定义

在 require.config() 方法的配置对象中有一个 shim 属性, 它的值是一个对象, 可以用于声明非标准模块的依赖和返回值。

所谓的 "非标准模块" 指的是那些不符合的AMD规范的JS插件。

下面我们先看看基本的 shim 配置参数:

require.config({
 baseUrl:'js/',
 paths:{
 'jquery':'http://xxx.xxxx.com/js/jquery.min',
 'index':'index',
 'say':'say',
 'bar':'bar',
 'tools':'tools'
 },
 shim:{
 'tools':{
 deps:['bar'],
 exports:'tool'
 },
 'say':{
 deps:['./a','./b'],
 init:function(){
 return {
 'sayBye':sayBye,
 'sayHellow':sayHellow
 }
 }
 }
 }
});

require(['index']);


这里需要注意的是如果所加载的模块文件是符合AMD规范, 比如通过 define 进行定义的, 那么require默认的优先级将是标准的, 只有在不符合标准的情况下才会采用shim中定义的参数。


在 index 模块执行时:

define(['jquery','tool','say'],function($,tool,say){
 tool.drag();
 say.sayHellow();
 say.sayBye();
})

上面的示例中, 关于 shim 中有三个重要的属性, 它们分别是:

deps: 用于声明当前非标准模块所依赖的其它模块, 值是一个数组, 数组元素是模块的名称或者是ID。

exports:用于定义非标准模块的全局变量或者方法。值一般是一个字符串。

init:用于初始, 处理, 非标准模块的全局变量或者是方法, 常用于当非标准模块存在多个全局变量以及方法, 值是一个函数。



常用参数

在 require.config 中还存在其他的常用属性设置。


urlArgs

RequireJS获取资源时附加在URL后面的额外的query参数。作为浏览器或服务器未正确配置时的“cache bust”手段很有用。使用cache bust配置的一个示例:

javascript:;urlArgs: "bust=" + (new Date()).getTime()

在开发中这很有用,但请记得在部署到生成环境之前移除它。


scriptType

指定RequireJS将script标签插入document时所用的type=""值。默认为“text/javascript”。想要启用Firefox的JavaScript 1.8特性,可使用值“text/javascript;version=1.8”。


waitSeconds

通过该参数可以设置requireJS在加载脚本时的超时时间,它的默认值是7,即如果一个脚本文件加载时长超过7秒钟,便会放弃等待该脚本文件,从而报出timeout超时的错误信息,考虑到国内网络环境不稳定的因素,所以这里我建议设置为0。当然一般不需要去改动它,除非到了你需要的时候。


deps

用于声明require.js在加载完成时便会自动加载的模块, 值是一个数组, 数组元素便是模块名。


callback

当deps中的自动加载模块加载完毕时,触发的回调函数。


config

config属性可以为模块配置额外的参数设定,其使用格式就是以模块名或者模块ID为key,然后具体的参数为value。


//app.js
require.config({
 baseUrl:'js/',
 paths:{
 'jquery':'http://xx.xxxx.com/js/jquery.min',
 'index':'index'
 },
 config:{
 'index':{
 'size':13,
 'color':'red'
 }
 }
});


//index.js
define(['jquery','module'],function($,module){
 console.log(module.config().size)
});

这里要引起我们注意的地方就是依赖的'module'模块, 它是一个预先定义好的值, 引入该值, 在当前模块下便可以调用module对象, 从该对象中执行 config() 方法便可以生成改模块的参数对象。




实战:

<script data-main="js/app.js" src="js/require.js"></script>


app.js为入口函数, 其代码为:

requirejs.config({
 //默认情况下模块所在目录为js/lib
 baseUrl: 'js/lib',
 //当模块id前缀为app时,他便由js/app加载模块文件
 //这里设置的路径是相对与baseUrl的,不要包含.js
 paths: {
 app: '../app'
 }
});

// 开始逻辑.
requirejs(['jquery', 'canvas', 'app/sub'],
function ($, canvas, sub) {
 //jQuery, canvas and the app/sub module are all
 //loaded and can be used here now.
});


实战2:

文件功能

require.js:

其中requirejs的核心代码就是require.js文件,可以从官方网站上下载:


config.js:

用于配置requirejs的相关内容,可以设置文件目录,加载模块命名匹配,以及一些依赖关系等等。


index.html:

我们的测试页面或者网址首页。


/lib/a.js和/lib/b.js 以及 /others/c.js

是测试的模块js文件。



index.html

<doctype html>
<html>
 <meta http-quiv="Content-Type" content="text/html;charset=UTF-8">
 <script data-main="config" src="require.js"></script>
</html>
<body>
 <h1>This is test for RequireJS</h1>
 <script type="text/javascript">
 alert("index");
 </script>
</body>


其中, data-main指定主要的配置文件, src为requirejs的文件。


config.js
require.config({
 baseUrl:'/', /* 项目必须放到根目录下才能正确执行 */
 paths:{
 "a":"lib/a",
 "b":"lib/b",
 "c":"others/c"
 }
 });
require(['a','b','c'],function(a,b,c){
 a.hello();
 b.hello();
 c.hello();
});


baseUrl指定所有文件的主要目录, paths配置模块名字以及其匹配的加载路径。

当有需要使用某些模块时, 就可以通过require([xxx],function(xxx){xxx});的方式使用。


a.js
define([], function() {
 return {
 hello: function() {
 alert("hello, a");
 }
 }
});
b.js
define([], function() {
 return {
 hello: function() {
 alert("hello, b");
 }
 }
});
c.js
define([], function() {
 return {
 hello: function() {
 alert("hello, c");
 }
 }
});




模块化文件, 一般是一个功能是一个文件。文件的名字, 就是上面requireconfig中配置的模块名字。require加载文件时, 会自动加上.js后缀。

当某些模块依赖其他模块时, 可以通过define([xxx],function(xxx){yyy});的方式添加依赖关系, require会在异步加载后, 自动调整次序。

当访问index.html时, 会先加载require.js然后把需要加载的文件都通过appendChild的方式, 添加到index.html的底部。

因此会先弹出index对话框, 当执行config.js的后半部分代码时, 会依次使用a.js b.js c.js, 因此也会依次弹出三个对话框。

Tags:

最近发表
标签列表