什麽是模塊化?
阿新 • • 發佈:2017-08-04
spa func 頁面 位置 技術 必須 們的 而且 先後 什麽是模塊化?
以前我們可能會這樣做代碼分工
modules.js -----
var moduleA = {......}
var moduleB = {......}
var moduleC = {......}
然後每個頁面都引入JS
<script src="modules.js"></script>
不管這個頁面用到了幾個模塊,你都要把整個文件加載進來,於是我們就想,不如分開吧
然後我們的代碼變成了這樣:
moduleA.js -----
var moduleA = {......}
moduleB.js -----
var moduleB = {......}
moduleC.js -----
var moduleC = {......}
但是,由於各個模塊之間存在相互引用的依賴關系,所以我們這樣引入JS:
<script src="jquery.js"></script>
<script src="moduleB.js"></script>
<script src="moduleC.js"></script>
<script src="moduleA.js"></script>
假設A引用了C,而C引用了B,ABC裏面都用到了JQ,你必須小心翼翼的確保它們的加載順序是正確的
於是require.js出現了
按照requireJS當中的規範要求,你只需加載一個文件就可以,但同時需要帶上一個配置文件config.js
第一步:
<script data-main ="config.js" src ="require.js" ></script>
這個require.js你可以從官網下載,這個config.js你必須自己完成。不用擔心,它寫起來非常簡單:
config.js -----
requirejs.config({
baseUrl : "js",
paths : {
"jquery": "jquery/jquery.1.11.3"
}
});
baseUrl的意思就是說你所有的路徑都是基於它基礎上的。
看的出來,我們正在配置一個文件的路徑
"jquery": "jquery/jquery.1.11.3"
左邊藍色的是我們定義的模塊名稱,右邊紅色則是文件路徑,註意文件沒有後綴名,因為在require看來所有的模塊都是JS
緊接著,我們可以定義自己的模塊文件了,moduleB.js
首先mouduleB.js也是一個模塊,但是我們必須對這個JS改造一下,讓它符合require的規範要求,才能使用
moduleB.js -----
define(["jquery"],function(jq){
//根據我們之前的設定,模塊B需要使用Jquery
//define函數中的第一個參數,用來表明,我們即將引用哪個模塊。
//由於之前我們已經定義過了jquery模塊,所以這裏可以直接使用
//註意,你如果不寫聲明,這裏是用不了jQuery的
//你如果註意到第一個參數是一個數組,那就應該猜到,其實我們可以一次引用好多模塊
//jq變量就是jquery,你如果不習慣,把名字改回$就是了
return {
start: function(){
jq("#box").show(1000);
console.log("模塊B提供的start方法");
//為什麽這裏要return呢? 因為我們定義的模塊要被其他人使用
//那又為什麽要return對象呢?為了更符合模塊化思維以及面相對象的原則
//我們返回一個對象而不是函數,使用會更方便(待會講完怎麽調用你就知道了)
}
}
});
好了,模塊B定義完了,我們可以來試試。當然,用之前別忘了修改配置
config.js -----
requirejs.config({
baseUrl : "js",
paths : {
"jquery": "jquery/jquery.1.11.3",
"moduleb" : "ms/moduleB"
}
});
註意:配置路徑時,排名不分先後,順序可以任意。
我們有一個index.html頁面,包含一個index.js文件。
index.js -----
require(["config",function(){ //config默認從項目根目錄找,由於我們的config.js就是放在根目錄下的
//只有先引用config,才能引用配置好的所有模塊
require(["jquery","moduleb"],function($,mb){
//我們引用兩個模塊jquery、moduleb
mb.start(); //mb就是我們剛才在模塊B中返回的對象
})
})
根據我們一開始的設定(A引用了C,而C引用了B,ABC裏面都用到了JQ )
和上面的講解,你能否自己完成moduleC.js和moduleA.js?
moduleC.js -----
define(["moduleb","jquery"],function(mb,$){
return {
.......
}
});
moduleA.js -----
define(["modulec","jquery"],function(mc,$){
return {
.......
}
});
別忘了配置:
config.js -----
..............
paths: {
"jquery": "jquery/jquery.1.11.3",
"modulea" : "ms/moduleA",
"moduleb" : "ms/moduleB",
"modulec" : "ms/moduleC"
}
..............
一些問題:
問:加載配置文件的路徑問題?
根據你目前的頁面的業務JS文件的位置來決定,也就是說,復雜加載config的JS在哪裏,路徑就從哪裏開始
例如index.js在根目錄,config也在根目錄:
require(["config"],function(){...});
例如index.js在js目錄中,config在根目錄
require(["../oncfig"], function(){...});
例如index.js在根目錄中,config在js目錄
require(["js/config", function(){...}]; 問:是不是所有要加載的模塊,都必須寫define函數,為什麽jquery就沒寫,一樣可以加載? 誰說JQ沒寫呢?這是jquery的部分源代碼。 ...... if ( typeof define === "function" && define.amd ) { define( "jquery", [], function() { return jQuery; }); } ...... 所以,所有的JS都必須遵循require的規範,才能夠正確的加載 問:我自己寫了一大堆的工具函數,請問怎麽加載? 首先,工具函數也可以用對象來表示 common.js ----- define(function(){ return { getStyle : function(){ ........ }, randomColor : function(){ ......... } } }); 當然如果你比較任性,非要寫成這個樣子: common.js ----- function getStyle(){ ......... } function randomColor(){ ........ } 解決辦法也是有的: 請百度requireJS, shim配置 問:有一些插件,本身沒有導出任何對象或核心函數,只是對某個框架的擴展,例如 my.jquery.scroll.js 該怎麽用? 官方文檔給出的答案是使用shim配置 config.js ----- require.config({ baseUrl: "js", paths: { "jquery" : "jquery.1.11.3", "jquery.scroll" : "my.jquery.scroll" }, shim: { "jquery.scroll": { deps: ["jquery"], exports: "jQuery.fn.scroll" } } }); 這是第一種方式, deps表示它依賴了哪個模塊,exports表示它輸出哪個函數,函數名字要和插件代碼裏保持一致 使用方式: index.js ----- require(["config",function(){ require(["jquery","jquery.scroll"],function($){ $("#box").scroll(500); }) }) ===================================================== 第二種配置方式: config.js ----- require.config({ baseUrl: "js", paths: { "jquery" : "jquery.1.11.3", "jquery.scroll" : "my.jquery.scroll" }, shim: { "jquery.scroll": ["jquery"] } }); 使用方法同上 But! 官方文檔又說,這樣使用可能導致錯誤! 請參考原文: 所以,終極解決方案是,把插件改成define調用......... 問:如果我用到了兩個框架,Jquery和Zepto,他們的核心函數都叫$ ,那該怎麽辦呢? require(["jquery","zepto"],function(jq,zepto){ // 改個名字就不沖突了 }) 說一說好處: 講了這麽多,requirejs所倡導的模塊化開發,好處在哪裏呢? 1 你有沒有發現,整個項目當中,再也沒有出現一個全局變量? 2 你有沒有發現,你再也沒有考慮過加載順序的問題? 3 你有沒有發現,即使兩個框架名字沖突了也沒關系? 4 而且你肯定沒有發現,所有的JS文件的加載過程,已經變成了異步。 5 最後,你還發現,用了requireJS,你的代碼想不寫成面相對象都難?? 關於AMD和CMD 總在網上看大家討論AMD和CMD,到底是什麽東西呢? AMD就是require所倡導的模塊化開發的方式 還有一個叫CommonJS的,不過還是和requireJS有些區別,主要針對NODE後端開發 另外還有一個大名鼎鼎的SeaJS,作者是淘寶的玉伯,於是誕生了CMD規範 根據作者自己的介紹,SeaJS各方面都比requireJS強大 這個就不做評價了 至於區別呢? AMD推崇的是依賴前置: define(["a","b","c"],function(a,b,c){ //我們把所有依賴提前寫好了 a.doSomething (); //這裏直接使用 b.doSometing(); }) CMD推崇的是依賴就近: define(function(require, exports, module) { var a = require(‘./a‘); a.doSomething(); ......... var b = require(‘./b‘) // 依賴可以就近書寫,用到的時候再加載 b.doSomething() }) 當然requireJS也支持CMD的寫法,不過作者本人是不推薦這麽寫的
require(["js/config", function(){...}]; 問:是不是所有要加載的模塊,都必須寫define函數,為什麽jquery就沒寫,一樣可以加載? 誰說JQ沒寫呢?這是jquery的部分源代碼。 ...... if ( typeof define === "function" && define.amd ) { define( "jquery", [], function() { return jQuery; }); } ...... 所以,所有的JS都必須遵循require的規範,才能夠正確的加載 問:我自己寫了一大堆的工具函數,請問怎麽加載? 首先,工具函數也可以用對象來表示 common.js ----- define(function(){ return { getStyle : function(){ ........ }, randomColor : function(){ ......... } } }); 當然如果你比較任性,非要寫成這個樣子: common.js ----- function getStyle(){ ......... } function randomColor(){ ........ } 解決辦法也是有的: 請百度requireJS, shim配置 問:有一些插件,本身沒有導出任何對象或核心函數,只是對某個框架的擴展,例如 my.jquery.scroll.js 該怎麽用? 官方文檔給出的答案是使用shim配置 config.js ----- require.config({ baseUrl: "js", paths: { "jquery" : "jquery.1.11.3", "jquery.scroll" : "my.jquery.scroll" }, shim: { "jquery.scroll": { deps: ["jquery"], exports: "jQuery.fn.scroll" } } }); 這是第一種方式, deps表示它依賴了哪個模塊,exports表示它輸出哪個函數,函數名字要和插件代碼裏保持一致 使用方式: index.js ----- require(["config",function(){ require(["jquery","jquery.scroll"],function($){ $("#box").scroll(500); }) }) ===================================================== 第二種配置方式: config.js ----- require.config({ baseUrl: "js", paths: { "jquery" : "jquery.1.11.3", "jquery.scroll" : "my.jquery.scroll" }, shim: { "jquery.scroll": ["jquery"] } }); 使用方法同上 But! 官方文檔又說,這樣使用可能導致錯誤! 請參考原文: 所以,終極解決方案是,把插件改成define調用......... 問:如果我用到了兩個框架,Jquery和Zepto,他們的核心函數都叫$ ,那該怎麽辦呢? require(["jquery","zepto"],function(jq,zepto){ // 改個名字就不沖突了 }) 說一說好處: 講了這麽多,requirejs所倡導的模塊化開發,好處在哪裏呢? 1 你有沒有發現,整個項目當中,再也沒有出現一個全局變量? 2 你有沒有發現,你再也沒有考慮過加載順序的問題? 3 你有沒有發現,即使兩個框架名字沖突了也沒關系? 4 而且你肯定沒有發現,所有的JS文件的加載過程,已經變成了異步。 5 最後,你還發現,用了requireJS,你的代碼想不寫成面相對象都難?? 關於AMD和CMD 總在網上看大家討論AMD和CMD,到底是什麽東西呢? AMD就是require所倡導的模塊化開發的方式 還有一個叫CommonJS的,不過還是和requireJS有些區別,主要針對NODE後端開發 另外還有一個大名鼎鼎的SeaJS,作者是淘寶的玉伯,於是誕生了CMD規範 根據作者自己的介紹,SeaJS各方面都比requireJS強大 這個就不做評價了 至於區別呢? AMD推崇的是依賴前置: define(["a","b","c"],function(a,b,c){ //我們把所有依賴提前寫好了 a.doSomething (); //這裏直接使用 b.doSometing(); }) CMD推崇的是依賴就近: define(function(require, exports, module) { var a = require(‘./a‘); a.doSomething(); ......... var b = require(‘./b‘) // 依賴可以就近書寫,用到的時候再加載 b.doSomething() }) 當然requireJS也支持CMD的寫法,不過作者本人是不推薦這麽寫的
什麽是模塊化?