高富帥seajs使用示例及spm合併壓縮工具露臉

1、扯淡高富帥

好久好久之前……………………的好久好久的之後,也就是昨天的昨天(2012-07-07),D2前端技術論壇,下午3點,分會場,@老趙分享其開源項目(什麼來着?名字彷佛很難記,讓我找找~~)Jscex. 印象較深的是末了,其戲稱seajs爲高富帥。javascript

爲什麼有此感慨?一樣是開源項目,seajs由於①原做者我的影響力②推廣渠道或者說團隊影響力③背後乾爹的大力支持等緣由,其知名度以及接受程度要比Jscex高很多。css

我本身彷佛也有相似的感觸,10年年初的時候,我本身折騰了一個關於CSS3的小項目,有別於通常的參考文檔,這裏容許用戶(主要是同行)添加相關的文章,以及一些須要注意的技術點。
能夠添加相關文章 能夠添加相關知識點html

雖知響應應甚少,然而,幾年下來,令我嘖舌的是,竟然沒有1條更新信息時出自他人之手。頭一年,我還樂此不疲地更新相關文章以及本身發現的一些知識注意點,但後來,本身也繳械投降,懶得折騰了,純碎看成靜態的參考文檔看了。前端

所謂獨木難支,我的的精力老是有限的,影響力也是有限的。不少時候,本身折騰點東西就像鵝毛飄到水裏同樣,漣漪都沒一個,不免心有悸動:要不去傍個高富帥、白富美~~java

哈哈,不過人貴自知,不是人人都能成爲郭美美的,沉澱心態,低調蛻變,說不定,之前一些渴求的東西會以附加產物的形式悄悄來到你的身邊。node

2、繼續扯,關於新東西的學習

我記得大學,應該是根叔吧,關於畢業,其說過(大體意思):「若是畢業的時候,你以爲你作什麼都沒有問題,那你就是合格的本科生。」
我將其理解爲:畢業真正考量的不是學到了多少東西,而是是否有足夠強大的學習能力。css3

很多人詢問過我,CSS該怎麼學,JS該怎麼學?我能回答的可能是,要重視基礎,要捨得吃苦~~至於其餘的,說得再多,其實也不必定有用。一來每一個人的學習能力與領悟力有差別;二來,本身的學習方法不必定適合他人。指個大方向,應該更有用的,正所謂「師父領進門修行在我的」。git

基礎是個很玄妙的東西,紮實的基礎猶如武俠裏男主角深厚的內功,內功深厚了學什麼其餘功夫都很快,並且能夠很好駕馭;一樣,基礎紮實,一些新技術什麼的上手也快,並且能夠自如靈活運用!程序員

前端發展突飛猛進,熱門的東西容易產生很強烈的熱氣,令人產生浮躁之氣,尤爲對於新手而言,這種燥氣是不利的。說不定就會捨本逐末,我忽然想起了@漁人碼頭以前發過的一個微博——「說實話看了這標題,看了這年齡,看了這評價,我鴨梨山大!鴨梨山大!鴨梨山大!」,圖以下:
github

所謂「資深」,並非順應所謂的潮流,追逐一些新東西。這些光鮮亮麗,華而不實的東西,猶如時尚的衣服同樣,第二年就老氣過期了。潮流一直在前行,而你,卻停不了;由於一旦停下來,就落後了,out了,這樣的過活是很累的;而實際上,你所掌握的又可能都是些浮於表面的東西,不過卻是糊弄糊弄些外行,混口飯吃吃!

貌似扯遠了,趕快,用繩子拴住,拉直,用力拉,拉啊,拉……尼瑪,終於拉回來了,擦下汗,咱們回到正題,關於seajs. seajs能夠算是前端屆國產新貴,其年齡(我不肯定)應該小於2歲,不過百度之,能夠看到相關的文章仍是很多的,確實有「高富帥」的跡象。seajs主要處理模塊加載,基於commonjs規範,使用spm壓縮合並顆粒JS模塊。

