继续这一系列的内容,到目前为止除了AMD规范中config的map、config参数外,我们已经全部支持其他属性了。这一篇文章中,我们来为增加对map的支持。同样问题,想要增加map的支持首先要知道map的语义。

  

  主要用于解决在两个不同模块集中使用一个模块的不同版本,并且保证两个模块集的交互没有冲突。

  

  假设磁盘有如下文件:

  

  当'some/newmodule'请求'foo'模块时,它将从foo1.2.js总得到'foo1.2'模块;当'some/oldmodule'请求'foo'模块时它将从foo1.0中得到'foo1.0'模块。

  在map属性中可以使用任何的module ID前缀,并且mapping对象可以匹配任何别的module ID前缀。

  

  如果出现通配符‘*’,表示任何模块使用这个匹配配置。通配符匹配对象中的模块ID前缀可以被覆盖。

  通过上文的解释,可以明白,如果在'some/newmodule'中依赖的foo实际是上依赖的foo1.2。转化成代码逻辑应当是这样的:如果在‘some/module’模块中发现依赖foo模块那就将foo替换成foo1.2。但是在什么地方实现替换好呢?因为模块的定义从define开始,同时只有在define中才能获得模块的绝对路径,所以我们把替换的处理放在define中。那么问题来了,我们的模块大部分都是匿名模块,模块自己如何知道自己的模块Id?所以一定要有一个标记去告诉define函数当前模块的Id,我们知道每一个模块都是一个JavaScript文件,每一个模块都有一个对应的script元素,所以最好的做法是没每一个script都加一个自定义特性,来标记当前元素的模块Id。

  所以在loadJs中要为script加自定义特性:

