現在公司開發的項目大量用到了JS,由於項目模塊要求不同,編程人員的開發水平也不同,前端的JS寫的非常的亂,最近一段時間也在思索着如何才能以最小的成本,在不大幅提高編程難度的前提下最大化的規范前端編程.前幾天看了一篇文章:使用SeaJS實現模塊化JavaScript開發,然后又仔細研究了關天,感覺很不錯,現將研究心得分享如下.
一.模塊化
JS的模塊化編程思路其實來源於一系列編程規范(如CommonJS)和此規范的一些實現(如Nodejs),雖然我對以上提到的這些東東沒有什以研究,但是以我在改別人前端代碼的痛苦經歷和就"模塊化"這三個字的字面意思來講,還是能夠理解它想表達的意思.所謂"模塊化",就是以分治法為依據,把一個大的問題分解成若干個高內聚,低耦合的小的問題.最后通過一系列依賴關系明確的相互調用,最后共同解決那個大問題.這里的兩個關鍵是高內聚與明確的低的耦合.seajs解決的正是后面一個關鍵.
二.seajs概述
大部分情況下,引入一個新的內容都會增加舊有內容的復雜性.既然它的目標是降低復雜性,那么它本身就應該保持足夠的簡單.seajs非常好的遵守了KISS原則.SeaJS僅向全局公開了兩個標識符:seajs與define.
seajs對象有兩個方法use和config.下面是define方法的結構圖
可以看到,define的第一個參數與module的屬性都有一個叫id的.其實他們是一回事,如果傳入id,module則用它,如果不傳入,則使用一個默認的規則生成每個模塊的id.
define的第二個參數dependencies與module的屬性dependencies也是有關系的.module的屬性dependencies是傳入的dependencies集合並上使用require方法獲取的依賴的集合.
define是個重要的方法,它有三個參數:require,exports與module.這個方法的構造完全附合CommonJS下面的Modules/Wrappings規范:使用require獲取“依賴”,使用exports導出“接口”,而module則代表被定義的模塊本身.
seajs對象與define方法的具體使用方法可以參考官方的文檔進行學習.從一般使用順序來講,首先通過seajs.config進行全局配置,然后通過define定義各個模塊,最后通過seajs.use方法使用各模塊.如果能按照這個方法去理解,應該就能夠很快的掌握這個框架.
下面來談談我對使用seajs框架的理解
一.代碼組織
如果一個網站的JS全部是由seajs組織的,你就會發現所有的js只會出現在兩個地方:define里與seajs.use里.在define里,因為這里是模塊定義的位置;在seajs.use里,因為這里是唯一能夠使用由define定義的模塊的地方.通過define定義模塊,所有的功能都被封裝成一個個js對象,且這些對象是閉包的,只有通過seajs.use方法的第二個參數----一個回調函數的參數才能使用這些定義的對象.
二.順序加載
由於seajs僅僅是一個模塊加載器而不是文件加載器,它不能保證腳本一定是按代碼的書寫順序進行加載.為了應付這種情況,seajs推薦使用LABjs配合seajs進行使用.
Labjs的使用也比較簡單,使用script方法加載腳本,使用wait方法執行腳本.更加具體的說明請參照其官方文檔.
三.循環引用
參見下面三段代碼:
//a.js define(function(require, exports, module) { var b = require("b"); alert(b); return b + 1; })
//b.js define(function(require, exports, module) { var a = require("a"); alert(a); return a + 2; })
//html seajs.use(["a.js"], function(result) { alert(result); })
請問發現了什么問題.答案是a模塊與b模塊發生了循環引用.這個問題如果在seajs早期的版本里,貌似會報異常,但是現在不會了.當html頁面請求a模塊時,a模塊發現其依賴於b模塊,於是執行b模塊.但是此時發現其又依賴a模塊.seajs現在的處理是直接返回一個空對象{},所以先是彈出[object Object],然后彈出[object Object]2,最后彈出[object Object]21.
四.腳本改寫
現有其它類庫是不能直接被seajs引用的,而是需要根據SeaJS的的模塊定義規則對現有庫進行一個封裝.以官方封裝的jquery1.7.1為例:
(function(factory) { if (typeof define === 'function') { define('#jquery/1.7.1/jquery', [], factory); } else { factory(); } })(function(require) { //jquery原生代碼 if (require) return $.noConflict(true); });
它的封裝規則,就是在jquery原生代碼的上下加上特定代碼.可以看到當typeof define == 'function',則說明當前js環境為seajs環境,使用define方法進行封裝,否則直接調用傳入的factory匿名函數.
對於匿名函數,如果是seajs環境,則傳入的require變量不為空,則就回調用jquery的noConflict方法.該方法可以讓你自定義jquery的控制變量.
這樣封裝的好處理,如果沒有seajs環境,則可以像普通寫法一樣在script標簽引入腳本,如果是seajs環境,則通過seajs引入.同一個腳本滿足了兩個執行環境.
五.調試模式
seajs還提供了一個官方插件plugin-map.js,它提供了seajs的在線本地調試模式.當你把它放在seajs的同級目錄下,然后在URL后面加上?seajs-debug參數時,窗口的右下腳就會出現一個輸入框讓你輸入需要調試的腳本的路徑.點擊刷新后,網站腳本的來源就變成你設定的來源了.
以上就是我研究seajs的心得.在研究的過程中參考了作者的官方參考與園友的研究成果,在這里一並感謝了.也祝願玉伯這個少有的得到眾多認可的國產框架寫的越來越好:)
參考的文章
1.從 RequireJS 到 SeaJS (1), (2), (3), (4), (5) (可能需要翻牆)
2.在線本地調試大觀 (可能需要翻牆)
3.jQuery 插件的模塊化 (可能需要翻牆)
4.seajs官方
8.SeaJS快速入門,讓js代碼模塊化 - 2011-09-09修訂,新增參考資料
13.Javascript文件加載:LABjs和RequireJS
14.labJS 介紹
15.LABjs
16.我的模塊加載系統 v3
17.工程化前端開發
18.js模塊化開發---js大項目代碼組織和多人協作的解決之道
19.通用前端開發框架(一)