具體如何如何使用,如用法啊,參數啊什麼的,能夠點擊這裏的手冊進行查看。更深刻的理解能夠參考原做者@玉伯也叫射鵰在D2上分享的PDF文件(來自微盤)。

下面這篇文章也是很不錯的:JavaScript模塊化開發庫之SeaJS,對模塊等對比的分析解釋是很受用的。

下面我想講講我是如何學習相似seajs這樣的新東西。

新東西的學習
1.感性認識,情感化認知 
並非全部的程序員都適合作前端,聽說,只有20%~30%, 緣由?我想,可能一部分緣由就是情感化思惟,畢竟,咱們所作的東西離用戶是很接近的。就我而言,我很在乎一個新事物從總體上給個人feeling, 這種感性的認識可讓我準確的把握這個事物的特質,因而會造成更合適的策略處理之。換個容易理解的說法:關於追求新認識的姑娘,我很在乎這個姑娘總體上給個人feeling, 這種感性的認識可讓我準確把握這個姑娘的特質,因而,腦中會天然造成更合適的追求策略。微博上不是有這麼個段子嘛:追女孩子,若是這個女孩懵懂無知,就帶她去看世間繁華;若是這個女孩看盡世間百態,就帶她去騎旋轉木馬!總體感受(感性認識)→類型特質→追求策略。

如今的問題是,若是得到準確的總體feeling呢!道聽途說,一面之緣等容易產生誤差。咱們須要經過多個途徑去了解。首當其衝是官方網站對其描述;而後,就是其餘人對其評價,認識等。仍是追妹子例子,去官網至關因而詢問其父母對這個姑娘的評價(固然,父母都會慣着本身的孩子,很差的話通常都不會說的);詢問其餘人就是詢問姑娘朋友們、同事們的評價,非誠勿擾不少男嘉賓就死在「好友評價」環節,可見後者仍是相對比較客觀的!

所以,反應到現實就是,去官方網站查看,若是是開源項目,也能夠去github查看;查看同行們寫的相關文章或微博,看看其餘人是如何介紹與評價的。

2. 實現方式、API參數
就像咱們使用一個全新的jQuery插件同樣,其中必經一步是查看文檔,熟悉其API,知道其一些特殊用法等。 比如要熟悉姑娘身高體重,三圍,鞋子尺碼,知道姑娘的一些癖好等。

3. 實踐出真知
古語有云:紙上得來終覺淺,絕知此事要躬行。實踐是必須的。爲什麼?

首先,不少時候,咱們面對一個全新的東西,其文檔或手冊上API用法、解釋等會讓咱們不知所云,disorder – 混亂。彷彿回到了大學學「拉普拉斯不等式」的時候,看得頭疼。可是,若是你經過本身製做實例,一個一個嘗試其所說的內容,不少一眼看上去頭疼的東西,立馬變得猶如春風拂面。

其次,由於是全新的東西,人總不免被過往的經驗(或者說慣性思惟)束縛,有些內容真正意思可能並非腦中所想的那樣。舉個例子,按照經驗式的理解,console.log([2, 10].sort())的結果應該是[2, 10],然而,實際上,在JavaScript中,結果是[10, 2], 字符串排序。這種跟以往經驗不一樣的表現差別須要經過實踐纔能有切身的體會。

這也是爲什麼會出現「婚前試愛」這種現象的緣由。

seajs的實踐
您可能跟我同樣,也是最近才接觸seajs, 您可能也跟我同樣,被手冊上頻繁出現的dependencies, factory等詞閃花了眼睛。只怪爾等JavaScript不夠精深,可是,seajs號稱API簡單,所以,雖然一些深刻的東西咱們一會兒消化不了,可是,這並不影響咱們使用這個工具。

如何使用?很簡單,既然seajs是用來模塊化JS的,所以,咱們想一個稍微複雜,又能承受的例子做爲實踐,從頭至尾走一遍,順便spm也摸索下,若是都經過,天然……你說姑娘都讓你實踐了,從頭至尾都*(和諧)過了,順便*(和諧)也摸索過了。好了,你們都懂的……