function loadJS(url, mId) {var script = document.createElement('script');script.setAttribute('data-moduleId', mId); //为script元素保留原始模块Idscript.type = "text/javascript";//判断模块是否在paths中定义了路径script.src = (url in global.require.parsedConfig.paths ? global.require.parsedConfig.paths[url] : url) + '.js';script.onload = function() {var module = modules[url];if (module && isReady(module) && loadings.indexOf(url) > -1) {callFactory(module);}checkDeps();};var head = document.getElementsByTagName('head')[0];head.appendChild(script);};

在define函数中,通过文件的绝对路径,找出对应的script元素,拿到模块Id,判断如果在map中,则进行替换:

global.define = function(id, deps, callback) {//加上moduleId的支持if (typeof id !== "string" && arguments.length === 2) {callback = deps;deps = id;id = "";}var id = id || getCurrentScript();var script = document.querySelector('script[src="' + id + '"]');if (script || id in require.parsedConfig.shim) {var mId = script ? script.getAttribute('data-moduleId') : id;var maping = getMapSetting(mId);if (maping) {deps = deps.map(function(dep) {return maping[dep] || dep;});}}if (modules[id]) {console.error('multiple define module: ' + id);}require(deps, callback, id);};
function getMapSetting(mId) {if (mId in require.parsedConfig.map) {return require.parsedConfig[mId];} else if ('*' in require.parsedConfig.map) {return require.parsedConfig.map['*'];} else {return null;}};

目前为止,我们的加载器已经支持了map属性,完整代码如下:

(function(global){global = global || window;modules = {};loadings = [];loadedJs = [];//module: id, state, factory, result, deps;global.require = function(deps, callback, parent){var id = parent || "Bodhi" + Date.now();var cn = 0, dn = deps.length;var args = [];var oriDeps = deps.slice();//保留原始dep的模块Id// dep为非绝对路径形式,而modules的key仍然需要绝对路径deps = deps.map(function(dep) {if (modules[dep]) { //jquery return dep;} else if (dep in global.require.parsedConfig.paths) {return dep;}var rel = "";if (/^Bodhi/.test(id)) {rel = global.require.parsedConfig.baseUrl;} else {var parts = parent.split('/');parts.pop();rel = parts.join('/');}return getModuleUrl(dep, rel);});var module = {id: id,deps: deps,factory: callback,state: 1,result: null};modules[id] = module;if (checkCircleRef(id, id)) {return;}deps.forEach(function(dep, i) {if (modules[dep] && modules[dep].state === 2) {cn++args.push(modules[dep].result);} else if (!(modules[dep] && modules[dep].state === 1) && loadedJs.indexOf(dep) === -1) {loadJS(dep, oriDeps[i]);loadedJs.push(dep);}});if (cn === dn) {callFactory(module);} else {loadings.push(id);checkDeps();}};global.require.config = function(config) {this.parsedConfig = {};if (config.baseUrl) {var currentUrl = getCurrentScript();var parts = currentUrl.split('/');parts.pop();var currentDir = parts.join('/');this.parsedConfig.baseUrl = getRoute(currentDir, config.baseUrl);}var burl = this.parsedConfig.baseUrl;// 得到baseUrl后,location相对baseUrl定位this.parsedConfig.packages = [];if (config.packages) {for (var i = 0, len = config.packages.length; i < len; i++) {var pck = config.packages[i];var cp = {name: pck.name,location: getRoute(burl, pck.location)}this.parsedConfig.packages.push(cp);}}this.parsedConfig.paths = {};if (config.paths) {for (var p in config.paths) {this.parsedConfig.paths[p] = /^http(s)?/.test(config.paths[p]) ? config.paths[p] : getRoute(burl, config.paths[p]);}}this.parsedConfig.map = {};if (config.map) {this.parsedConfig.map = config.map;}this.parsedConfig.shim = {};//shim 要放在最后处理if (config.shim) {this.parsedConfig.shim = config.shim;for (var p in config.shim) {var item = config.shim[p];define(p, item.deps, function() {var exports;if (item.init) {exports = item.init.apply(item, arguments);}return exports ? exports : item.exports;});}}console.log(this.parsedConfig);}global.define = function(id, deps, callback) {//加上moduleId的支持if (typeof id !== "string" && arguments.length === 2) {callback = deps;deps = id;id = "";}var id = id || getCurrentScript();var script = document.querySelector('script[src="' + id + '"]');if (script || id in require.parsedConfig.shim) {var mId = script ? script.getAttribute('data-moduleId') : id;var maping = getMapSetting(mId);if (maping) {deps = deps.map(function(dep) {return maping[dep] || dep;});}}if (modules[id]) {console.error('multiple define module: ' + id);}require(deps, callback, id);};global.define.amd = {};//AMD规范function getMapSetting(mId) {if (mId in require.parsedConfig.map) {return require.parsedConfig[mId];} else if ('*' in require.parsedConfig.map) {return require.parsedConfig.map['*'];} else {return null;}};function checkCircleRef(start, target){var m = modules[start];if (!m) {return false;}var depModules = m.deps.map(function(dep) {return modules[dep] || null;});return depModules.some(function(m) {if (!m) {return false;}return m.deps.some(function(dep) {var equal = dep === target;if (equal) {console.error("circle reference: ", target, m.id);}return equal;});}) ? true : depModules.some(function(m) {if (!m) {return false;}return m.deps.some(function(dep) {return checkCircleRef(dep, target);});});//return hasCr ? true: };function getRoute(base, target) {var bts = base.replace(/\/$/, "").split('/');  //base dirvar tts = target.split('/'); //target partswhile (isDefined(tts[0])) {if (tts[0] === '.') {return bts.join('/') + '/' + tts.slice(1).join('/');} else if (tts[0] === '..') {bts.pop();tts.shift();} else {return bts.join('/') + '/' + tts.join('/');}}};function isDefined(v) {return v !== null && v !== undefined;};function getModuleUrl(moduleId, relative) {function getPackage(nm) {for (var i = 0, len = require.parsedConfig.packages.length; i < len; i++) {var pck = require.parsedConfig.packages[i];if (nm === pck.name) {return pck;}}return false;}var mts = moduleId.split('/');var pck = getPackage(mts[0]);if (pck) {mts.shift();return getRoute(pck.location, mts.join('/'));} else if (mts[0] === '.' || mts[0] === '..') {return getRoute(relative, moduleId);} else {return getRoute(require.parsedConfig.baseUrl, moduleId);}};function loadJS(url, mId) {var script = document.createElement('script');script.setAttribute('data-moduleId', mId); //为script元素保留原始模块Idscript.type = "text/javascript";//判断模块是否在paths中定义了路径script.src = (url in global.require.parsedConfig.paths ? global.require.parsedConfig.paths[url] : url) + '.js';script.onload = function() {var module = modules[url];if (module && isReady(module) && loadings.indexOf(url) > -1) {callFactory(module);}checkDeps();};var head = document.getElementsByTagName('head')[0];head.appendChild(script);};function checkDeps() {for (var p in modules) {var module = modules[p];if (isReady(module) && loadings.indexOf(module.id) > -1) {callFactory(module);checkDeps(); // 如果成功,在执行一次,防止有些模块就差这次模块没有成功}}};function isReady(m) {var deps = m.deps;var allReady = deps.every(function(dep) {return modules[dep] && isReady(modules[dep]) && modules[dep].state === 2;})if (deps.length === 0 || allReady) {return true;}};function callFactory(m) {var args = [];for (var i = 0, len = m.deps.length; i < len; i++) {args.push(modules[m.deps[i]].result);}m.result = m.factory.apply(window, args);m.state = 2;var idx = loadings.indexOf(m.id);if (idx > -1) {loadings.splice(idx, 1);}};function getCurrentScript(base) {// 参考 https://github.com/samyk/jiagra/blob/master/jiagra.jsvar stack;try {a.b.c(); //强制报错,以便捕获e.stack} catch (e) { //safari的错误对象只有line,sourceId,sourceURLstack = e.stack;if (!stack && window.opera) {//opera 9没有e.stack,但有e.Backtrace,但不能直接取得,需要对e对象转字符串进行抽取stack = (String(e).match(/of linked script \S+/g) || []).join(" ");}}if (stack) {/**e.stack最后一行在所有支持的浏览器大致如下:*chrome23:* at http://113.93.50.63/data.js:4:1*firefox17:*@http://113.93.50.63/query.js:4*opera12:http://www.oldapps.com/opera.php?system=Windows_XP*@http://113.93.50.63/data.js:4*IE10:*  at Global code (http://113.93.50.63/data.js:4:1)*  //firefox4+ 可以用document.currentScript*/stack = stack.split(/[@ ]/g).pop(); //取得最后一行,最后一个空格或@之后的部分stack = stack[0] === "(" ? stack.slice(1, -1) : stack.replace(/\s/, ""); //去掉换行符return stack.replace(/(:\d+)?:\d+$/i, "").replace(/\.js$/, ""); //去掉行号与或许存在的出错字符起始位置}var nodes = (base ? document : head).getElementsByTagName("script"); //只在head标签中寻找for (var i = nodes.length, node; node = nodes[--i]; ) {if ((base || node.className === moduleClass) && node.readyState === "interactive") {return node.className = node.src;}}};
})(window)

 

  下面我们看一个demo:

  使用我们的加载器来加载jquery,同时禁用jquery的全局模式$:

window.something = "Bodhi";require.config({baseUrl: "./",packages: [{name: "more",location: "./more"}, {name: "mass",location: "../"}, {name: "wab",location: "../../../"}],shim: {"something": {"deps": ['jquery'],exports: 'something',init: function(jq, ol) {console.log(jq);console.log($);return something + " in shim";}}},map: {'*': {'jquery': 'jquery-private'},'jquery-private': {'jquery': 'jquery'}},paths: {'jquery': "../../Bodhi/src/roots/jquery"}});require(['bbb',//'aaa.bbb.ccc',//'ccc',//'ddd',//'fff','something'], function(aaabbbccc){console.log('simple loader');console.log(arguments);});

jquery-private代码如下:

define(['jquery'], function(jquery) {return jquery.noConflict(true);
});

如果某一模块依赖jquery,那么将会加载jquery-private。而在jquery中,因为配置了map和paths,所以jquery-private中的jquery根据paths找到jquery文件,并加载。同时在jquery-private中将禁用了全局模式之后的jquery对象返回给something模块。通过这个配置所有的模块在引用jquery时,实际上是引用了jquery-private模块。

AMD加载器实现笔记(四)相关推荐

  1. AngularJs2与AMD加载器(dojo requirejs)集成

    现在是西太平洋时间凌晨,这个问题我鼓捣了一天,都没时间学英语了,英语太差,相信第二天我也看不懂了,直接看结果就行. 核心原理就是require在AngularJs2编译过程中是关键字,而在浏览器里面运 ...

  2. amba simple class驱动_学习笔记:class加载器和双亲委派模型

    类加载器 类加载器有四种 启动类加载器(Bootstrap ClassLoader) 负责加载 JAVA_HOMElib ⽬录中的,或通过-Xbootclasspath参数指定路径中的且被虚拟机认可( ...

  3. 程序的加载和执行(四)——《x86汇编语言:从实模式到保护模式》读书笔记24

    程序的加载和执行(四)--<x86汇编语言:从实模式到保护模式>读书笔记24 通过本文能学到什么? 怎样跳转到用户程序 用户程序通过调用内核过程完成自己的功能 怎样从用户程序返回到内核 接 ...

  4. 【OS学习笔记】十 实模式:实现一个程序加载器-程序加载器如何将用户程序加载到内存并执行

    上一篇文章学习了以下内容: 用一种不同的分段方法,从另一个不同的的角度理解处理器的分段内存访问机制 使用循环和条件转移指令来优化主引导扇区代码 点击链接查看上一篇文章:点击链接查看 对于主引导扇区部分 ...

  5. JVM学习笔记-03-类加载器及双亲委派机制

    JVM学习笔记-03-类加载器及双亲委派机制 文章目录 JVM学习笔记-03-类加载器及双亲委派机制 1. 类加载器 视频链接-最新JVM教程IDEA版[Java面试速补篇]-03-类加载器及双亲委派 ...

  6. 020-JVM-类加载器的四个层级-ClassLoader

    上一篇:019-JVM-类的加载过程 https://yuhongliang.blog.csdn.net/article/details/111499604 1.package sun.misc.La ...

  7. java学习笔记-类的加载器

    目录 第一节 概述 1.类加载的分类 2. 类加载器的必要性 3. 命名空间 4. 类加载机制的基本特征 第二节 类的加载器分类 概述 1. 引导类加载器 2. 扩展类加载器 3. 系统类加载器 4. ...

  8. contiki学习笔记(六)contiki程序加载器和多线程库

    六.contiki程序加载器 contiki程序加载器是一个用于加载和启动程序的抽象接口. Data Structures struct dsc//DSC程序描述结构. ModulesThe Cont ...

  9. 网络安全进阶篇之免杀(十四章-7)C#加载器过360

    一. 概念 1.1 360 安全卫士和 360 杀毒 360 杀毒是 360 安全中心出品的一款免费的云安全杀毒软件.它创新性地整合了五大领先查杀引擎,包括国际知名的 BitDefender 病毒查杀 ...

最新文章

  1. Cadence入门笔记(1):创建元件库的基本操作!
  2. android中的常用方法,android开发中常用方法总结
  3. SAP OData Batch request processing in backend
  4. 使用onclick跳转到其他页面。使用button跳转到指定url
  5. dede首页调用会员积分和头像代码
  6. safari windows版本_iPhone 和 Windows 协作的五个技巧
  7. android黑科技系列——手机端破解神器MT的内购VIP功能破解教程
  8. android视频播放器课程设计,ANDROID视频播放器课程设计.pdf
  9. 【JZOJ】 【NOIP2014】【模拟试题】保镖排队
  10. Console 接口介绍及电路设计
  11. 卷积神经网络:一个模块化视角
  12. 【UE4】给游戏制作一个简易的小地图
  13. pthread_cond_init 初始化条件变量使用
  14. 【目标检测】SSD(Single Shot MultiBox Detector)的复现
  15. mes系统和plc通讯案例_MES与PLC实时通信系统研究
  16. Collection集合家族
  17. 老祖宗的老话大全收藏,每个人都该看一看
  18. echarts 盒须图配置
  19. 【Java】求1到100以内的素数(质数)
  20. Amazone S3 生成公网链接

热门文章

  1. 选择Data类型在Access与Sql中的区别
  2. 解决 macOS 下HTTP请求访问本地服务被阻塞5秒钟的问题
  3. 【LeetCode-面试算法经典-Java实现】【109-Convert Sorted List to Binary Search Tree(排序链表转换成二叉排序树)】...
  4. HttpConnectionUtil
  5. HoloLens开发手记 - 手势输入 Gesture input
  6. mysql的limit使用方法
  7. 【Git入门之十二】DIY Git
  8. DHCP 服务大团圆
  9. 微型计算机原理 考试试题,微机原理期末考试试卷(有答案)
  10. service和thread的区别,何时用service,何时用thread?