AMD加载器实现笔记(五)
笔记 实现 加载 AMD
2023-09-14 08:58:19 时间
前几篇文章对AMD规范中的config属性几乎全部支持了,这一节主要是进一步完善。到目前为止我们的加载器还无法处理环形依赖的问题,这一节就是解决环形依赖。
所谓环形依赖,指的是模块A的所有依赖项的依赖中有没有依赖A模块本身的模块。如果有那就说明存在环形依赖。所以检验的方式是利用递归,检查一个模块的依赖的依赖项中有没有依赖A模块,以及依赖项的依赖项的依赖项中有没有A模块,核心代码如下:
function checkCircleRef(start, target){ var m = modules[start]; if (!m) { return false; var depModules = m.deps.map(function(dep) { return modules[dep] || null;
剩下的问题是我们把检查放到哪里去?我们的模块最先在require中注册,所以最佳的位置是放在require函数中去检查:
//require 函数 //。。。。。。。 var module = { id: id, deps: deps, factory: callback, state: 1, result: null modules[id] = module; if (checkCircleRef(id, id)) { hasCircleReferece = true; return; // ...................... //....................... //......................下一个要面临的问题就是:如果存在依赖项如何处理?对此,我的做法是如果存在环形依赖,结束整个加载过程。我们在加载器内部使用一个哨兵变量,一旦存在环形依赖,停止所有工作。如:loadJs中:
script.onload = function() { if (hasCircleReferece) { return; var module = modules[url]; if (module isReady(module) loadings.indexOf(url) -1) { callFactory(module); checkDeps(); };如define函数中:
if (modules[id]) { console.error(multiple define module: + id); if (!hasCircleReferece) { require(deps, callback, id); }测试:
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); });bbb中:
define(["aaa", "ccc"],function(a, c){ console.log("已加载bbb模块", 7) return { aaa: a, ccc: c.ccc, bbb: "bbb" })aaa中:
define([bbb], function(){ console.log("已加载aaa模块", 7) return "aaa" });测试结果:
circle reference: simpleAMDLoader/aaa simpleAMDLoader/bbb
目前为止整个加载器代码如下:
(function(global){ global = global || window; var modules = {}; var loadings = []; var loadedJs = []; var hasCircleReferece = false; //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)) { hasCircleReferece = true; return; //checkCircleRef(id, id) 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);
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 mId = getModuleId(id); if (mId || id in require.parsedConfig.shim) { mId = mId ? mId : 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); if (!hasCircleReferece) { require(deps, callback, id); global.define.amd = {};//AMD规范 function getModuleId(url) { var script = document.querySelector(script[src=" + url + "]); if (script) { return script.getAttribute(data-moduleId); } else { return null; 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;
function getRoute(base, target) { var bts = base.replace(/\/$/, "").split(/); //base dir var tts = target.split(/); //target parts while (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元素保留原始模块Id script.type = "text/javascript"; //判断模块是否在paths中定义了路径 script.src = (url in global.require.parsedConfig.paths ? global.require.parsedConfig.paths[url] : url) + .js; script.onload = function() { if (hasCircleReferece) { return; 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.js var stack; try { a.b.c(); //强制报错,以便捕获e.stack } catch (e) { //safari的错误对象只有line,sourceId,sourceURL stack = 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)
linux加载动态库.so的3种方法 我了解的加载动态库,一共是三种方法。分别是将库放到/usr/lib64下;修改/etc/ld.so.conf以及在/etc/ld.so.conf.d下添加conf文件,将路径包装到该文件中。root下使用ldconfig命令加载生效,使用ldconfig -v|grep xxx进行查看是否生效。
JVM笔记11-类加载器和OSGI 一.JVM 类加载器: 一个类在使用前,如何通过类调用静态字段,静态方法,或者new一个实例对象,第一步就是需要类加载,然后是连接和初始化,最后才能使用。 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialzation)、使用(Using)和卸载(Unloading)7 个阶段。
相关文章
- 【笔记】Java 调用 COM 组件之 com4j 使用说明
- Linux — Makefile的学习笔记以及多级目录下Makefile的编写
- Lucene笔记15-Lucene的分词-通过TokenStream显示分词[通俗易懂]
- CSS(初级)笔记
- JVM 学习笔记(3):HotSpot 算法实现的细节
- SEM学习笔记——推广策略后期修改计划
- 深入理解计算机系统 第二章 笔记
- 关于Kubernetes中kube-apiserver使用token、kubeconfig认证的一些笔记
- Vuejs 设计与实现笔记(一)
- 全网最详细笔记:张益唐北大讲解火热出炉!本质上已证明「零点猜想」
- rust 入门笔记:使用rust实现双向链表、二叉树
- Python笔记之自动登录实现
- 关于javaIO一些简单的笔记
- Vue.js 滑动拼图验证码实现笔记
- 协同文档:OT与CRDT实现协同编辑笔记
- java学习笔记06–正则表达式详解编程语言
- Linux防火墙iptables学习笔记(四)iptables实现NAT
- Class与ID区别margin和padding区别CSS学习笔记
- Android开发笔记之:在ImageView上绘制圆环的实现方法
- JavascriptObject对象学习笔记