OK, 我想了想,就實現一個簡單的彈出框效果,有必定數量的模塊JS, 功能實用,且不復雜。下面,若是你是新人,能夠跟我一塊兒,看看,如何使用seajs模塊化相關腳本,同時實現的彈出框效果。

3、seajs使用示例-彈出框

如下實現爲本身的思路,可能比較低級與囉嗦,熟悉seajs的人能夠直接跳過。

1. 半透明背景遮罩層
說到彈框,首先想到的是後面黑色半透明的遮罩層。咱們就套用:

define(function(require, exports, module) {
  // 模塊代碼
});

這種寫法應該就能夠了,須要暴露的接口直接exports.*就能夠了。

恩,咱們新建一個名叫overlay.js的JavaScript文件;而後,咱們就着手建立這個遮罩層元素。Wait! 這個建立元素的方法應該模塊化,方便其餘地方公用,例如彈框中元素建立等。

所以,咱們須要把overlay.js擱置一邊~~

2. 元素建立模塊JS
同目錄下,新建一個名爲elementCreate.js的JavaScript文件,一樣的套用,以下JavaScript代碼,註釋也要規範哦~~

/**
 * 建立元素
 */
 
define(function(require, exports) {
    exports.create = function(tagName, attr) {
        var element = null;
        if (typeof tagName === "string") {
            element = document.createElement(tagName);    
            
            if (typeof attr === "object") {
                var keyAttr, keyStyle;
                for (keyAttr in attr) {
                    if (keyAttr === "styles" && typeof attr[keyAttr] === "object") {
                        // 樣式們
                        for (keyStyle in attr[keyAttr]) {
                            element.style[keyStyle]    = attr[keyAttr][keyStyle];
                            
                            if (keyStyle === "opacity" && window.innerWidth + "" == "undefined") {
                                element.style.filter = "alpha(opacity="+ (attr[keyAttr][keyStyle] * 100) +")";
                            }
                        }
                    } else {
                        if (keyAttr === "class") {
                            keyAttr = "className";
                        }
                        element[keyAttr] = attr[keyAttr]attr["class"];
                    }
                    
                }
            }
        }
        return element;
    };
});

exports.create表示模塊暴露在外的方法名稱是createreturn element;表示該方法返回的是建立的元素對象。

3. 回到半透明遮罩層
如今,咱們就可使用elementCreate.js模塊了,要使用這個模塊,就得使用require這個函數(注意:require名字必須是require,不能修改)。

var elementCreate = require("./elementCreate");

因而,var element = elementCreate.create("div", {});就能夠建立一個div元素啦!相關完整代碼以下(具體遮罩層實現細節能夠忽略):

/**
 * 黑色半透明遮罩層
 */
 
define(function(require, exports, module) {
    var elementCreate = require("./elementCreate");
    var overlay = (function() {
        var element = elementCreate.create("div", {
            styles: {
                display: "none",
                width: "100%",
                backgroundColor: "#000",
                opacity: 0.35,
                position: "absolute",
                zIndex: 1,
                left: 0,
                top: 0,
                bottom: 0    
            }
        });
        document.body.appendChild(element);
        
        return {
            display: false, 
            show: function() {
                element.style.display = "block";
                this.display = true;
                return this;
            },
            hide: function() {
                element.style.display = "none";    
                this.display = false;
                return this;
            }
        };    
    })();    
    
    exports.overlay = overlay;
});

您能夠狠狠地點擊這裏:seajs下黑色半透明遮罩效果測試demo

seajs黑色半透明遮罩demo頁面截圖

demo頁面實際上已經演示了seajs的應用,代碼以下:

<script src="sea.js"></script>
<script>
seajs.use("./overlay", function(overlay) {
    // overlay.overlay就是遮罩層元素相關對象
});
</script>

