在沒有模塊化思想以前,咱們老是將大量的邏輯代碼寫在一塊兒,這樣的代碼雜亂無章,沒有條理性,不便於維護,不利用複用。而且不少代碼重複,邏輯重複。甚至形成全局變量污染,也不方便保護私有數據。
爲了解決上面的問題,模塊化的編程思想應運而生。
模塊化的基本思想就是:==閉包自調用函數==
對閉包瞭解不夠的同窗,請先查看《 JS閉包全面解析》一文。html
想要了解模塊化就要先知道JS中3個模塊規範。
JS中的模塊規範(CommonJS,AMD,CMD),若是你聽過模塊化這個東西,那麼你就應該聽過或CommonJS、AMD、CMD這些規範,我也聽過,但以前也真的是聽聽而已。直到最近項目中使用到了纔有了必定的理解, 如今就看看吧,這些規範究竟是啥東西,怎麼用的。(本文對CommonJS及CMD作一個大概的說明,對AMD中RequireJS作較爲全面的講解)前端
CommonJS API定義不少普通應用程序(主要指非瀏覽器的應用)使用的API,也是Node中使用的模塊化解決方案。vue
CMD:common module define,CMD實際上是阿里一位大神編寫的seajs中提出的模塊化解決方案。
==其實CMD能夠當作是CommonJS的前端實現==。
在前幾年很是火,不過隨着前端框架的崛起,vue、react、angular都集成了各自的模塊化。而且es六、webpack等都提供了模塊化的解決方案。使得seajs出場機會愈來愈少,做者也中止了更新。seajs也漸漸退出了歷史的舞臺。react
AMD:async module define:異步模塊定義。
AMD其實就是requireJS實現的模塊化解決方案,下面我會着重的介紹AMD規範中的requireJS。
連接>>>RequireJS中文網jquery
例如在一個電商網站中,購物車和商品的邏輯會在須要場景應用,因此咱們就能夠將二者抽出做爲模塊開發,使用的時候直接引用,直接上代碼。
首先咱們先建立一個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>
仍是上面的栗子,再加一些代碼
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(); }) })
==注意==:
param1,,,param2
通常將模塊的入口也定義在一個單獨的js文件中,如main.js。
這樣引用入口文件處就能夠簡寫爲:
<script data-main="./main" src="../js/require.js"></script>
經過在入口文件中的一些配置可讓咱們在使用模塊時更加便捷。
如咱們想在模塊中使用jQuery,咱們要這樣寫
define([jquery-3.3.1],function($){ })
這裏可能有人不理解爲何每一個模塊引用jq,都要引用jq模塊。由於:
設想一下,若是有幾十個模塊,每一個模塊都這樣引入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; })
一個模塊化的項目目錄都會比較複雜,如建立兩個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的
場景:a模塊依賴b模塊,可是b模塊也須要a模塊,若是按常理去寫會形成循環依賴,致使報錯。
define(["require","a"],function(require){ require("a")(); })
另外還須要==注意==的一點是:一個模塊被不一樣模塊引用若干次,可是他們獲取到的都是該模塊同一個引用(閉包數據共享),模塊代碼不會從新執行,節省性能。
這個方式也是jQuery中使用的。
if ( typeof define === "function" && define.amd ) { define([], function() { return jQuery; } ); }
學習模塊化,重要的不是學習具體的實現,而是學習一種思想。只有真正的領悟了模塊化的思想才能把模塊化更好的應用到開發中,而且在使用其餘框架時才能更加駕輕就熟。