本篇文章主要介紹了深入理解requireJS-實作一個簡單的模組載入器,現在分享給大家,也給大家做個參考。
在前文中我們不只一次強調模組化程式設計的重要性,以及其可以解決的問題:
① 解決單一檔案變數命名衝突問題
② 解決前端多人協作問題
③ 解決檔案依賴問題
④ 按需載入(這個說法其實很假了)
⑤ ......
為了深入了解載入器,中間閱讀過一點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);
#
<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
事實上,上面的方案仍然是以檔案劃分,而不是以模組劃分的,若是檔案名稱發生變化,頁面會涉及到改變,其實這裡應該有一個路徑的映射處理這個問題var pathCfg = { 'companyReserve': 'companyReserve', 'incomeTax': 'incomeTax', 'performanceCoefficient': 'performanceCoefficient' };
companyReserve = requile('companyReserve');
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); });
require(depArr, callback)
實作方案
說到模組加載,人們第一反應都是ajax,因為無論何時,能拿到模組檔的內容,都是模組化的基本,但是採用ajax的方式是不行的,因為ajax有跨域的問題而模組化方案又不可避免的要處理跨域的問題,所以使用動態創建script標籤加載js文件便成為了首選,但是,不使用ajax的方案,對於實現難度來說還是有要求PS:我們實際工作中還會有加載html模板文件的場景,這個稍候再說通常我們是這樣做的,require作為程式入口,調度javascript資源,而載入到各個define模組後,各個模組便悄無聲息的創建script標籤加載加載結束後便往require模組佇列報告自己載入結束了,當require中多有依賴模組皆載入結束時,便執行其回呼#原理大致如此,剩下的只是具體實現,而後在論證這個理論是否可靠即可載入器閹割實作核心模組根據上述理論,我們由整體來說,首先以入口三個基本函數來說var require = function () { }; require.config = function () { }; require.define = function () { };
(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>
//util define([], function () { return { formatNum: function (n) { if (n < 10) return '0' + n; return n; } }; });
//math define(['num'], function (num) { return { getRadom: function () { return parseInt(Math.random() * num); } }; });
//math define(['num'], function (num) { return { getRadom: function () { return parseInt(Math.random() * num); } }; });
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
详细介绍在Vue2.0中v-for迭代语法的变化(详细教程)
以上是requireJS如何實作模組載入器?的詳細內容。更多資訊請關注PHP中文網其他相關文章!