4. 彈框了
遮罩層搞定了,下面就是彈框了。咱們新建一個名爲」flbox.js「的js文件。實現彈框效果,首先要黑色半透明層(能夠require overlay.js模塊),而後標題欄,關閉按鈕等建立(能夠require elementCreate.js);最後,彈框裏面內容須要Ajax加載,可是,並無ajax的模塊,所以,咱們要讓ajax模塊插下隊,先搞定之。

5. ajax模塊
由於是示例頁面,所以,咱們沒有必要寫得很完善與詳細,這裏,咱們就能夠單純實現使用get方法加載外部一段HTML。因而,搗鼓搗鼓,以下代碼:

/**
 * ajax方法
 */

define(function(require, exports) {
    exports.get = function(url, succCall) {
        if (url + "" === "undefined") {
            console.log("請求地址缺失!");
            return;    
        }
        
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4 && xhr.status === 200 && typeof succCall === "function") {
                succCall.call(xhr, xhr.responseText);
            }
        };
        
        xhr.open("GET", url, true);
        xhr.send();
    };
});

輕輕鬆鬆,您能夠狠狠地點擊這裏:ajax模塊測試demo

seajs ajax加載外部頁面測試截圖

測試OK,如今回到彈框模塊。

6. 彈框模塊
下面,須要的模塊都有了,咱們只要require進來,就能夠用啦!

require("./elementCreate")
require("./ajax")
require("./overlay")

雖然,單純地展現代碼我本身也以爲挺傻的沒勁,可是,對於天地不知的初學者而已,這種餵奶喂到嘴邊的方式是他們學習所須要的。

/**
 * 簡易彈框
 */

define(function(require, exports) {
    var funCreate = require("./elementCreate")
        , funAjax = require("./ajax")
        , overlay = require("./overlay");
        
    var eleWin = funCreate.create("div", {
            styles: {
                display: "none",
                position: "fixed",
                left: "50%",
                zIndex: 2    
            }    
        })
        , eleBar = funCreate.create("div", {
            styles: {
                fontSize: "12px",
                padding: "8px",
                backgroundColor: "#eee"
            }    
        })
        , eleClose = funCreate.create("a", {
            href: "javascript:",
            styles: {
                fontSize: "12px",
                color: "#34538b",
                textDecoration: "none",
                position: "absolute",
                margin: "-22px 0 0 85%"    
            }    
        })
        , eleBody = funCreate.create("div", {
            styles: {
                backgroundColor: "#fff",
                borderTop: "1px solid #ddd"
            }    
        })
        , eleOverlay = overlay.overlay;
    
    eleWin.appendChild(eleBar);
    eleWin.appendChild(eleClose);
    eleWin.appendChild(eleBody);
    
    document.body.appendChild(eleWin);
    
    eleBar.innerHTML = "彈出框";
    
    eleClose.innerHTML = "[關閉]";
    eleClose.onclick = function() {
        flbox.close();    
        return false;
    };
    
    var flbox = {
        loading: function() {
            eleBody.innerHTML = '<div style="width:200px;height:100px;padding:10px;">加載中...</div>';    
            this.position();
        },
        open: function(url) {
            var self = flbox;
            funAjax.get(url, function(html) {
                eleBody.innerHTML = html;
                self.position();
            });
        },
        position: function() {
            eleWin.style.display = "block";
            eleOverlay.show();
            var widthWin = eleWin.clientWidth
              , heightWin = eleWin.clientHeight;
                  
            // 定位
            eleWin.style.marginLeft =  "-" + widthWin / 2 + "px";
            eleWin.style.top = (screen.availHeight - heightWin - 100) / 2 + "px";
        },
        close: function() {
            eleOverlay.hide();
            eleWin.style.display = "none";
            eleBody.innerHTML = "";    
        }
    }
    
    exports.open = flbox.open;
});

