首頁 > web前端 > js教程 > 一份超詳細的requireJS介紹及應用

一份超詳細的requireJS介紹及應用

零到壹度
發布: 2018-04-13 17:02:15
原創
7835 人瀏覽過

requireJS 可以很輕易的將一個專案中的JavaScript程式碼分割成若干個模組(module)。並且requireJS推薦一個模組就是一個文件,所以,你將獲得一些零碎的具有相互依賴關係的JS文件。模組化的好處也淺顯意見,那就是大幅增強程式碼的可讀性、易維護性、可擴展性、減少全局污染等。

目錄:

基本概念
#requireJS的歷史發展
##模組化的優點
require 實戰    引入requireJS
    參數設定
    載入設定檔
    訂模組# ##    所依賴的函數式定義
    載入模組
    模組的回值定義
          return常用參數
          urlArgs
#          scriptType
          waitSeconds
          deps
          map
          packages

rquire 壓縮

##它問題

#    1 . timeout逾時問題
    2. 循環依賴問題
    3. CDN回退
    4.定義AMD插件
    5. 關於require的預定義模組# .本地文件的問題    7. 關於R.js - shim功能的說明    8.因為自身設計的問題



因為自身設計的問題




##因為自身設計的問題


##因為自身設計的問題。不足,JavaScript 這門語言其實並沒有模組化這種概念與機制,所以想實現如JAVA,PHP等一些後台語言的模組化開發,那麼我們必須藉助requireJS 這個前端模擬模組化的插件,雖然我們不需要去了解它的實現原理,但是大致去了解它是如何運作的,我相信這會讓我們更容易上手。

requireJS使用head.appendChild()将每一个依赖加载为一个script标签。
requireJS等待所有的依赖加载完毕,计算出模块定义函数正确调用顺序,然后依次调用它们。
登入後複製

requireJS的歷史發展

在說JavaScript模組化之前,我們必須提CommonJS(原名叫ServerJs),這個社群可謂大牛雲集,他們為NodeJS制定過模組化規範Modules/1.0 ,並得到了廣泛的支持。在為JavaScript客製化模組化規範時,討論的都是在Modules/1.0 上進行改進,但是Modules/1.0 是專門為服務端制定的規範,所以要想套用在客服端環境的JS上,情況就會有很大的不同,例如,對於服務端加載一個JS文件,其耗費的時間幾乎都是可以忽略不計的,因為這些都是基於本地環境,而在客戶端瀏覽器上加載一個文件,都會發送一個HTTP請求,並且還可能存在跨域的情況,也就是說資源的加載,到執行,是會存在一定的時間消耗與延遲。

所以社群的成員們意識到,要想在瀏覽器環境中也能模組化開發,則需要對現有規範進行更改,而就在社群討論制定規範的時候內部發生了比較大的分歧,分裂出了三個主張,漸漸的形成三個不同的派別:

