關於seajs


雖然已經有很長時間沒寫JavaScript,但很多時候看到一些應用還是會帶着好奇心去研究一下。之前是看騰訊的朋友網,它的webchat做的很不錯(雖然ff下有bug,也有消息丟失的情況,但總體的設計和體驗上還是很不錯的),抓包大致看了看請求和部分代碼。

它的聊天框使用div + contenteditable屬性實現了一個簡單的“富”文本編輯器,但好像僅支持表情(源碼里有正則,對指定的img節點的src進行過濾)。我分別在chrome和ie下使用了它的聊天框,發現IE下在粘貼會先出現粘貼的內容,然后所粘貼的內容被全部轉換為文本。

一般情況下,如果僅使用一個textarea來實現ubb這樣的輸入,插入的表情不能立即顯示出來,這個體驗其實不好,但如果支持富文本輸入,則可能導致頁面因為節點的問題而發生錯位等等…所以要實現的功能就是,支持插入特定的表情和特定的節點,除此之外的所有的節點都將被替換/刪除掉。

 

朋友網前台頁面使用Seajs裝載jQuery來進行開發的,大概查看了一下編寫的代碼,寫的比較優雅。上面表情插入的聊天框只要監聽它的onpaste(低版本的opera監聽它的oninput)進行處理。

由用戶選擇的表情,將不會被替換掉。

 

下面有一張關於各瀏覽器字符輸入與剪貼板的事件的支持圖:

 

而關於Seajs,很早就看過,但從來沒用過,諸如CommonJS、RequireJS這些東東,都沒有太多精力去使用和研究過。不過在查看朋友網的部分JS代碼后,對於seajs就很好奇,想看看源碼寫幾個例子,權當學習一下JavaScript的模塊開發方式。在08年團隊開發頁面時,前台JavaScript會采用類似這樣的寫法:xx.include(‘…/dom.js’);xx.include([‘a.js’, ‘b.js’, …]);也算是有了JavaScript模塊化開發的雛形,但還不是很成熟。仔細查看了一下seajs的源碼,確實寫的挺贊的。

 

我在寫例子之前是參考過這篇文章的:《JavaScript模塊化開發庫之seajs》

Seajs適用於分模塊化開發的應用,比如類似QZone,點擊“裝扮空間”時需要動態加載CSS、JS,在資源加載完成后渲染裝扮,監聽事件、回調…使用seajs來開發的話會很方便,主體的邏輯結構也將十分清晰。

 

Seajs的世界里,一個文件就是一個模塊,而如果模塊過多,那文件同樣也將比較多,那隨之而來的JS請求也將變多。而如果你在一個頁面就定義了很多模塊,那么有幾個問題要注意…

 

假設直接在html頁面里定義了一個名為domx的模塊

   1: define('./domx', [], function(require, exports, module) {
   2:     var mod;
   3:  
   4:     return mod = {
   5:         'a' : function() {
   6:             alert('a');
   7:         },
   8:         'b' : function() {
   9:             alert('b');
  10:         }
  11:     };
  12: })

一個文件只能書寫一個模塊(可定義多個模塊),書寫多個模塊時,最后一個會替換之前的定義方法<在加載資源時發生>。

   1: define(function(require, exports, module) {
   2:     alert('start-1');
   3: });

