Stories

Detail Return Return

javascript模塊化【RequireJS 】 - Stories Detail

一、模塊化的由來

在沒有模塊化思想之前,我們總是將大量的邏輯代碼寫在一起,這樣的代碼雜亂無章,沒有條理性,不便於維護,不利用複用。並且很多代碼重複,邏輯重複。甚至造成全局變量污染,也不方便保護私有數據。
為了解決上面的問題,模塊化的編程思想應運而生。
模塊化的基本思想就是:==閉包自調用函數==
對閉包瞭解不夠的同學,請先查看《 JS閉包全面解析》一文。


二、模塊規範

想要了解模塊化就要先知道JS中3個模塊規範。
JS中的模塊規範(CommonJS,AMD,CMD),如果你聽過模塊化這個東西,那麼你就應該聽過或CommonJS、AMD、CMD這些規範,我也聽過,但之前也真的是聽聽而已。直到最近項目中使用到了才有了一定的理解, 現在就看看吧,這些規範到底是啥東西,怎麼用的。(本文對CommonJS及CMD做一個大概的説明,對AMD中RequireJS做較為全面的講解)

1.CommonJS

CommonJS API定義很多普通應用程序(主要指非瀏覽器的應用)使用的API,也是Node中使用的模塊化解決方案。

2.CMD

CMD:common module define,CMD其實是阿里一位大神編寫的seajs中提出的模塊化解決方案。
==其實CMD可以看成是CommonJS的前端實現==。
在前幾年非常火,不過隨着前端框架的崛起,vue、react、angular都集成了各自的模塊化。並且es6、webpack等都提供了模塊化的解決方案。使得seajs出場機會越來越少,作者也停止了更新。seajs也漸漸退出了歷史的舞台。

3.AMD

AMD:async module define:異步模塊定義。
AMD其實就是requireJS實現的模塊化解決方案,下面我會着重的介紹AMD規範中的requireJS。
鏈接>>>RequireJS中文網


三、RequireJS

1.基本用法

例如在一個電商網站中,購物車和商品的邏輯會在需要場景應用,所以我們就可以將兩者抽出作為模塊開發,使用的時候直接引用,直接上代碼。
首先我們先創建一個cart.js文件

define([],function(){
    console.log('cart模塊');
})

然後創建一個product.js文件

define([],function(){
    console.log('product模塊');
})

然後在首頁index.html中調用模塊

<!-- 首先在官網下載requirejs源文件,通過script標籤導入 -->
<script src="../js/require.js"></script>
<script>
// 將之前定義好的cart和product模塊導入首頁模塊中
require(["cart","product"],function(){
    console.log('這裏是首頁模塊');
})
</script>
2.動態加載模塊&模塊返回值

還是上面的栗子,再加一些代碼
cart.js

define([],function(){
    // 將函數作為模塊的返回值
    return function(){
        console.log('購物車模塊初始化');
    }
})

product.js

define([],function(){
    // 模塊不僅可以返回函數,也可返回對象
    return {
        init() {
            console.log('商品模塊初始化');
        }
    }
})

index.html

// 這裏我們給require的回調函數添加形參,接收前面對應模塊的返回值,要與數組順序一致
require(['cart','product'],function(cart,product) {
    // 這裏我們不想一進入就加載cart和product模塊,而是等點擊按鈕再去加載模塊
    // 這裏也可以理解成按需加載模塊
    var btn = document.getElementById('btn1');
    btn.onclick(function(){
        // 在按鈕的點擊事件中去加載模塊
        cart();
        product.init();
    })
})

==注意==:

  • 回調函數中的形參一定要與,數組中導入模塊的順序一致
  • 沒有返回值的模塊儘量放到最後導入(數組最後),當然es6中可以寫成param1,,,param2
  • 大部分模塊都是按需加載的
3.入口文件

一般將模塊的入口也定義在一個單獨的js文件中,如main.js。
這樣引用入口文件處就可以簡寫為:

<script data-main="./main" src="../js/require.js"></script>
4.入口文件配置---path

