操刀 requirejs,自己動手寫一個


這篇文章講的是,我怎么去寫一個 requirejs 。

去 github 上fork一下,順便star~

requirejs,眾所周知,是一個非常出名的js模塊化工具,可以讓你使用模塊化的方式組織代碼,並異步加載你所需要的部分。balabala 等等好處不計其數。

之所以寫這篇文章,是做一個總結。目前打算動一動,換一份工作。感謝 一線碼農 大大幫忙推了攜程,得到了面試的機會。

面試的時候,聊着聊着感覺問題都問在了自己的“點”上,應答都挺順利,於是就慢慢膨脹了。在說到模塊化的時候,我腦子一抽,憑着感覺說了一下requirejs實現的大概步驟,充滿了表現欲望,廢話一堆。僥幸不可能當場讓我寫一遍,算是過了,事后嘗試了一下,在這里跟大家分享一下我的實現。

上面是我划分的項目結構:

  1. tool,工具模塊,存放便捷方法,很多地方需要用到。
  2. async,異步處理模塊,主要實現了 promisedeferred 。邏輯上的異步。
  3. requirejs -> loader ,amd加載器,處理模塊的依賴和異步加載。物理上的異步。

因為對於異步流程控制方面,研究過一段時間,所以這里第一時間想到的就是 promise ,如果用這個來做,所有的模塊放入字典,路徑做key,promise做value,所有依賴都結束之后,才進行下一步操作。 不用管復雜的依賴關系,把邏輯盡量簡單化:

  1. 首先有一個字典,存放所有的模塊。key放地址,value放promise,promise在模塊加載完畢的時候resolve。
  2. 如果依賴某個模塊,先根據路徑從字典找key,存在就用該promise,不存在就去加載該模塊並放入字典,並使用該模塊的promise。
  3. 所有的模塊,我只用它的 promise ,在它的回調中寫我的后續操作。它的resolve應該單獨抽離出來,屬於異步加載方面。

大致思路有了,當然實際寫的時候肯定困難重重,不過沒關系,遇到問題再去解決。

考慮到代碼的簡易性,以及我的個人習慣。我打算用類似於 jquery 的 $.Deferred() 和它的promise,與es6的promise有一定的出入。這樣代碼書寫更簡易,並且邏輯上更清晰,es6的promise用起來確實稍顯麻煩。我需要的是一個 pub/sub 模式,一個地方觸發,多個回調執行的並行方式,es6的promise,需要在then中一次次返回,並且resolve起來也不方便,最最主要的是需要 polyfill 一下,而我想自己寫,寫我熟悉且喜歡的代碼 。

回調模塊 callbacks,熟悉jquery的朋友接下來可能會覺得使用方式很熟悉,沒錯,我受jq的影響算是比較深的。以前在學習jq源碼的時候,就覺得這個很好用,你可以從我的代碼里面看到jq的影子 :

 1 import _ from '../tool/tool';
 2 
 3 /**
 4  * 基礎回調模塊
 5  * 
 6  * @export
 7  * @returns callbacks
 8  */
 9 export default function () {
10     let list = [],
11         _args = (arguments[0] || '').split(' '),           // 參數數組
12         fireState = 0,                                     // 觸發狀態  0-未觸發過 1-觸發中  2-觸發完畢
13         stopOnFalse = ~_args.indexOf('stopOnFalse'),       // stopOnFalse - 如果返回false就停止
14         once = ~_args.indexOf('once'),                     // once - 只執行一次,即執行完畢就清空
15         memory = ~_args.indexOf('memory') ? [] : null,     // memory - 保持狀態
16         fireArgs = [];                                     // fire 參數
17 
18     /**
19      * 添加回調函數
20      * 
21      * @param {any} cb
22      * @returns callbacks
23      */
24     function add(cb) {
25         if (memory && fireState == 2) {  // 如果是memory模式,並且已經觸發過
26             cb.apply(null, fireArgs);
27         }
28 
29         if (disabled()) return this;      // 如果被disabled
30 
31         list.push(cb);
32         return this;
33     }
34 
35     /**
36      * 觸發
37      * 
38      * @param {any} 任意參數
39      * @returns callbacks
40      */
41     function fire() {
42         if (disabled()) return this; // 如果被禁用
43 
44         fireArgs = _.makeArray(arguments); // 保存 fire 參數
45 
46         fireState = 1; // 觸發中 
47 
48         _.each(list, (index, cb) => { // 依次觸發回調
49             if (cb.apply(null, fireArgs) === false && stopOnFalse) { // stopOnFalse 模式下,遇到false會停止觸發
50                 return false;
51             }
52         });
53 
54         fireState = 2; // 觸發結束
55 
56         if (once) disable(); // 一次性列表
57 
58         return this;
59     }
60 
61     function disable() {    // 禁止
62         list = undefined;
63         return this;
64     }
65 
66     function disabled() {  // 獲取是否被禁止
67         return !list;
68     }
69 
70     return {
71         add: add,
72         fire: fire,
73         disable: disable,
74         disabled: disabled
75     };
76 }
View Code

 

這是一個工廠方法,每次所需的對象由該方法生成,用閉包來隱藏局部變量,私有方法。而最后暴露(發布)出來的對象,用 pub/sub 模式,提供了 訂閱觸發禁用查看禁用 4個方法。 這里要說的是 ,提供了3個參數:stopOnFalseoncememory。觸發的時候,按照訂閱順序依次觸發,如果是 stopOnFalse 模式,當某個訂閱的函數,返回是 false 的時候,停止整個觸發過程。 如果是 once ,表示每個函數只能執行一次,在執行過后,會被移除隊列。而 memory 狀態下,在 callback 觸發后,會被保持狀態,之后添加的方法,添加后會直接執行。

這三種模式,傳參的時候直接傳入字符串,可以隨意組合,用空格分開,比如:callbacks('once memory')

該模塊用於整個項目中,處理所有的回調。使用方式類似於jquery的:$.Callbacks(...)

 

寫到這里,就已經結束了。本文講了對於requirejs,我的實現思路,列舉了可能遇到的問題,及我的解決方式。希望能給大家的學習提供點幫助。

去 github 上fork一下,順便star~

上面是github的地址,求star啊,作為一個虛榮的人,我對這個很看重的,哈哈,也就這點追求了。再次感激 一線碼農 大哥的推薦,還有 linkFly 的經驗指導。


免責聲明!

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



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