7. ajax地址增長隨機數
爲了不彈框打開時IE瀏覽器下的緩存問題,咱們須要一個url地址增長隨機查詢字符串的方法。新建名爲queryRandom.js的模塊JS文件,鍵入以下代碼:

/**
 * URL地址後面增長隨機參數
 */
define(function(require, exports, module) {
    exports.queryRandom = function(url) {
        var strQueryRandom = "random=" + Math.random();
        var arrQuery = url.split("?");
        if (arrQuery[1] != undefined) {
            // 含查詢字符串
            if (url.slice(-1) === "&") {
                url = url + strQueryRandom;        
            } else {
                url = url + "&" + strQueryRandom;
            }
        } else {
            // 不含查詢字符串
            url = url + "?" + strQueryRandom;    
        }
        return url;
    };
});

您能夠狠狠地點擊這裏:seajs增長隨機查詢demo

seajs地址增長隨機查詢字符串截圖 張鑫旭-鑫空間-鑫生活

8. 萬事具有、終極測試
如今,咱們新建一個主線js, 命名爲main.js, 配合demo頁面,咱們寫個很簡單的方法——即點擊某連接,彈框顯示該連接href屬性所對應的頁面內容。
完整代碼以下:

/** 
 * 主線js 
 * 點擊小圖查看大圖測試
*/
define(function(require, exports) {
    var query = require("./queryRandom")
      , flbox = require("./flbox");
    
    exports.bind = function(element) {
        element.onclick = function() {
            var href = this.href;
            flbox.open(query.queryRandom(href));    
            return false;
        };
    };
});

您能夠狠狠地點擊這裏:seajs實現簡易彈框效果demo(不支持IE6瀏覽器)
seajs使用顯示彈出框效果demo截圖 張鑫旭-鑫空間-鑫生活

而調用的腳本很簡單:

seajs.use("./main.js", function(test) {
    test.bind(document.getElementById("test"));
});

這很好地體現的seajs API簡單的優勢。

因而乎,遵循commonjs寫法規範的各個子模塊們,在seajs簡單的API下,組合出了咱們想要的彈出框效果。而,這些子模塊們,又能夠被用在其餘地方。沒有腳本衝突,沒有上線時差衝突。可維護性大大提高。這就是seajs的意義所在。

上面簡易彈框效果相關代碼我已經打包了,點擊這裏(右鍵 – [目標|連接]另存爲):seajs-flbox.zip

4、spm做用及使用

雖說,咱們須要的彈框效果已經實現了,可是,倒是不能直接上線使用的,爲什麼?

咱們看下其http請求,以下截圖:
http請求們 張鑫旭-鑫空間-鑫生活

哎呀呀,這麼多JS請求,嚇着烏索普脆弱的當心臟了。雖然,seajs模塊化的書寫提升了維護性,可是,也帶來了前端性能的問題!如何解決?

如今,就是救世主spm出場的時候了,spm → seajs package manage?

spm能夠合併並壓縮seajs中的各個模塊JS文件。仍是上面的彈框demo頁面,咱們在地址後面增長」?spm=1「,以下圖所示,訪問之
spm下seajs模塊合併並壓縮後的demo頁面 張鑫旭-鑫空間-鑫生活

再次刷新頁面,查看HTTP請求數,額哈哈,本來6個JS請求,如今合併成1個啦:
只有一個JS相關的HTTP請求截圖 張鑫旭-鑫空間-鑫生活

命令行中以下代碼即實現效果:

spm build main.js --combine --app_url zxx

而後,main.js中所用到的相對路徑require的全部模塊就會合並並壓縮,新生成的文件(若是沒有指定輸出路徑)會是__build/main.js
smp合併並壓縮main.js命令行中運行 張鑫旭-鑫空間-鑫生活

spm的安裝與使用
spm安裝與使用github上有介紹。大體以下:

安裝
先安裝node和npm, http://nodejs.org/#download;而後安裝spm, 以下命令行代碼:

$ npm install spm -g

smp的安裝進度示意圖 張鑫旭-鑫空間-鑫生活

