layout: post
title: 使用requirejs編寫模塊化代碼
category: javascript
date: 2016-10-22 00:00:00
tags:javascript
最先接觸javascript的時候,javascript代碼直接扔在script標籤裏面就完事了。css
反正代碼很少,交互簡單,邏輯不難,和HTML混在一塊兒也何嘗不可。html
後來交互愈來愈複雜,代碼越多越多了,咱們就開始把JS代碼獨立到了單獨的JS文件中。html5
公共的庫引用在前,本身的邏輯代碼引用在後,全局變量定義在HTML內部,在獨立JS文件中直接使用變量就好。java
咱們會常常看到下面這種代碼:jquery
<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管理依賴關係。git
阮一峯老師在Javascript模塊化編程(三):require.js的用法
一文中總結了這樣寫法的缺點:github
首先,加載的時候,瀏覽器會中止網頁渲染,加載文件越多,網頁失去響應的時間就會越長;web
其次,因爲js文件之間存在依賴關係,所以必須嚴格保證加載順序(好比上例的1.js要在2.js的前面),依賴性最大的模塊必定要放到最後加載.ajax
當依賴關係很複雜的時候,代碼的編寫和維護都會變得困難。
而requirejs的誕生即是爲了解決這個問題。
在官網把requirejs 下載回來以後。使用通常的方法引入:
<script src="js/require.js"></script>
可是這樣的方法,仍是可能在加載require.js的時候致使網頁失去響應。解決方案通常有兩種:
把上面的代碼放到網頁底部
使用異步的方法加載,以下:
<script src="js/require.js" defer async="true" ></script>
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的詳細內容能夠看下下面這些文章:
配置作完了,咱們也能夠開始真正寫咱們的邏輯代碼了,咱們使用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(0); console.log(result.Error); } } }); $('#search-offcanvas').offCanvas({ effect: 'overlay' }); $(".amap-sug-result").css("z-index", 9999); })
忽略function裏面的具體邏輯,加載以下:
require(['domready!', 'jquery', 'AMUI', 'mapController', 'city', 'commuteGo'], function (doc, $, AMUI, mapController, city, commuteGo){ //todo });
第一個參數爲一個數組,表示所依賴的模塊,此處爲['domready!', 'jquery', 'AMUI', 'mapController', 'city', 'commuteGo'];
第二個參數爲回調函數,當前面指定的模塊都所有加載成功以後,便調用此函數。加載的模塊會以參數形式傳入此函數,從而在回調函數內部就可使用這些模塊啦。
require()異步加載所需模塊的時候,此時瀏覽器並不會失去響應;當前面的模塊加載成功以後,執行回調函數纔會運行咱們的邏輯代碼,所以解決了依賴性問題。
講完了模塊加載,咱們下面講一下模塊編寫。
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(1).match(reg); if (r != null) return unescape(r[2]); 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: 1000 }).getLocation(address, function(status, result) { if (status === "complete" && result.info === 'OK') { var geocode = result.geocodes[0]; 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 > 0) _map.remove(_markerArray); _markerArray = []; } var clear = function() { if (_workMarker) { _map.remove(_workMarker); } } return { load: load, add: add, clearArray: clearArray, clear: clear }; });
這樣的話,一個供require調用的模塊也就寫好了。
最後感謝小夥伴Larry Sean 幫忙重構代碼。
全文完。