1.Modules/1.x派
这一波人认为,在现有基础上进行改进即可满足浏览器端的需要,既然浏览器端需要function包装,需要异步加载,那么新增一个方案,能把现有模块转化为适合浏览器端的就行了,有点像“保皇派”。基于这个主张,制定了Modules/Transport(http://wiki.commonjs.org/wiki/Modules/Transport)规范,提出了先通过工具把现有模块转化为复合浏览器上使用的模块,然后再使用的方案。
    browserify就是这样一个工具,可以把nodejs的模块编译成浏览器可用的模块。(Modules/Transport规范晦涩难懂,我也不确定browserify跟它是何关联,有知道的朋友可以讲一下)
    目前的最新版是Modules/1.1.1(http://wiki.commonjs.org/wiki/Modules/1.1.1),增加了一些require的属性,以及模块内增加module变量来描述模块信息,变动不大。
     
2. Modules/Async派
这一波人有点像“革新派”,他们认为浏览器与服务器环境差别太大,不能沿用旧的模块标准。既然浏览器必须异步加载代码,那么模块在定义的时候就必须指明所依赖的模块,然后把本模块的代码写在回调函数里。模块的加载也是通过下载-回调这样的过程来进行,这个思想就是AMD的基础,由于“革新派”与“保皇派”的思想无法达成一致,最终从CommonJs中分裂了出去,独立制定了浏览器端的js模块化规范AMD(Asynchronous Module Definition)(https://github.com/amdjs/amdjs-api/wiki/AMD)
     
3. Modules/2.0派
这一波人有点像“中间派”,既不想丢掉旧的规范,也不想像AMD那样推到重来。他们认为,Modules/1.0固然不适合浏览器,但它里面的一些理念还是很好的,(如通过require来声明依赖),新的规范应该兼容这些,AMD规范也有它好的地方(例如模块的预先加载以及通过return可以暴漏任意类型的数据,而不是像commonjs那样exports只能为object),也应采纳。最终他们制定了一个Modules/Wrappings(http://wiki.commonjs.org/wiki/Modules/Wrappings)规范,此规范指出了一个模块应该如何“包装”,包含以下内容:
登入後複製

實際上這三個流派誰都沒有勝過誰,反而是最後的AMD,CMD 規範紮根在這在三個流派之上,吸取它們所提出的優點不斷壯大。
總的來說AMD,CMD都是從commonJS規範中結合瀏覽器現實情況,並且吸收三大流派的優點而誕生。其中CMD是國內大牛所製定的規範,其實現的工具是seaJS,而AMD則是國外大牛所製定的,其實現技術則是requireJS

模組化的優點

#既然我們已經詳細的了解了「前端模組化」的歷史與發展,那麼我們也要大致了解模組開發的好處,畢竟這是我們學習的動力。

1. 作用域污染
    小明定义了 var name = 'xiaoming';
    N ~ 天之后:
    小王又定义了一个 var name = 'xiaowang';

2.  防止代码暴漏可被修改:
    为了解决全局变量的污染,早期的前端的先驱们则是以对象封装的方式来写JS代码:
    var utils = {
        'version':'1.3'
    };
    然而这种方式不可以避免的是对象中的属性可被直接修改:utils.version = 2.0 。

3. 维护成本的提升。
   如果代码毫无模块化可言,那么小明今天写的代码,若干天再让小明自己去看,恐怕也无从下手。


4. 复用与效率
   模块与非模块的目的就是为了复用,提高效率
登入後複製

總的來說,前端的模組化就是在眼瞎與手殘的過程進行發展的,大致我們可以總結一下幾時代:

  1. 無序(洪荒時代) :自由的書寫代碼。

  2. 函數時代 :將程式碼關入了籠子之中。

  3. 物件導向的方式。

  4. 匿名自執行函數:其典型的代表作就是JQ。

  5. 偽模組開發(CMD/AMD)

  6. #模組化開發(尚未誕生的ES6標準)

我们相信未来必将更加光明,但是回顾现在,特别是在国内的市场环境中IE浏览器依然占据半壁江山,所以基于ES6的模块特性依然任重道远,因此,在光明还未播撒的时刻,就让我们率先点燃一朵火苗照亮自己,而这朵火苗就是 ———— requireJS

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:&#39;js/&#39;,
    paths:{
        &#39;jquery&#39;:&#39;http://xxxx.xxx.com/js/jquery.min&#39;,
        &#39;index&#39;:&#39;index&#39;
    }
});

require([&#39;index&#39;]);
</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:&#39;js/&#39;,
    paths:{
        &#39;jquery&#39;:&#39;http://xxx.xxxx.com/js/jquery.min&#39;,
        &#39;index&#39;:&#39;index&#39;
    }
});
require([&#39;index&#39;]);
登入後複製

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

<script>
    var require = {
        baseUrl: &#39;js/&#39;,
        paths: {
            &#39;jquery&#39;: &#39;http://xxx.xxxx.com/js/jquery.min&#39;,
            &#39;index&#39;: &#39;index&#39;
        },
        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。

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

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

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

//define.js
define([&#39;app1&#39;,&#39;app2&#39;,&#39;app3&#39;,&#39;app4&#39;],function(app1,app2,app3,app4){
    if(page == &#39;app1&#39;){
        require.config(app1);
    }else if(page == &#39;app2&#39;){
        require.config(app2);
    }else if(page == &#39;app3&#39;){
        require.config(app3);
    }else{
        require.config(app4);
    }
})
登入後複製

当然关于模块的定义和载入我们后面会详细的讲解到,这里只需要有一个概念即可。

定义模块

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

define([id,deps,] callback);

ID:模块的ID,默认的便是文件名,一般无需使用者自己手动指定。
deps:当前模块所以依赖的模块数组,数组的每个数组元素便是模块名或者叫模块ID。
callback:模块的回调方法,用于保存模块具体的功能与代码,而这个回调函数又接收一个或者多个参数,这些参数会与模块数组的每个数组元素一一对应,即每个参数保存的是对应模块返回值。

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

简单的值对

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

define({
    &#39;color&#39;:&#39;red&#39;,
    &#39;size&#39;:&#39;13px&#39;,
    &#39;width&#39;:&#39;100px&#39;
});
登入後複製

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

示例:

//app1.js
define({
    baseUrl:&#39;music/js/&#39;,
    paths:{
        msuic:&#39;music&#39;,
        play:&#39;play&#39;
    }
});
登入後複製
//app2.js
define({
    baseUrl:&#39;video/js/&#39;,
    paths:{
        video:&#39;video&#39;,
        play:&#39;play&#39;
    }
});
登入後複製

非依赖的函数式定义

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

define(function(require,exports,modules){
    // do something
    return {
    &#39;color&#39;:&#39;red&#39;,
    &#39;size&#39;:&#39;13px&#39;
    }
});
登入後複製

依赖的函数式定义

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

//index.js
define([&#39;jquery&#39;,&#39;./utils&#39;], 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:{
            &#39;index&#39;:&#39;index&#39;
        }
    });
    
    require([&#39;index&#39;]);
登入後複製

requireJS 通过 require([]) 方法去载入模块,并执行模块中的回调函数,其值是一个数组,数组中的元素便是要载入的模块名称也就是模块ID,这里我们通过 require([&#39;index&#39;]) 方法载入了 index 这个模块,又因为该模块依赖了 jquery 模块,所以接着便会继续载入jquery模块,当jquery模块加载完成后,便会将自身的方法传递给形参 $ 最后执行模块的回调方法,alert出$参数具体内容。

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

这种按需载入模块,也叫就近依赖模式,它的使用要遵循一定的使用场景:
当模块是非依赖的函数式时,可以直接使用

define(function(require,exports,modules){
    var utils = require(&#39;utils&#39;);
    utils.sayHellow(&#39;hellow World&#39;)
})
登入後複製

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

define([&#39;jquery&#39;], function($) {
    $(function() {
        require([&#39;utils&#39;],function(utils){
            utils.sayHellow(&#39;Hellow World!&#39;);
        });
    });
});
登入後複製

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

define([&#39;jquery&#39;,&#39;require&#39;,&#39;exports&#39;,&#39;modules&#39;], function($,require,exports,modules) {
    $(function() {
        //方式一
        require([&#39;utils&#39;],function(utils){
            utils.sayHellow(&#39;Hellow World!&#39;);
        });
        //方式二:
        var utils = require(&#39;utils&#39;);
        utils.sayHellow(&#39;hellow World&#39;)
    });
});
登入後複製

模块的返回值

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(&#39;utils&#39;);
    sayHellow(&#39;hellow World&#39;);
})
登入後複製

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

// utils.js

define(function(require,exports,modules){
    function sayHellow(params){
        alert(params);
    }
    
    function sayBye(){
        alert(&#39;bye-bye!&#39;);
    }
    
    return {
        &#39;sayHellow&#39;:sayHellow,
        &#39;sayBye&#39;:sayBye
    }
});

// index.js
define(function(require,exports,modules){
    var utils = require(&#39;utils&#39;);
    utils.sayHellow(&#39;hellow World&#39;);
})
登入後複製

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(&#39;utils&#39;);
    utils.sayHellow(&#39;hellow World&#39;);
});
登入後複製

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

require:加载模块时使用。
exports:导出模块的返回值。
modules:定义模块的相关信息以及参数。

非标准模块定义

require.config() 方法的配置对象中有一个 shim 属性,它的值是一个对象,可以用于声明非标准模块的依赖和返回值。
所谓的 “非标准模块” 指的是那些不符合的AMD规范的JS插件。
下面我们先看看基本的 shim 配置参数:

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

require([&#39;index&#39;]);
登入後複製

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

在 index 模块执行时:

define([&#39;jquery&#39;,&#39;tool&#39;,&#39;say&#39;],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:&#39;js/&#39;,
    paths:{
        &#39;jquery&#39;:&#39;http://xx.xxxx.com/js/jquery.min&#39;,
        &#39;index&#39;:&#39;index&#39;
    },
    config:{
        &#39;index&#39;:{
            &#39;size&#39;:13,
            &#39;color&#39;:&#39;red&#39;
        }
    }
});

//index.js
define([&#39;jquery&#39;,&#39;module&#39;],function($,module){
    console.log(module.config().size)
});
登入後複製

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

map

[略],暂时还未弄明白其具体使用方式,后续会继续保持关注,如果你知晓其作用,麻烦你一定要与我联系。

packages

[略],暂时还未弄明白其具体使用方式,后续会继续保持关注,如果你知晓其作用,麻烦你一定要与我联系。

rquire 压缩

RequireJS 会将完整项目的JavaScript代码轻易的分割成苦干个模块(module),这样,你将获得一些具有互相依赖关系的JavaScript文件。在开发环境中,这种方式可以让我们的代码更具有模块化与易维护性。但是,在生产环境中将所有的JavaScript文件分离,这是一个不好的做法。这会导致很多次请求(requests),即使这些文件都很小,也会浪费很多时间。因此我们可以通过合并这些脚本文件压缩文件的大小,以减少请求的次数与资源的体积来达到节省加载时间的目的,所以这里我们就要提到一个关于requireJS 延伸,那就是 r.js。
r.js 是一个独立的项目,它作用在nodeJS环境中,可以实现对JS代码的合并压缩。

使用r.js 要具有以下几个条件:

  1. r.js 源文件

  2. bulid.js (即属于r.js的配置文件)

  3. nodeJS 环境

r.js 可以直接丢在项目的根目录上,build.js 是 r.js 的配置文件,由开发者自己新建,与r.js同目录。其一般的目录结构如下:


[project]
  /js
  /css
  /images
index.html
r.js
build.js
登入後複製


r.js 下载
nodeJS环境,以及r.js都好办,重要的就是掌握配置文件的使用 -- build.js,下面我们就详细的说说它。

({
    //(选填)app的顶级目录。如果指定该参数,说明您的所有文件都在这个目录下面(包括baseUrl和dir都以这个为根目录)。如果不指定,则以baseUrl参数为准
    appDir: &#39;./&#39;, 
    
     // 输出目录。如果不指定,默认会创建一个build目录
    dir: &#39;pack&#39;,
    
     // 模块所在默认相对目录,如果appDir有指定,则baseUrl相对于appDir。
    baseUrl: &#39;js/&#39;,
    paths: {
        &#39;index&#39;: &#39;index&#39;,
        &#39;a&#39;: &#39;a&#39;,
        &#39;b&#39;: &#39;b&#39;,
        &#39;c&#39;: &#39;c&#39;

    },
    
    //过滤规则,匹配到的文件将不会被输出到输出目录去
    fileExclusionRegExp:   /^(r|build)\.js|.*\.scss$/, 
    
     /*
        JS 文件优化方式,目前支持以下几种:
        uglify: (默认) 使用 UglifyJS 来压缩代码
        closure: 使用 Google&#39;s Closure Compiler 的简单优化模式
        closure.keepLines: 使用 closure,但保持换行
        none: 不压缩代码
    */
    optimize: &#39;none&#39;,
   
   /*
    允许优化CSS,参数值:
    “standard”: @import引入并删除注释,删除空格和换行。删除换行在IE可能会出问题,取决于CSS的类型
    “standard.keepLines”: 和”standard”一样但是会保持换行
    “none”: 跳过CSS优化
    “standard.keepComments”: 保持注释,但是去掉换行(r.js 1.0.8+)
    “standard.keepComments.keepLines”: 保持注释和换行(r.js 1.0.8+)
    “standard.keepWhitespace”: 和”standard”一样但保持空格
    */
    optimizeCss:   &#39;“standard”&#39;, 
    

    // 是否忽略 CSS 资源文件中的 @import 指令
    cssImportIgnore: null,
    
    //参与压缩的主模块,默认情况下会将paths模块中定义的模块都压缩合并到改模块中,通过exclude 可以排除参与压缩的模块,其中模块的地址都是相对于baseUrl的地址。
    modules: [{ 
        name: &#39;index&#39;,
        exclude: [&#39;c&#39;]
    }],
    
    // 包裹模块
    wrap: true,
    
    // 自定义包裹模块,顾名思义就是使用特定内容去包裹modules指定的合并模块内容,如此一来 define/require 就不再是全局变量,在 end 中可以暴露一些全局变量供整个函数使用
    wrap: {
         start: "(function() {",
         end: "}(window));"
     },

    removeCombined: false,
    
    //如果shim配置在requirejs运行过程中被使用的话,需要在这里重复声明,这样才能将依赖模块正确引入。
    shim: {} 
    
     // 载入requireJS 的配置文件,从而使用其中的paths 以及 shim 属性值。通过指定该属性,可以省去我们在bulid.js中重复定义 paths 与 shim属性。
    mainConfigFile:"js/app.js",
})
登入後複製

以上环节都准备好了之后,就可以在终端中允许打包压缩命令: node r.js -o build.js
当执行该命令后,r.js 会将自身所在目录的所有资源连同目录重新拷贝一份到输出目录(dir)中。然后再输出目录进行最后的合并与压缩操作。

其它问题

timeout超时问题

该问题一般是 waitSeconds 属性值导致,解决的方法有两个,一个是将 waitSeconds的值设置更长时间,比如17s,另一个就是将其值设置为0,让其永不超时。

循环依赖问题

何为循环依赖?
如果存在两个模块,moduleA 与 moduleB, 如果 moduleA 依赖 moduleB ,moduleB也依赖了moduleA,并且这中情况下,便是循环依赖。
循环依赖导致的问题!
如果两个模块循环依赖,并且A中有调用B中的方法,而B中也有调用A中的方法,那么此时,A调用B正常,但是B中调用A方法,则会返回 undefined 异常。
如何解决循环依赖的问题?
通过 require([],fn) 解决
此时在模块B中,我们通过引入 require 依赖,然后再通过 require() 方法去载入模块A,并在回调中去执行。

define([&#39;require&#39;,&#39;jquery&#39;],function(require,$){

    function bFunction(){
        alert(&#39;this is b module&#39;);
    }

    require([&#39;moduleA&#39;],function(m){
        m() // 执行传递过来方法
    });

    return bFunction;
});
登入後複製

这里要引起我们注意的地方就是依赖的'module'模块,它是一个预先定义好的值,引入该值,在当前模块下便可以调用 require 方法。

通过 exports 解决

define([&#39;exports&#39;,&#39;jquery&#39;],function(exports,$){

    function bFunction(){
        exports.aFunction();
        alert(&#39;this is b module&#39;);
    }

    exports.bFunction = bFunction;
});
登入後複製

相同的这里依赖的 module 模块也是一个预先定义好的值,,引入该值,在当前模块下便可以调用 exports 对象设定当前模块的返回值。
而通过 exports 所解决的循环依赖问题,有一个需要注意的地方,那就是方法的执行必须要放入到当前定义方法的回调中,因为我们不能确定 moduleA 与 moduleB的加载顺序。

CDN回退

如果我们不确定一个模块的加载正确,我们可以在 require.config()方法中将模块的地址替换为一个数组,数组的元素,便是同一模块的多个地址。

requirejs.config({
    paths: {
        jquery: [
            &#39;//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.min.js&#39;,
            &#39;lib/jquery&#39;
        ]
    }
});
登入後複製

定义AMD插件

有时候我们自己编写的一款插件,我们需要它能够在任何环境中都能起作用,比如在引入requireJS的AMD环境下可以作为符合AMD规范的插件,进行模块式加载调用,而在普通的浏览器环境中也可以正常的执行。
想实现这一功能,其实很简单,只需要按照下例格式去编写插件即可。

// say.js 基于JQ扩展的插件。

(function(win, factory) {
    if (&#39;function&#39; === typeof define && define.amd) {
        define([&#39;jquery&#39;], function($) {
            return new factory(win, $)
        });
    } else {
        factory(win, $);
    }
}(window, function(win, $) {

    var say = function(value) {
        alert(value);
    }

    if (&#39;function&#39; === typeof define && define.amd) {
        return say;
    } else if ($ && &#39;function&#39; === typeof $) {
        $.say = function(v) {
            return new say(v);
        }
    } else {
        win.say = function(v) {
            return new say(v);
        }
    }

}));

// index.js
define([&#39;say&#39;],function(say){
    say(&#39;hellow requireJS&#39;);
})
登入後複製

关于require的预定义模块

关于这个问题,我们上面也有说到,这里就进行一次总结。
我们可以这样理解,对于 requireJS 来说,除了我们自己使用require.config()定义的模块,它内部也有自己预先定义好的模块,比如:require,exports,modules ,在使用时,我们无需在 require.config() 去中定义,而是可以直接在依赖中引入使用,比如:

//index.js
define([&#39;jquery&#39;,&#39;config&#39;,&#39;require&#39;,&#39;exports&#39;,&#39;module&#39;],function($,config,require,exports,module){
  $(function(){
      require.config(config); // 载入配置文件
      exports.data = &#39;index Module Return Value&#39; //定义模块的返回值。
      modules.config().color; // 接受在配置文件中为该模块配置的参数数据。
  })
});
登入後複製

关于R.js压缩非本地文件的问题

r.js 中是无法合并压缩远程文件的,它只能操作本地文件,因此这就带来一个问题,当我们进行模块的压缩合并时,若某个模块存在着对远程模块(文件)的依赖时,使用 r.js 进行操作便会报错,虽然可以将这个远程文件拷贝到本地来解决这一问题,但是如果像一些公用的资源例如JQ插件等,如果让每个项目都在本地放入一个 common 资源包,这就脱离了我们的实际意义。

({
    paths:{
        jquery:&#39;http://xxx.com/js/jquery.min&#39;
    }
})
登入後複製

此时进行打包的时候在就会报错。但是如果我们不在 paths 中去声明 jquery模块,当打包的时候,r.js 发现其它模块有依赖 jquery的,但是你又没有在 build.js 中声明,依然会报错阻碍运行。
那么有没有一个好的办法呢?比如虽然声明了 jquery 模块,但是值却不是远程的文件,本地也不存在该文件,更不会报错。答案是有的,那就是对(不需要参与压缩合并的)远程的资源模块设置值为 empty:。 "javascript:; ({ paths:{ jquery:&#39;empty:&#39; } }) " 或者是在执行命令时,指定参数值为空:node r.js -o build.js paths.jquery=empty:`

关于R.js - shim功能的说明

R.js 用于合并多个模块(多个文件),以及压缩文件中的JS代码,也就是说在这个合并后的文件中会包含多个 define定义的模块,而这个合并后的文件也就是这个页面的入口文件,并且rquire的config配置也会在其中。

模块的合并,对于R.js来言,它会以 build.jspaths属性定义的模块为参考,然后到要合并的模块只能中去匹配依赖,匹配到了就合并到当前文件中。如果参与合并的所有模块有某些依赖顺序上的调整,则可以通过 shim 属性去调整合并时的前后顺序。

//build.js
({
    &#39;paths&#39;:{
        &#39;a&#39;:&#39;a&#39;,
        &#39;b&#39;:&#39;b&#39;
    },
    &#39;shim&#39;:{
        &#39;a&#39;:{
            &#39;deps&#39;:[&#39;b&#39;]
        }
    },
    wrapShim:true
})
登入後複製

此时合并到主文件后,b 模块的内容就会在 a 模块之前。

define(&#39;b&#39;,[],function(){}),
define(&#39;a&#39;,[],function(){})
登入後複製

最后强调一点,对于通过 exclude 属性排除合并的模块,使用 shim 并不会产生作用,因为它只对合并在一个文件中的模块有效。

关于require加载CSS的问题

requireJS不仅仅只加载JS文件,实际上它还可以加载CSS样式文件,但是这需要借助一个requireJS插件才能实现。
下载地址:require-css.js

使用上,有两种方式,一种在配置参数中进行声明:

var require = {
    baseUrl:&#39;js/&#39;,
    paths:{
        &#39;index&#39;:&#39;index&#39;,
        &#39;a&#39;:&#39;a&#39;
    },
    shim:{
        &#39;a&#39;:{
            deps:[&#39;css!../css/a.css&#39;]
        }
    },
    deps:[&#39;index&#39;]
};
登入後複製
//index.js
define([&#39;a&#39;]); // 载入模块不执行任何操作。
登入後複製

另一种是直接在模块中进行依赖声明

define([&#39;css!../css/a.css&#39;]);
登入後複製

最后说下对 css!../css/index.css 的理解吧,首先 ! 是插件与插件参数的分割符号,因此"css"就是插件的模块名,requireJS会先去检查 css 这个模块是否有在配置文件中声明,如果没有则会默认在 baseUrl 指向的路径下去载入,而分隔符右边的 '../css/a.css' 就是插件要使用的参数,这里便是要载入的css文件地址。

以上是一份超詳細的requireJS介紹及應用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板