寫在前面
最早接觸javascript的時候,javascript代碼直接扔在script标簽裡面就完事了。
反正代碼不多,互動簡單,邏輯不難,和HTML混在一起也未嘗不可。
後來互動越來越複雜,代碼越多越多了,我們就開始把JS代碼獨立到了單獨的JS檔案中。
公共的庫引用在前,自己的邏輯代碼引用在後,全局變量定義在HTML内部,在獨立JS檔案中直接使用變量就好。
我們會經常看到下面這種代碼:
<script src="1.js"></script>
<script src="2.js"></script>
<script src="3.js"></script>
<script src="4.js"></script>
<script src="5.js"></script>
<script src="6.js"></script>
通過script标簽順序去js管理依賴關系。
阮一峰老師在Javascript子產品化程式設計(三):require.js的用法
一文中總結了這樣寫法的缺點:
首先,加載的時候,浏覽器會停止網頁渲染,加載檔案越多,網頁失去響應的時間就會越長;
其次,由于js檔案之間存在依賴關系,是以必須嚴格保證加載順序(比如上例的1.js要在2.js的前面),依賴性最大的子產品一定要放到最後加載.
當依賴關系很複雜的時候,代碼的編寫和維護都會變得困難。
而requirejs的誕生便是為了解決這個問題。
requirejs
在官網把requirejs 下載下傳回來之後。使用一般的方法引入:
但是這樣的方法,還是可能在加載require.js的時候導緻網頁失去響應。解決方案一般有兩種:
- 把上面的代碼放到網頁底部
- 使用異步的方法加載,如下:
async屬性 表明這個檔案需要異步加載,避免網頁失去響應。
不過IE下不支援這個屬性,隻支援defer,是以可以把defer也寫上。
加載主子產品
在上一步,我們已經引入了require了,那麼require怎麼知道我們究竟要加載什麼東西呢?答案是使用data-main屬性。
假設我們的主子產品為js/home.js,引入代碼應該如下:
<script src="js/require.js" data-main="js/home"></script>
//require.js預設檔案字尾為js,是以home.js可以寫成home。
接下來我使用58HouseSearch 的代碼來講解一下require的使用。
在此項目裡面,重構前大概就是JS變量漫天飛,js檔案裡面各種函數到處亂放。一開始用起來還沒什麼,後來加入了更多功能的時候,JS代碼維護起來就疼不欲生了。是以托了個小夥伴幫忙使用子產品化思想重構了一下JS代碼。
上面說了,我們首先需要建立我們的子產品,在這個項目裡面,主子產品叫home.js。
home.js中我們需要配置一下require.config.
require.config({
baseUrl: '/DomainJS/',
paths: {
jquery: "lib/jquery-1.11.3.min",
"AMUI": "lib/amazeui.2.7.1.min",
"jquery.range": "lib/jquery.range",
"es5": "lib/es5",
"mapController": "mapController",
"addToolbar": "addToolbar",
},
shim: {
"addToolbar": {
deps: ["jquery"]
},
"jquery.range": {
deps: ["jquery"]
}
}
});
在這裡我主要配置了一下baseURL(所有子產品的查找根路徑),paths(名稱映射),shim(
為那些沒有使用define()來聲明依賴關系、設定子產品的”浏覽器全局變量注入”型腳本做依賴和導出配置。)
關于require.config的詳細内容可以看下下面這些文章:
- RequireJS進階:配置檔案的學習
- RequireJS進階:子產品的優化及配置的詳解
配置做完了,我們也可以開始真正寫我們的邏輯代碼了,我們使用require來加載我們需要的庫。
代碼如下:
require(['domready!', 'jquery', 'AMUI', 'mapController', 'city', 'commuteGo'],
function (doc, $, AMUI, mapController, city, commuteGo) {
city.initAllCityInfo();
mapController.init();
$("input[name='locationType']").bind('click',
mapController.locationMethodOnChange)
$("input[name='vehicle']").bind('click', commuteGo.go)
$('#Get58Data').bind('click', function(e) {
e.preventDefault();
mapController.Get58DataClick();
e.stopPropagation();
});
$.ajax({
type: "post",
url: "../Commom/GetPVCount",
data: { },
success: function (result)
{
if (result.IsSuccess){
$("#lblPVCount").text(result.PVCount);
}else {
$("#lblPVCount").text();
console.log(result.Error);
}
}
});
$('#search-offcanvas').offCanvas({ effect: 'overlay' });
$(".amap-sug-result").css("z-index", );
})
忽略function裡面的具體邏輯,加載如下:
require(['domready!', 'jquery', 'AMUI', 'mapController', 'city', 'commuteGo'],
function (doc, $, AMUI, mapController, city, commuteGo){
//todo
});
第一個參數為一個數組,表示所依賴的子產品,此處為[‘domready!’, ‘jquery’, ‘AMUI’, ‘mapController’, ‘city’, ‘commuteGo’];
第二個參數為回調函數,目前面指定的子產品都全部加載成功之後,便調用此函數。加載的子產品會以參數形式傳入此函數,進而在回調函數内部就可以使用這些子產品啦。
require()異步加載所需子產品的時候,此時浏覽器并不會失去響應;目前面的子產品加載成功之後,執行回調函數才會運作我們的邏輯代碼,是以解決了依賴性問題。
講完了子產品加載,我們下面講一下子產品編寫。
AMD子產品編寫
require.js加載的子產品的采用的AMD規範。是以我們的子產品必須按照AMD的規定來寫。
關于AMD規範詳情可以看這個文章:Javascript子產品化程式設計(二):AMD規範
子產品有兩個情況,不依賴其他子產品和依賴其他子產品。
不依賴其他子產品
直接define定義,使用function回調。
58HouseSearch/DomainJS/helper.js
define(function () {
//擷取URL中的參數
var getQueryString= function (name) {
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
var r = window.location.search.substr().match(reg);
if (r != null) return unescape(r[]); return null;
}
return {
getQueryString: getQueryString,
};
})
依賴其他子產品
define中如同require一樣,用數組表明需要加載的子產品,function回調。
58HouseSearch/DomainJS/marker.js
define(['mapSignleton', 'city', 'transfer'],
function(mapSignleton, city, transfer) {
var _map = mapSignleton.map;
var _workMarker = null;
var _markerArray = [];
var load = function(x, y, locationName) {
_workMarker = new AMap.Marker({
map: _map,
title: locationName,
icon: 'http://webapi.amap.com/theme/v1.3/markers/n/mark_r.png',
position: [x, y]
});
}
var add = function(address, rent, href, markBG) {
new AMap.Geocoder({
city: city.name,
radius:
}).getLocation(address, function(status, result) {
if (status === "complete" && result.info === 'OK') {
var geocode = result.geocodes[];
var rentMarker = new AMap.Marker({
map: _map,
title: address,
icon: markBG ? 'IMG/Little/' +
markBG : 'http://webapi.amap.com/theme/v1.3/markers/n/mark_b.png',
position: [geocode.location.getLng(), geocode.location.getLat()]
});
_markerArray.push(rentMarker);
rentMarker.content = "<div><a target = '_blank' href='"
+ href + "'>房源:" + address + " 租金:" + rent + "</a><div>"
rentMarker.on('click', function(e) {
transfer.add(e, address);
});
}
})
};
var clearArray = function() {
if (_markerArray && _markerArray.length > )
_map.remove(_markerArray);
_markerArray = [];
}
var clear = function() {
if (_workMarker) {
_map.remove(_workMarker);
}
}
return {
load: load,
add: add,
clearArray: clearArray,
clear: clear
};
});
這樣的話,一個供require調用的子產品也就寫好了。
最後感謝小夥伴Larry Sean 幫忙重構代碼。
全文完。