build[option]模塊
壓縮一個JS模塊:

$ spm build a.js

壓縮併合並:

$ spm build a.js --combine

壓縮併合並全部獨立模塊(包括絕對路徑的):

$ spm build a.js --combine_all

清除建立的文件:

$ spm build --clear

經過定義build-config.js能夠作更多的事情:
build-config.js:

module.exports = {
  "base_path": "/path/to/libs/",
  "app_url": "http://test.com/js/app/",
  "app_path": "/path/to/app/",
  "loader_config": "path/to/init.js"
};

更多信息,能夠調用:

$ spm help build

以上爲我以爲經常使用的。這裏推薦一篇關於smp使用的文章:seajs的spm使用摸索。這篇文章對spm各個參數都有解釋,另外,還提供了自定義輸出路徑等參數的使用示例。能夠說是對github上使用的很好補充。

smp我也是新手,也沒有什麼其餘心得什麼的,就這樣吧~~

5、結了個語

中小的企業有其自己的侷限性,例如優秀的技術人才,有事就會產生一些無奈。切身說法,咱們的web端相關的JS腳本中各個模塊從誕生之初就在一個JS文件中,住在一間房子裏。通常狀況下是沒有問題的,可是,多多少少會出現這樣的情況:

項目經理:我們這個圖片上傳模塊要改下,後天上線!
前端仔:沒問題。

由於前端仔成功約到了和女神一塊兒看電影,一來興奮;二來怕加班。其進入仙人模式,歘歘歘三下五除二把JS端的改動都搞定了,就等後臺那邊對頁面修改了!
可是,剛改完不久,悲劇來了——

項目經理:趕快,發現一個重大bug, 支付計算貌似有漏洞,趕快fix它,上線!
前端仔:沒問題。

又進入無敵模式,快刀斬亂麻,漏洞補上了。結果,準備發佈JS上線的時候,前端仔抓狂了

尼瑪,不能上啊。圖片上傳頁面還沒改,上線了雖然支付沒有問題,可是,圖片上傳被搞殘了啊!

因而本來興奮的前端仔像被艾尼路劈了同樣,焉了。因而,回到苦逼狀態,把以前改好的圖片上傳模塊又改回先前的樣子……

這就是沒有模塊化的痛苦。然而,問題早就知道,可是,一直沒有去解決,爲什麼?沒人搞這件事。其實,靜態資源壓縮併合並對於有經驗編程人員講並非難事。看大衆點評網,雖然你們都是使用MooTools, 可是,其靜態資源就能夠壓縮合並,可是,咱們這裏,沒人弄,雖然提過,雖然有人說過願意作這個事情。我曾研究過淘寶開源的一個JS合併加載工具,可是是PHP的,而咱們用的.net, 有點折騰不來!因而,就這樣拖着~~

不過,如今,貌似能夠不用依賴.net程序員們了,在node環境下,使用seajs模塊化書寫,spm合併並壓縮。先前的一些維護問題顯然不復存在。因而,咱們的精力能夠更多地放在代碼實現以及如何更好地劃分模塊上,這有助於工做效率的提高。

所以,seajs我是會去使用的,雖然它的做者不是foreigner. 至於文章一開始提到的Jscex, 恩……它能夠幫我把到妹子嗎?不能!能夠幫我釣到大魚嗎?不能!能夠幫我車子加油嗎?不能!所以,我感受我不會去用它。

扯淡居多,但願不會浪費您寶貴的時間。感謝閱讀,若是表述不許確之處,歡迎指正。

對了,我有問題求教:在彈出框實例頁面中,(require, exports, module)這三個參數惟獨module一次也沒有使用,所以,我想問下,module在何種情境下使用,有何做用?

原創文章,轉載請註明來自張鑫旭-鑫空間-鑫生活[http://www.zhangxinxu.com]
本文地址:http://www.zhangxinxu.com/wordpress/?p=2476

相關文章
相關標籤/搜索