如果html僅有上述代碼,上面的alert(‘start-1’)並不會執行,這個可以從源碼那里找到答案<詳見define函數的定義>:

   1: (function(util, data, fn) {
   2:  
   3:   /**
   4:    * Defines a module.
   5:    * @param {string=} id The module id.
   6:    * @param {Array.<string>|string=} deps The module dependencies.
   7:    * @param {function()|Object} factory The module factory function.
   8:    */
   9:   function define(id, deps, factory) {
  10:     var argsLen = arguments.length;
  11:  
  12:     // define(factory)
  13:     if (argsLen === 1) {
  14:       factory = id;
  15:       id = undefined;
  16:     }
  17:     // define(id || deps, factory)
  18:     else if (argsLen === 2) {
  19:       factory = deps;
  20:       deps = undefined;
  21:  
  22:       // define(deps, factory)
  23:       if (util.isArray(id)) {
  24:         deps = id;
  25:         id = undefined;
  26:       }
  27:     }
  28:  
  29:     // Parse dependencies
  30:     if (!util.isArray(deps) && util.isFunction(factory)) {
  31:       deps = parseDependencies(factory.toString());
  32:     }
  33:  
  34:     // Get url directly for specific modules.
  35:     if (id) {
  36:       var url = util.id2Uri(id);
  37:     }
  38:     // Try to derive url in IE6-9 for anonymous modules.
  39:     else if (document.attachEvent && !util.isOpera) {
  40:  
  41:       // Try to get the current script
  42:       var script = util.getCurrentScript();
  43:       if (script) {
  44:         url = util.unParseMap(util.getScriptAbsoluteSrc(script));
  45:       }
  46:  
  47:       if (!url) {
  48:         util.log('Failed to derive URL from interactive script for:',
  49:             factory.toString());
  50:  
  51:         // NOTE: If the id-deriving methods above is failed, then falls back
  52:         // to use onload event to get the url.
  53:       }
  54:     }
  55:  
  56:     var mod = new fn.Module(id, deps, factory);
  57:  
  58:     if (url) {
  59:       util.memoize(url, mod);
  60:       data.packageMods.push(mod);
  61:     }
  62:     else {
  63:       // Saves information for "memoizing" work in the onload event.
  64:       data.anonymousMod = mod;
  65:     }
  66:  
  67:   }
  68:  
  69:  
  70:   function parseDependencies(code) {
  71:     // Parse these `requires`:
  72:     //   var a = require('a');
  73:     //   someMethod(require('b'));
  74:     //   require('c');
  75:     //   ...
  76:     // Doesn't parse:
  77:     //   someInstance.require(...);
  78:     var pattern = /(?:^|[^.])\brequire\s*\(\s*(["'])([^"'\s\)]+)\1\s*\)/g;
  79:     var ret = [], match;
  80:  
  81:     code = removeComments(code);
  82:     while ((match = pattern.exec(code))) {
  83:       if (match[2]) {
  84:         ret.push(match[2]);
  85:       }
  86:     }
  87:  
  88:     return util.unique(ret);
  89:   }
  90:  
  91:  
  92:   // http://lifesinger.github.com/lab/2011/remove-comments-safely/
  93:   function removeComments(code) {
  94:     return code
  95:         .replace(/(?:^|\n|\r)\s*\/\*[\s\S]*?\*\/\s*(?:\r|\n|$)/g, '\n')
  96:         .replace(/(?:^|\n|\r)\s*\/\/.*(?:\r|\n|$)/g, '\n');
  97:   }
  98:  
  99:  
 100:   fn.define = define;
 101:  
 102: })(seajs._util, seajs._data, seajs._fn);

當書寫模塊define(fn);時,define的實參長度為1,即argsLen===1,而id等於undefined時,url的值也將為undefined,所以此時的這個匿名函數將會被綁定到seajs._data.anonymousMod ==> 一個fn.Module的實例({id:undefined, dependencies:[], factory:xxx});它是不會被執行的…

 

假設現在加載一個dom模塊==>seajs.use(‘dom’),它將會請求一個dom.js,如果dom.js未定義名為dom的模塊或是未使用define(fn)時,當文件被加載后,調用seajs在內部調用fetch方法時,之前那個mod(id為undefined)的id將會被替換為http://yourdomain.com/dom.js。代碼function(require, exports, module) { alert('start-1');}將綁定到dom模塊上,實際開發中defind(fn)這樣的代碼應當放在單獨的JS文件中,讓它作為模塊的定義方法。

 

Seajs的簡單使用示例:

1、定義模塊<在JS中定義>

   1: define('./dom', [], function(require, exports, module) {
   2:     var mod;
   3:  
   4:     return mod = {
   5:         'a' : function() {
   6:             alert('a');
   7:         },
   8:         'b' : function() {
   9:             alert('b');
  10:         }
  11:     };
  12: })

或者

   1:  
   2: define(function(require, exports, module) {//or define([], function(require, exports, module) {
   3:     
   4:     exports.a = function() {
   5:         alert('dom-a');
   6:     };
   7:  
   8:     exports.b = function() {
   9:         alert('dom-b');
  10:     };
  11:  
  12: });

 

2、使用模塊

   1: seajs.use('./dom', function(dom) {
   2:     dom.a();
   3: });

注:第一種方式,其它在調用的時候也會轉換為第二種方式。多個模塊調用如下

   1: define('./domx', [], function(require, exports, module) {
   2:     var mod;
   3:  
   4:     return mod = {
   5:         'a' : function() {
   6:             alert('a');
   7:         },
   8:         'b' : function() {
   9:             alert('b');
  10:         }
  11:     };
  12: });
  13:  
  14: define('./events', [], function(require, exports, module) {
  15:     var mod;
  16:  
  17:     return mod = {
  18:         'c' : function() {
  19:             alert('d');
  20:         },
  21:         'd' : function() {
  22:             alert('d');
  23:         }
  24:     };
  25: });
  26:  
  27: seajs.use(['./domx', './events'], function(domx, events) {
  28:     domx.a();
  29:     events.c();
  30: });

回調函數里的參數與依賴的模塊是一一對應的關系

 

Seajs內部實現了依賴關系,讓開發者可以將更多的精力放到應用的業務中去。假設test.html頁面引用了dom.js,而dom.js 依賴event.js 而event.js依賴cache.js,那么加載順序將是

dom.js –> event.js –> cache.js

image

而各模塊的執行順序是cache.js –> event.js –> dom.js

 

在dom下定義如下兩種寫法,差異較大。

方式一:

   1: define('./dom', function(require, exports, module) {
   2:     
   3:     var cache = require('event');
   4:  
   5:     exports.a = function() {
   6:         alert('dom-a');
   7:     };
   8:  
   9:     exports.b = function() {
  10:         alert('dom-b');
  11:     };
  12:  
  13:     alert('dom');
  14:  
  15: });
 
方式二:
   1: define('./dom', [], function(require, exports, module) {
   2:     
   3:     var cache = require('event');
   4:  
   5:     exports.a = function() {
   6:         alert('dom-a');
   7:     };
   8:  
   9:     exports.b = function() {
  10:         alert('dom-b');
  11:     };
  12:  
  13:     alert('dom');
  14:  
  15: });

使用方式二,event.js並不會被請求,而使用方式一,seajs會調用parseDependencies方法,解析出模塊的依賴關系。

   1: function parseDependencies(code) {
   2:   // Parse these `requires`:
   3:   //   var a = require('a');
   4:   //   someMethod(require('b'));
   5:   //   require('c');
   6:   //   ...
   7:   // Doesn't parse:
   8:   //   someInstance.require(...);
   9:   var pattern = /(?:^|[^.])\brequire\s*\(\s*(["'])([^"'\s\)]+)\1\s*\)/g;
  10:   var ret = [], match;
  11:  
  12:   code = removeComments(code);
  13:   while ((match = pattern.exec(code))) {
  14:     if (match[2]) {
  15:       ret.push(match[2]);
  16:     }
  17:   }
  18:  
  19:   return util.unique(ret);
  20: }

 

參考鏈接:

使用Seajs實現模塊化JavaScript開發>>

JavaScript模塊化開發庫之Seajs>>

Sea.js手冊與文檔>>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM