子產品化開發了解
Javascript子產品化程式設計,主要用于實作開發者隻需要實作核心的業務邏輯,其他都可以加載别人已經寫好的子產品。
子產品就是實作特定功能的一組方法。有了子產品,我們就可以友善的使用别人的代碼,需要什麼功能,就加載相應的子產品。
一個子產品其實就是一個實作特定功能的檔案,這個檔案中封裝了一些函數和變量。每個子產品都提供了對外通路的接口,使得使用者可以友善的調用子產品裡面封裝的方法。
有了子產品,我們就可以更友善地使用别人的代碼,需要什麼函數來實作指定功能,就加載相應的子產品就可以了。子產品開發需要大家按照同樣的方式來編寫子產品,否則就都亂套了。
目前,通用的js子產品規範有:AMD(
Asynchronous Module Definition
異步子產品定義)、
CMD
(Common Module Definition) 通用子產品定義和commonJS。其中AMD和CMD是浏覽器端子產品化開發的規範,commonJS是伺服器端的規範。
AMD規範
該規範通過define方法來定義子產品,require方法來調用子產品,實作代碼的子產品加載。子產品将被異步加載,子產品加載不影響後面語句的運作。所有依賴這個子產品的語句,都定義在一個回調函數中,等到加載完成之後,這個回調函數才會運作。
AMD
是
RequireJS
在推廣過程中對子產品定義的規範化的産出。
AMD就隻有一個接口:define(id?,dependencies?,factory);
它要在聲明子產品的時候制定所有的依賴(dep),并且還要當做形參傳到factory中,像這樣:
define(['dep1','dep2'],function(dep1,dep2){...});
要是沒什麼依賴,就定義簡單的子產品,下面這樣就可以啦:
define(function(){
var exports = {};
exports.method = function(){...};
return exports;
});
RequireJS
學習
RequireJS
<!DOCTYPE html>
<html >
<head>
<meta charset="utf-8">
<title>require</title>
</head>
<body>
<script type="text/javascript" src="js/require.js" data-main="js/main.js"></script>
</body>
</html>
主子產品main.js,是整個頁面的入口代碼
//main.js是主子產品,主子產品需要依賴其他子產品來進行工作
//alert("加載成功");
require(['math','sorts'],function(math,sorts){
console.log(math.add(10,12));
console.log(math.chu(24,12));
console.log([1,5,4,3,2].sort(sorts.compare));
});
其他子產品
sorts.js
define(function(){
var compare=function(num1,num2){
if(num1<num2){
return -1;
}else if(num1>num2){
return 1;
}else{
return 0;
}
};
return {
compare:compare
};
});
//math.js
define(function(){
var add=function(x,y){
return x+y;
};
var chu=function(x,y){
return x/y;
};
return {
add:add,
chu:chu
};
});
require()函數接受兩個參數。第一個參數是一個數組,表示主子產品所依賴的子產品;第二個參數是一個回調函數,目前面指定的子產品都加載成功後,它将被調用。加載的子產品會以參數形式傳入該函數,進而在回調函數内部就可以使用這些子產品。主子產品的代碼就寫在回調函數中
require()異步加載所需子產品,浏覽器不會失去響應;它指定的回調函數,隻有前面的子產品都加載成功後,才會運作,解決了依賴性的問題。
require.config配置
require.config是用來配置子產品加載位置,簡單點說就是給子產品起一個更短更好記的名字,比如将百度的jquery庫位址标記為jquery,這樣在require時隻需要寫["jquery"]就可以加載該js,本地的js我們也可以這樣配置:
require.config({
paths : {
"jquery" : ["http://libs.baidu.com/jquery/2.0.3/jquery"],
"a" : "js/a"
}
})
require(["jquery","a"],function($){
$(function(){
alert("load finished");
})
})
通過paths的配置會使我們的子產品名字更精煉,paths還有一個重要的功能,就是可以配置多個路徑,如果遠端cdn庫沒有加載成功,可以加載本地的庫,如:
require.config({
paths : {
"jquery" : ["http://libs.baidu.com/jquery/2.0.3/jquery", "js/jquery"],
"a" : "js/a"
}
})
require(["jquery","a"],function($){
$(function(){
alert("load finished");
})
})
這樣配置後,當百度的jquery沒有加載成功後,會加載本地js目錄下的jquery.在使用requirejs時,加載子產品時不用寫.js字尾的,當然也是不能寫字尾
全局配置
上面的例子中重複出現了require.config配置,如果每個頁面中都加入配置,必然顯得十分不雅,requirejs提供了一種叫"主資料"的功能,我們首先建立一個main.js:
require.config({
paths : {
"jquery" : ["http://libs.baidu.com/jquery/2.0.3/jquery", "js/jquery"],
"a" : "js/a"
}
})
然後再頁面中使用下面的方式來使用requirejs:
<script data-main="js/main" src="js/require.js"></script>
加載requirejs腳本的script标簽加入了data-main屬性,這個屬性指定的js将在加載完reuqire.js後處理,我們把require.config的配置加入到data-main後,就可以使每一個頁面都使用這個配置,然後頁面中就可以直接使用require來加載所有的短子產品名
data-main還有一個重要的功能,當script标簽指定data-main屬性時,require會預設的将data-main指定的js為根路徑。如上面的data-main="js/main"設定後,我們在使用require(['jquery'])後(不配置jquery的paths),require會自動加載js/jquery.js這個檔案,而不是jquery.js。由于require.js預設的檔案字尾名是js,是以可以把main.js簡寫成main。
加載非規範的子產品
這樣的子產品在用require()加載之前,要先用require.config()方法,定義它們的一些特征。
舉例來說,underscore和backbone這兩個庫,都沒有采用AMD規範編寫。如果要加載它們的話,必須先定義它們的特征。
require.config({
shim: {
'underscore':{
exports: '_'
},
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
}
}
});
require.config()接受一個配置對象,這個對象除了有前面說過的paths屬性之外,還有一個shim屬性,專門用來配置不相容的子產品。具體來說,每個子產品要定義(1)exports值(輸出的變量名),表明這個子產品外部調用時的名稱;(2)deps數組,表明該子產品的依賴性。
jQuery的插件可以這樣定義:
shim: {
'jquery.scroll': {
deps: ['jquery'],
exports: 'jQuery.fn.scroll'
}
}
require.js插件
domready插件,可以讓回調函數在頁面DOM結構加載完成後再運作。
require(['domready!'], function (doc){
// called once the DOM is ready
});
text和image插件,則是允許require.js加載文本和圖檔檔案。
define([
'text!review.txt',
'image!cat.jpg'
],
function(review,cat){
console.log(review);
document.body.appendChild(cat);
}
);
類似的插件還有json和mdown,用于加載json檔案和markdown檔案。
CMD規範
CMD
(Common Module Definition) 通用子產品定義。該規範明确了子產品的基本書寫格式和基本互動規則。該規範是在國内發展出來的。AMD是依賴關系前置,CMD是按需加載。
AMD 是 RequireJS 在推廣過程中對子產品定義的規範化産出。
CMD是SeaJS在推廣過程中對子產品定義的規範化産出
對于依賴的子產品,
AMD
是提前執行,
CMD
是延遲執行。
CMD:延遲執行(運作按需加載,根據順序執行)
<span style="font-weight: normal;">// 所有子產品都通過 define 來定義
define(function(require, exports, module) {
// 通過 require 引入依賴
var $ = require('jquery');
var Spinning = require('./spinning');
// 通過 exports 對外提供接口
exports.doSomething = ...
// 或者通過 module.exports 提供整個接口
module.exports = ...
});</span>
<span style="font-weight: normal;">// 所有子產品都通過 define 來定義
define(function(require, exports, module) {
// 通過 require 引入依賴
var $ = require('jquery');
var Spinning = require('./spinning');
// 通過 exports 對外提供接口
exports.doSomething = ...
// 或者通過 module.exports 提供整個接口
module.exports = ...
});</span>
SeaJS
學習
SeaJS
<!DOCTYPE html>
<html >
<head>
<meta charset="utf-8">
<title>seajs</title>
</head>
<body>
<script type="text/javascript" src="sea.js"></script>
<script type="text/javascript">
seajs.use('dialog',function(Dialog){
//alert(Util.add(1,2));
//console.log([1,5,4,3,2].sort(Util.sorts));//[1, 2, 3, 4, 5]
var res=Dialog.sum([1,5,4,3,2]);
console.log(res);//15
});
</script>
</body>
</html>
util,js
define(function(require,exports){
exports.add=function(x,y){
return x+y;
};
exports.sorts=function(num1,num2){
if(num1>num2){
return 1;
}else if(num1<num2){
return -1;
}else{
return 0;
}
};
});
dialog.js
define(function(require,exports){
var util=require('./util.js');
exports.sum=function(arr){
var sum=0;
console.log(arr.sort(util.sorts));
for(var i=0;i<arr.length;i++){
sum+=arr[i];
}
return sum;
};
});
SeaJS對子產品的态度是懶執行, 而RequireJS對子產品的态度是預執行
SeaJS隻會在真正需要使用(依賴)子產品時才執行該子產品
SeaJS是異步加載子產品的沒錯, 但執行子產品的順序也是嚴格按照子產品在代碼中出現(require)的順序.
而RequireJS會先盡早地執行(依賴)子產品, 相當于所有的require都被提前了, 而且子產品執行的順序也不一定100%就是先mod1再mod2
是以你看到執行順序和你預想的完全不一樣!
CommonJS規範
CommonJS
是伺服器端子產品的規範,
Node.js
采用了這個規範。Node.JS首先采用了js子產品化的概念。
根據
CommonJS
規範,一個單獨的檔案就是一個子產品。每一個子產品都是一個單獨的作用域,也就是說,在該子產品内部定義的變量,無法被其他子產品讀取,除非定義為
global
對象的屬性。
加載子產品使用
require
方法,該方法讀取一個檔案并執行,最後傳回檔案内部的
module.exports
對象。