国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

深入理解requireJS-實現一個簡單的模塊加載器

2019-11-19 14:31:31
字體:
來源:轉載
供稿:網友

在前文中我們不止一次強調過模塊化編程的重要性,以及其可以解決的問題:

① 解決單文件變量命名沖突問題

② 解決前端多人協作問題

③ 解決文件依賴問題

④ 按需加載(這個說法其實很假了)

⑤ ......

為了深入了解加載器,中間閱讀過一點requireJS的源碼,但對于很多同學來說,對加載器的實現依舊不太清楚

事實上不通過代碼實現,單單憑閱讀想理解一個庫或者框架只能達到一知半解的地步,所以今天便來實現一個簡單的加載器

加載器原理分析

分與合

事實上,一個程序運行需要完整的模塊,以下代碼為例:

//求得績效系數 var performanceCoefficient = function () {  return 0.2; }; //住房公積金計算方式 var companyReserve = function (salary) {  return salary * 0.2; }; //個人所得稅 var incomeTax = function (salary) {  return salary * 0.2; }; //基本工資 var salary = 1000; //最終工資 var mySalary = salary + salary * performanceCoefficient(); mySalary = mySalary - companyReserve(mySalary) - incomeTax(mySalary - companyReserve(mySalary)); console.log(mySalary);

我一份完整的工資來說,公司會有績效獎勵,但是其算法可能非常復雜,其中可能涉及到出勤率,完成度什么的,這里暫時不管

而有增便有減,所以我們會交住房公積金,也會扣除個人所得稅,最終才是我的工資

對于完整的程序來說上面的流程缺一不可,但是各個函數中卻有可能異常的復雜,跟錢有關系的東西都復雜,所以單單是公司績效便有可能超過1000行代碼

于是我們這邊便會開始分:

<script src="companyReserve.js" type="text/javascript"></script><script src="incomeTax.js" type="text/javascript"></script><script src="performanceCoefficient.js" type="text/javascript"></script><script type="text/javascript"> //基本工資 var salary = 1000; //最終工資 var mySalary = salary + salary * performanceCoefficient(); mySalary = mySalary - companyReserve(mySalary) - incomeTax(mySalary - companyReserve(mySalary)); console.log(mySalary);</script>

上面的代碼表明上是“分”開了,事實上也造成了“合”的問題,我要如何才能很好的把它們重新合到一起呢,畢竟其中的文件可能還涉及到依賴,這里便進入我們的require與define

require與define

事實上,上面的方案仍然是以文件劃分,而不是以模塊劃分的,若是文件名發生變化,頁面會涉及到改變,其實這里應該有一個路徑的映射處理這個問題

var pathCfg = { 'companyReserve': 'companyReserve', 'incomeTax': 'incomeTax', 'performanceCoefficient': 'performanceCoefficient'};

于是我們一個模塊便對應了一個路徑js文件,剩下的便是將之對應模塊的加載了,因為前端模塊涉及到請求。所以這種寫法:

companyReserve = requile('companyReserve');

對于前端來說是不適用的,就算你在哪里看到這樣做了,也一定是其中做了一些“手腳”,這里我們便需要依據AMD規范了:

