javascript模塊化【RequireJS 】

1、模塊化的由來

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


2、模塊規範

想要了解模塊化就要先知道JS中3個模塊規範。
JS中的模塊規範(CommonJS,AMD,CMD),若是你聽過模塊化這個東西,那麼你就應該聽過或CommonJS、AMD、CMD這些規範,我也聽過,但以前也真的是聽聽而已。直到最近項目中使用到了纔有了必定的理解, 如今就看看吧,這些規範究竟是啥東西,怎麼用的。(本文對CommonJS及CMD作一個大概的說明,對AMD中RequireJS作較爲全面的講解)前端

1.CommonJS

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

2.CMD

CMD:common module define,CMD實際上是阿里一位大神編寫的seajs中提出的模塊化解決方案。
==其實CMD能夠當作是CommonJS的前端實現==。
在前幾年很是火,不過隨着前端框架的崛起,vue、react、angular都集成了各自的模塊化。而且es六、webpack等都提供了模塊化的解決方案。使得seajs出場機會愈來愈少,做者也中止了更新。seajs也漸漸退出了歷史的舞臺。react

3.AMD

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


3、RequireJS

1.基本用法

例如在一個電商網站中,購物車和商品的邏輯會在須要場景應用,因此咱們就能夠將二者抽出做爲模塊開發,使用的時候直接引用,直接上代碼。
首先咱們先建立一個cart.js文件webpack

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

而後建立一個product.js文件es6

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

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

<!-- 首先在官網下載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.jsbootstrap

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;
        } );
    }

4、總結

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

相關文章
相關標籤/搜索