通過在入口文件中的一些配置可以讓我們在使用模塊時更加便捷。
如我們想在模塊中使用jQuery,我們要這樣寫

define([jquery-3.3.1],function($){
})

這裏可能有人不理解為什麼每個模塊引用jq,都要引用jq模塊。因為:

  • 防止全局變量污染(zepto:$)
  • 使用amd方式在每個模塊導入一下,$就是一個局部變量

設想一下,如果有幾十個模塊,每個模塊都這樣引入jq,如果jq的文件目錄發生改變亦或是jq版本改變,那將會是一個非常大的工程。
這時我們就可以利用path來解決這個問題
main.js

require.config({
    path:{
        jquery:"lib/jquery-3.3.1", // 文件
        bootstrap:"assets/bootstrap/js/bootstrap.min", // 文件
        service:"../service" // 文件夾
    }
})

當然,不是所有的模塊都需要配置在這裏的,一般來説常用的模塊、文件夾才需要配置。
這樣當需要用到jq的時候,只需要導入入口文件中配置好的jquery即可,後續的任何修改,每個引用的模塊都會同步。

// 指定文件的可以直接導入文件,指定文件夾的可以通過配置的文件夾目錄找到對應文件
define(["jquery","service/xxxxService","bootstrap"],function($,xxxxService){
})

為什麼jq可以像我們編寫的其他模塊一樣被導入使用,是因為jq中已經註冊了amd模塊。雖説jq的設計並不是像我們寫的amd模塊那樣,但是在內部已經做了兼容,許多第三方庫都是這麼兼容amd的,通過源碼可以看到:

define([],function() {
    // 定義一個模塊,將jq對象返回,這樣我們在導入模塊後拿到的參數$就是這個jq對象
    return jQuery;
})
5.入口文件配置---baseUrl

一個模塊化的項目目錄都會比較複雜,如創建兩個js文件,cart.js、cartDetail.js。存放的目錄為~/js/cart/中,那麼想要在cart模塊中倒入cartDetail模塊就要這樣去寫:
cart.js

define(["js/cart/cartDetail"],function(cartDetail){
})

雖然兩個模塊同處一個文件夾中,但是模塊的導入是根據入口文件所在的路徑去查找的,如果入口文件放在根路徑下,那麼導入模塊的路徑也是根路徑。
利用baseUrl簡化路徑查找:
main.js

require.config({
    baseUrl:"js/"
})

改造後我們再導入模塊可以這樣去寫:
cart.js

define(["cart/cartDetail"],function(cartDetail){
})

==注意==:path裏面的配置也是相對於baseUrl的

6.requirejs中的循環依賴

場景:a模塊依賴b模塊,但是b模塊也需要a模塊,如果按常理去寫會造成循環依賴,導致報錯。

  • 這時我們要在b模塊中添加require模塊的依賴,然後再添加a的依賴
  • ==但是一定不要去通過回調函數形參的形式獲取返回值。==
  • 在需要執行a模塊代碼的時候通過require調用。
define(["require","a"],function(require){
    require("a")();
})

另外還需要==注意==的一點是:一個模塊被不同模塊引用若干次,但是他們獲取到的都是該模塊同一個引用(閉包數據共享),模塊代碼不會重新執行,節省性能。

7.檢測第三方庫是否支持AMD規範

這個方式也是jQuery中使用的。

  if ( typeof define === "function" && define.amd ) {
        define([], function() {
            return jQuery;
        } );
    }

四、總結

學習模塊化,重要的不是學習具體的實現,而是學習一種思想。只有真正的領悟了模塊化的思想才能把模塊化更好的應用到開發中,並且在使用其他框架時才能更加得心應手。

user avatar toopoo Avatar dingtongya Avatar Leesz Avatar yinzhixiaxue Avatar aqiongbei Avatar zourongle Avatar chongdianqishi Avatar leexiaohui1997 Avatar linx Avatar banana_god Avatar hard_heart_603dd717240e2 Avatar shuirong1997 Avatar
Favorites 180 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.