require.config({ 'companyReserve': 'companyReserve', 'incomeTax': 'incomeTax', 'performanceCoefficient': 'performanceCoefficient'});require(['companyReserve', 'incomeTax', 'performanceCoefficient'], function (companyReserve, incomeTax, performanceCoefficient) { //基本工資 var salary = 1000; //最終工資 var mySalary = salary + salary * performanceCoefficient(); mySalary = mySalary - companyReserve(mySalary) - incomeTax(mySalary - companyReserve(mySalary)); console.log(mySalary);});

這里便是一個標準的requireJS的寫法了,首先定義模塊以及其路徑映射,其中定義依賴項

require(depArr, callback)

一個簡單完整的模塊加載器基本就是這個樣子了,首先是一個依賴的數組,其次是一個回調,回調要求依賴項全部加載才能運行,并且回調的參數便是依賴項執行的結果,所以一般要求define模塊具有一個返回值

方案有了,那么如何實現呢?

實現方案

說到模塊加載,人們第一反應都是ajax,因為無論何時,能拿到模塊文件的內容,都是模塊化的基本,但是采用ajax的方式是不行的,因為ajax有跨域的問題

而模塊化方案又不可避免的要處理跨域的問題,所以使用動態創建script標簽加載js文件便成為了首選,但是,不使用ajax的方案,對于實現難度來說還是有要求

PS:我們實際工作中還會有加載html模板文件的場景,這個稍候再說

通常我們是這樣做的,require作為程序入口,調度javascript資源,而加載到各個define模塊后,各個模塊便悄無聲息的創建script標簽加載

加載結束后便往require模塊隊列報告自己加載結束了,當require中多有依賴模塊皆加載結束時,便執行其回調

原理大致如此,剩下的只是具體實現,而后在論證這個理論是否靠譜即可

加載器閹割實現

核心模塊

根據以上理論,我們由整體來說,首先以入口三個基本函數來說

var require = function () {};require.config = function () {};require.define = function () {};

這三個模塊比不可少:

① config用以配置模塊與路徑的映射,或者還有其他用處

② require為程序入口

③ define設計各個模塊,響應require的調度

然后我們這里會有一個創建script標簽的方法,并且會監聽其onLoad事件

④ loadScript

其次我們加載script標簽后,應該有一個全局的模塊對象,用于存儲已經加載好的模塊,于是這里提出了兩個需求:

⑤ require.moduleObj 模塊存儲對象

⑥ Module,模塊的構造函數

有了以上核心模塊,我們形成了如下代碼:

(function () { var Module = function () {  this.status = 'loading'; //只具有loading與loaded兩個狀態  this.depCount = 0; //模塊依賴項  this.value = null; //define函數回調執行的返回 }; var loadScript = function (url, callback) { }; var config = function () { }; var require = function (deps, callback) { }; require.config = function (cfg) { }; var define = function (deps, callback) { };})();

于是接下來便是具體實現,然后在實現過程中補足不具備的接口與細節,往往在最后的實現與最初的設計沒有半毛錢關系......

代碼實現

這塊最初實現時,本來想直接參考requireJS的實現,但是我們老大笑瞇瞇的拿出了一個他寫的加載器,我一看不得不承認有點妖

于是這里便借鑒了其實現,做了簡單改造:

(function () { //存儲已經加載好的模塊 var moduleCache = {}; var require = function (deps, callback) {  var params = [];  var depCount = 0;  var i, len, isEmpty = false, modName;  //獲取當前正在執行的js代碼段,這個在onLoad事件之前執行  modName = document.currentScript && document.currentScript.id || 'REQUIRE_MAIN';  //簡單實現,這里未做參數檢查,只考慮數組的情況  if (deps.length) {   for (i = 0, len = deps.length; i < len; i++) {    (function (i) {     //依賴加一     depCount++;     //這塊回調很關鍵     loadMod(deps[i], function (param) {      params[i] = param;      depCount--;      if (depCount == 0) {       saveModule(modName, params, callback);      }     });    })(i);   }  } else {   isEmpty = true;  }  if (isEmpty) {   setTimeout(function () {    saveModule(modName, null, callback);   }, 0);  } }; //考慮最簡單邏輯即可 var _getPathUrl = function (modName) {  var url = modName;  //不嚴謹  if (url.indexOf('.js') == -1) url = url + '.js';  return url; }; //模塊加載 var loadMod = function (modName, callback) {  var url = _getPathUrl(modName), fs, mod;  //如果該模塊已經被加載  if (moduleCache[modName]) {   mod = moduleCache[modName];   if (mod.status == 'loaded') {    setTimeout(callback(this.params), 0);   } else {    //如果未到加載狀態直接往onLoad插入值,在依賴項加載好后會解除依賴    mod.onload.push(callback);   }  } else {   /*   這里重點說一下Module對象   status代表模塊狀態   onLoad事實上對應requireJS的事件回調,該模塊被引用多少次變化執行多少次回調,通知被依賴項解除依賴   */   mod = moduleCache[modName] = {    modName: modName,    status: 'loading',    export: null,    onload: [callback]   };   _script = document.createElement('script');   _script.id = modName;   _script.type = 'text/javascript';   _script.charset = 'utf-8';   _script.async = true;   _script.src = url;   //這段代碼在這個場景中意義不大,注釋了   //   _script.onload = function (e) {};   fs = document.getElementsByTagName('script')[0];   fs.parentNode.insertBefore(_script, fs);  } }; var saveModule = function (modName, params, callback) {  var mod, fn;  if (moduleCache.hasOwnProperty(modName)) {   mod = moduleCache[modName];   mod.status = 'loaded';   //輸出項   mod.export = callback ? callback(params) : null;   //解除父類依賴,這里事實上使用事件監聽較好   while (fn = mod.onload.shift()) {    fn(mod.export);   }  } else {   callback && callback.apply(window, params);  } }; window.require = require; window.define = require;})();

首先這段代碼有一些問題:

沒有處理參數問題,字符串之類皆未處理

未處理循環依賴問題

未處理CMD寫法

未處理html模板加載相關

未處理參數配置,baseUrl什么都沒有搞

基于此想實現打包文件也不可能

......

但就是這100行代碼,便是加載器的核心,代碼很短,對各位理解加載器很有幫助,里面有兩點需要注意:

① requireJS是使用事件監聽處理本身依賴,這里直接將之放到了onLoad數組中了

② 這里有一個很有意思的東西

document.currentScript

這個可以獲取當前執行的代碼段

requireJS是在onLoad中處理各個模塊的,這里就用了一個不一樣的實現,每個js文件加載后,都會執行require(define)方法

執行后便取到當前正在執行的文件,并且取到文件名加載之,正因為如此,連script的onLoad事件都省了......

demo實現

<html xmlns="http://www.w3.org/1999/xhtml"><head> <title></title></head><body></body><script src="require.js" type="text/javascript"></script><script type="text/javascript"> require(['util', 'math', 'num'], function (util, math, num) {  num = math.getRadom() + '_' + num;  num = util.formatNum(num);  console.log(num); });</script></html>
//utildefine([], function () { return {  formatNum: function (n) {   if (n < 10) return '0' + n;   return n;  } };});
//mathdefine(['num'], function (num) { return {  getRadom: function () {   return parseInt(Math.random() * num);  } };});
//mathdefine(['num'], function (num) { return {  getRadom: function () {   return parseInt(Math.random() * num);  } };});

小結

今天我們實現了一個簡單的模塊加載器,通過他希望可以幫助各位了解requireJS或者seaJS,最后順利進入模塊化編程的行列

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 利辛县| 西安市| 沁阳市| 宜宾市| 义马市| 新巴尔虎右旗| 高淳县| 清涧县| 库车县| 宁河县| 武山县| 宿迁市| 克东县| 临沂市| 清水县| 于都县| 正镶白旗| 荃湾区| 清河县| 连平县| 阳泉市| 荔波县| 巴林右旗| 秦安县| 林周县| 西贡区| 靖安县| 资源县| 内乡县| 双城市| 宁国市| 神农架林区| 类乌齐县| 张掖市| 东方市| 长兴县| 肥城市| 南川市| 本溪| 廊坊市| 文成县|