// define用以预定义模块,调用define方法时未完成js文件的加载,页面用script标签显示加载除外 // require方法直接加载js文件,同时完成加载define方法添加的父级依赖 // 模块的依赖通过Module.enable调用load或callPlugin完成加载,首先需要把相应模块推送到待加载对象registry中 // 该过程通过completeLoad调用takeGlobalQueue完成,将模块推送到待加载对象registry中 var requirejs, require, define; (function (global) { var req, s, head, baseElement, dataMain, src, interactiveScript, currentlyAddingScript, mainScript, subPath, version = '2.1.22', commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,// ^在[]内部代表非,外部代表起始 cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,// $1模块名 jsSuffixRegExp = /\.js$/,// js后缀 currDirRegExp = /^\.\//,// 匹配当前目录 op = Object.prototype, // typeof监测出为object的情况下,再用Object.prototype.toString再区分出是数组还是函数(还要剔除对象是正则对象的可能,instanceof RegRex判断) ostring = op.toString, hasOwn = op.hasOwnProperty, ap = Array.prototype, isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document), isWebWorker = !isBrowser && typeof importScripts !== 'undefined',// Web Worker用来载入运行在后台的js文件。不知importScripts的意义??? //PS3 indicates loaded and complete, but need to wait for complete //specifically. Sequence is 'loading', 'loaded', execution, // then 'complete'. The UA check is unfortunate, but not sure how //to feature test w/o causing perf issues. readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ? /^complete$/ : /^(complete|loaded)$/,// 指playstation 3操作系统需要加载完成执行html页面解析??? defContextName = '_',// 默认上下文 //Oh the tragedy, detecting opera. See the usage of isOpera for reason. isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]', contexts = {}, cfg = {}, globalDefQueue = [],// 存储define方法添加的模块,模块以数组形式存储name,deps,callback useInteractive = false; function isFunction(it) { return ostring.call(it) === '[object Function]'; } function isArray(it) { return ostring.call(it) === '[object Array]'; } // each遍历数组,有值时跳出循环体 function each(ary, func) { if (ary) { var i; for (i = 0; i < ary.length; i += 1) { if (ary[i] && func(ary[i], i, ary)) { break; } } } } // eachReverse反向遍历数组 function eachReverse(ary, func) { if (ary) { var i; for (i = ary.length - 1; i > -1; i -= 1) { if (ary[i] && func(ary[i], i, ary)) { break; } } } } // hasProp判断属性是否为自有属性, function hasProp(obj, prop) { return hasOwn.call(obj, prop); } // getOwn提取自有属性的值 function getOwn(obj, prop) { return hasProp(obj, prop) && obj[prop]; } // eachProp遍历对象的自有属性,值和键作为参数传入函数中,当函数返回真值时终止遍历(当函数没有返回值为否) function eachProp(obj, func) { var prop; for (prop in obj) { if (hasProp(obj, prop)) { if (func(obj[prop], prop)) { break; } } } } // mixin合并对象,支持深拷贝以及同名属性强制拷贝,target、source对象,force同名属性强制合并,deepStringMixin深拷贝 function mixin(target, source, force, deepStringMixin) { if (source) { eachProp(source, function (value, prop) { if (force || !hasProp(target, prop)) { if (deepStringMixin && typeof value === 'object' && value && !isArray(value) && !isFunction(value) && !(value instanceof RegExp)) { if (!target[prop]) { target[prop] = {}; } mixin(target[prop], value, force, deepStringMixin); } else { target[prop] = value; } } }); } return target; } // bind使object对象调用函数或方法,同时改变this关键字的指向为object function bind(obj, fn) { return function () { return fn.apply(obj, arguments);// 此时arguments的用途就是使this关键字指向object }; } // scripts获取页面中的scripts元素节点 function scripts() { return document.getElementsByTagName('script'); } // req.onError中调用,简单地抛出错误 function defaultOnError(err) { throw err; } // getGlobal(value)获取window对象的属性或方法,支持带.的value值 function getGlobal(value) { if (!value) { return value; } var g = global; each(value.split('.'), function (part) { g = g[part]; }); return g; } // makeError创建错误对象,错误对象中添加requireType、requireModules、originalError属性 function makeError(id, msg, err, requireModules) { var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id); e.requireType = id; e.requireModules = requireModules; if (err) { e.originalError = err; } return e; } // 已经使用了另一个AMD模块化加载器,则不予加载requirejs插件; if (typeof define !== 'undefined') { return; } // requirejs变量名已被使用,若非函数,cfg暂存该内容,require.js加载完成后重新赋值; // 若为函数,不予加载requirejs插件; if (typeof requirejs !== 'undefined') { if (isFunction(requirejs)) { return; } cfg = requirejs; requirejs = undefined; } // 因何不要作require是函数时返回、阻止require.js加载的处理??? //Allow for a require config object if (typeof require !== 'undefined' && !isFunction(require)) { //assume it is a config object. cfg = require; require = undefined; } function newContext(contextName) { var inCheckLoaded, Module, context, handlers, checkLoadedTimeoutId, config={ waitSeconds: 7,// 加载超时的秒数 baseUrl:'./', // 所有requirejs模块加载时查找的相对路径,作为根路径 paths:{},// 通常设置不是根据baseUrl加载的模块路径 bundles:{},// 模块束,同一个js文件下多个模块 pkgs:{},// 以包名为键存储包下的main模块路径 shim:{},// 对给定的模块前缀,使用一个不同的模块ID来加载该模块 config:{}// 传入模块的配置信息 }, registry={},// 以键值对形式存储待加载的模块,id为键,requirejs封装的module为值 enabledRegistry = {},// 以键值对形式存储加载的模块,id为键,requirejs封装的module为值 undefEvents = {}, defQueue = [], defined={},// 键值对形式存储已加载的模块,加载完成后,清除registry中的相应模块 urlFetched = {}, bundlesMap = {}, requireCounter = 1, unnormalizedCounter = 1; // trimDots当文件路径转化成数组以后,按'.'或'..'调整该路径,去除多余或非法的'.'及'..' function trimDots(ary) { var i, part; for (i = 0; i < ary.length; i++) { part = ary[i]; if (part === '.') { ary.splice(i, 1); i -= 1; } else if (part === '..') { if (i === 0 || (i === 1 && ary[2] === '..') || ary[i - 1] === '..') { continue; } else if (i > 0) { ary.splice(i - 1, 2); i -= 2; } } } } // 由相对路径拼接真实路径,根据config.map获取特定版本的子模块,config.pkgs获取main文件 // 当模块符合config.paths获取文件路径时,applyMap为否,不对name作处理,文件路径通过nameToUrl获取 function normalize(name,baseName,applyMap){ var pkgMain,mapValue,nameParts,i,j,nameSegment,lastIndex, foundMap,foundI,foundStarMap,starI,normalizedBaseParts, baseParts=(baseName && baseName.split('/')), map=config.map,// 对给定的模块前缀,使用一个不同的模块ID来加载该模块 starMap=map && map['*']; // config.nodeIdCompat为真时,移除js文件后缀 // name以"."起始,通过baseName作相应调整 if ( name ){ name=name.split('/'); lastIndex=name.length-1; if ( config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex]) ){ name[lastIndex]=name[lastIndex].replace(jsSuffixRegExp,''); } if ( name[0].charAt(0)==='.' && baseParts ){ normalizedBaseParts=baseParts.slice(0,baseParts.length-1); name=normalizedBaseParts.concat(name); } trimDots(name); name=name.join('/'); } // 根据父模块获取不同版本的子模块,name包含子模块的简写名称,baseName包含父模块的名称 if ( applyMap && map && (baseParts || starMap) ){ nameParts=name.split('/'); // js的新认识,循环体的命名和跳出循环体,可以限定循环层级 outerLoop: for ( i=nameParts.length; i>0; i-=1 ){ nameSegment=nameParts.slice(0,i).join('/'); if ( baseParts ){ for ( j=baseParts.length; j>0; j-=1 ){ mapValue=getOwn(map, baseParts.slice(0,j).join('/')); if ( mapValue ){ mapValue=getOwn(mapValue,nameSegment); if ( mapValue ){ foundMap=mapValue; foundI=i; break outerLoop; } } } } if ( !foundStarMap && starMap && getOwn(starMap,nameSegment) ){ foundStarMap=getOwn(starMap,nameSegment); starI=i; } } if ( !foundMap && foundStarMap ){ foundMap=foundStarMap; foundI=starI; } if ( foundMap ){ nameParts.splice(0,foundI,foundMap); name=nameParts.join('/'); } } // name指向config.pkgs的包名,替换为包下为main文件 pkgMain=getOwn(config.pkgs, name); return pkgMain ? pkgMain : name; } // 当前上下文context.contextName下移除模块名为name的script节点 function removeScript(name) { if (isBrowser) { each(scripts(), function (scriptNode) { if (scriptNode.getAttribute('data-requiremodule') === name && scriptNode.getAttribute('data-requirecontext') === context.contextName) { scriptNode.parentNode.removeChild(scriptNode); return true; } }); } } // 加载模块符合config.map配置,先移除id名模块,通过context.makeReequire重新加载模块 function hasPathFallback(id) { // config.paths映射模块的加载路径,"/"或"http:"取绝对路径,其余情况相对baseUrl;针对cdn加载的模块 var pathConfig = getOwn(config.paths, id); if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) { pathConfig.shift(); context.require.undef(id);// 移除模块 // 加载模块符合config.map配置,传入skipMap=true,最终makeModuleMap、normalize对name不作处理,context.nameToUrl获取文件路径 context.makeRequire(null, { skipMap: true })([id]); return true; } } // 以!分割prefix和name,prefix为插件 function splitPrefix(name){ var prefix, index=name ? name.indexOf('!') : -1; if ( index>-1 ){ prefix=name.substring(0, index); name=name.substring(index+1, name.length); } return [prefix,name]; } // 将模块转化为对象形式,存储转化的路径、父模块、插件等属性 function makeModuleMap(name,parentModuleMap,isNormalized,applyMap){ var url, pluginModule, suffix, nameParts, prefix=null, parentName=parentModuleMap ? parentModuleMap.name : null, originalName=name, isDefine=true, normalizedName=''; // 使用require方法时可以没有name,由requirejs生成;define需要name参数 if (!name){ isDefine=false; name='_@r'+(requireCounter+=1); } nameParts=splitPrefix(name); prefix=nameParts[0];// 插件名 name=nameParts[1]; if ( prefix ){ prefix=normalize(prefix,parentName,applyMap); pluginModule=getOwn(defined,prefix);// 获取插件 } if ( name ){ if ( prefix ){ if ( pluginModule && pluginModule.normalize ){ normalizedName=pluginModule.normalize(name,function (name){ // 相对路径转化为绝对路径,根据config.map|pkgs调整路径 return normalize(name,parentName,applyMap); }); } else { normalizedName=name.indexOf('!')===-1 ? normalize(name,parentName,applyMap) : name; } }else{ normalizedName=normalize(name,parentName,applyMap); nameParts=splitPrefix(normalizedName); prefix=nameParts[0]; normalizedName=nameParts[1]; isNormalized=true; url=context.nameToUrl(normalizedName); } } // name中带插件前缀,插件未加载,id中添加suffix suffix=prefix && !pluginModule && !isNormalized ? '_unnormalized'+(unnormalizedCounter+=1) : ''; return { prefix:prefix,// 插件 name:normalizedName,// 模块名由相对路径转化为绝对路径,又经过config.map|pkgs处理 parentMap:parentModuleMap,// 父模块 unnormalized:!!suffix,// name有插件的情况下,插件pluginModule未加载 url:url,// 加载js文件的实际路径 originalName:originalName,// require加载的原始模块名 isDefine:isDefine,// 没有携带name时为否,其余为真 id:( prefix ? prefix+'!'+normalizedName : normalizedName )+suffix // id中携带插件,转化后的绝对路径,和插件有无加载的后缀 }; } // 添加的模块通过makeModuleMap方法根据config.map等将路径转化为绝对路径,取得id; // 通过getModule获取requirejs封装的模块对象,并且添加到registry中,以供调用 function getModule(depMap) { var id = depMap.id, mod = getOwn(registry, id); if (!mod) { mod = registry[id] = new context.Module(depMap); } return mod; } // 模块已完成加载,且name为defined,执行回调函数fn;加载出错时,且name为error,执行回调fn;其余为模块绑定事件 function on(depMap, name, fn) { var id = depMap.id, mod = getOwn(registry, id); if (hasProp(defined, id) && (!mod || mod.defineEmitComplete)) { if (name === 'defined') { fn(defined[id]); } } else { mod = getModule(depMap); if (mod.error && name === 'error') { fn(mod.error); } else { mod.on(name, fn); } } } // 报错,或者直接调用errback回调函数,或者触发模块的error事件,或者直接抛出错误 function onError(err, errback) { var ids = err.requireModules,// 数组形式,当前加载的模块 notified = false; if (errback) { errback(err); } else { each(ids, function (id) { var mod = getOwn(registry, id); if (mod) { mod.error = err; if (mod.events.error) { notified = true; mod.emit('error', err); } } }); if (!notified) { req.onError(err); } } } // 将define方法添加的模块globalDefQueue推送到defQueue,context.defQueueMap中 function takeGlobalQueue() { if (globalDefQueue.length) { each(globalDefQueue, function(queueItem) { var id = queueItem[0]; if (typeof id === 'string') { context.defQueueMap[id] = true; } defQueue.push(queueItem); }); globalDefQueue = []; } } // 依赖特殊模块require、exports、module,模块可以用commonjs风格书写 handlers = { 'require': function (mod) {// requirejs封装后的模块 if (mod.require) { return mod.require; } else { return (mod.require = context.makeRequire(mod.map)); } }, 'exports': function (mod) { mod.usingExports = true; if (mod.map.isDefine) { if (mod.exports) { return (defined[mod.map.id] = mod.exports); } else { return (mod.exports = defined[mod.map.id] = {}); } } }, 'module': function (mod) { if (mod.module) { return mod.module; } else { return (mod.module = { id: mod.map.id, uri: mod.map.url, config: function () { return getOwn(config.config, mod.map.id) || {}; }, exports: mod.exports || (mod.exports = {}) }); } } }; // 模块加载完成后,将模块添加到define对象中,待加载模块对象registry、enabledRegistry消除该模块 function cleanRegistry(id) { delete registry[id]; delete enabledRegistry[id]; } // 通过depMaps获取依赖,再次调用breakCycle函数,通过依赖模块的check方法添加script节点,完成依赖的加载 function breakCycle(mod, traced, processed) { var id = mod.map.id; if (mod.error) { mod.emit('error', mod.error); } else { traced[id] = true; each(mod.depMaps, function (depMap, i) { var depId = depMap.id, dep = getOwn(registry, depId); if (dep && !mod.depMatched[i] && !processed[depId]) { if (getOwn(traced, depId)) { mod.defineDep(i, defined[depId]); mod.check(); } else { breakCycle(dep, traced, processed); } } }); processed[id] = true; } } // 模块符合config.paths,删除节点,通过context.nameToUrl获取模块文件路径后完成加载 // 通过breakCycle找到依赖,并调用依赖的check方法加载依赖 function checkLoaded() { var err, usingPathFallback, waitInterval = config.waitSeconds * 1000, expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), noLoads = [], reqCalls = [], stillLoading = false, needCycleCheck = true; if (inCheckLoaded) { return; } inCheckLoaded = true; eachProp(enabledRegistry, function (mod) { var map = mod.map, modId = map.id; if (!mod.enabled) { return; } if (!map.isDefine) { reqCalls.push(mod); } if (!mod.error) { if (!mod.inited && expired) { // hasPathFallback加载模块符合config.map配置,先移除id名模块,通过context.makeReequire重新加载模块 if (hasPathFallback(modId)) { usingPathFallback = true; stillLoading = true; } else { noLoads.push(modId); removeScript(modId); } } else if (!mod.inited && mod.fetched && map.isDefine) { stillLoading = true; if (!map.prefix) {// 加载失败的模块不予循坏加载,除非插件未获取成功 return (needCycleCheck = false); } } } }); if (expired && noLoads.length) {// 加载超时,且模块init方法未执行 err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads); err.contextName = context.contextName; return onError(err); } // init方法执行完成的模块,加载超时时重新加载 if (needCycleCheck) { each(reqCalls, function (mod) { breakCycle(mod, {}, {}); }); } // 定时跑checkLoaded,加载cdn模块以及依赖 if ((!expired || usingPathFallback) && stillLoading) { if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) { checkLoadedTimeoutId = setTimeout(function () { checkLoadedTimeoutId = 0; checkLoaded(); }, 50); } } inCheckLoaded = false; } Module = function (map) { this.events = getOwn(undefEvents, map.id) || {}; this.map = map;// 将字符串形式的模块名通过makeModuleMap转化为对象形式,包含id,parentModule,url,plugin等数据 this.shim = getOwn(config.shim, map.id);// 模块没有使用define声明依赖,shim用以声明依赖、以及导出函数名 this.depExports = [];// 数组形式存储依赖模块的接口 this.depMaps = [];// 依赖的模块 this.depMatched = []; this.pluginMaps = {}; this.depCount = 0; /* this.exports this.factory this.depMaps = [], this.enabled, this.fetched */ }; Module.prototype = { init: function (depMaps, factory, errback, options) { options = options || {}; if (this.inited) { return; } this.factory = factory; if (errback) { this.on('error', errback); } else if (this.events.error) {// 有报错error时触发的回调函数,没有errback,触发错误回调函数 errback = bind(this, function (err) { this.emit('error', err); }); } this.depMaps = depMaps && depMaps.slice(0); this.errback = errback; this.inited = true; this.ignore = options.ignore; if (options.enabled || this.enabled) {// 模块已执行enable方法,则不予再次执行 this.enable(); } else { this.check(); } }, // 获取依赖模块的接口,数组形式存储在this.depExports中,供当前模块使用;this.depCount用以判断依赖是否加载完成 defineDep: function (i, depExports) { if (!this.depMatched[i]) { this.depMatched[i] = true; this.depCount -= 1; this.depExports[i] = depExports; } }, // 依赖没有通过init方法初始化(define方法加载的模块均不执行init方法,直接调用enable方法,require加载的模块则执行) // 需要通过路径找到js文件完成加载,特别还需要通过shim转换为模块,或者经plugin处理 fetch: function () { if (this.fetched) { return; } this.fetched = true; context.startTime = (new Date()).getTime(); var map = this.map; if (this.shim) {// 模块没有使用define声明依赖,shim用以声明依赖、以及导出函数名 context.makeRequire(this.map, { enableBuildCallback: true })(this.shim.deps || [], bind(this, function () { return map.prefix ? this.callPlugin() : this.load(); })); } else { return map.prefix ? this.callPlugin() : this.load(); } }, // 通过context.load调用req.load方法加载js文件,并绑定载入成功、失败事件 load: function () { var url = this.map.url; if (!urlFetched[url]) { urlFetched[url] = true; context.load(this.map.id, url); } }, // 加载模块,并获得其exports接口 check: function () { if (!this.enabled || this.enabling) { return; } var err, cjsModule, id = this.map.id, depExports = this.depExports, exports = this.exports, factory = this.factory; // 依赖没有通过init方法初始化(define方法加载的模块均不执行init方法,直接调用enable方法,require加载的模块则执行) // 需要通过路径找到js文件完成加载,特别还需要通过shim转换为模块,或者经plugin处理 if (!this.inited) { if (!hasProp(context.defQueueMap, id)) { this.fetch(); } } else if (this.error) { this.emit('error', this.error); } else if (!this.defining) { this.defining = true; if (this.depCount < 1 && !this.defined) { if (isFunction(factory)) { try { // context.execCb以exports为this关键字,depExports为传参,执行factory函数 exports = context.execCb(id, factory, depExports, exports); } catch (e) { err = e; } // makeModuleMap方法中定性this.map.isDefine为携带name值 if (this.map.isDefine && exports === undefined) { cjsModule = this.module;// 依赖了module模块,输出为module.exports if (cjsModule) { exports = cjsModule.exports; } else if (this.usingExports) { exports = this.exports; } } if (err) { if ((this.events.error && this.map.isDefine) || req.onError !== defaultOnError) { err.requireMap = this.map; err.requireModules = this.map.isDefine ? [this.map.id] : null; err.requireType = this.map.isDefine ? 'define' : 'require'; return onError((this.error = err)); } else if (typeof console !== 'undefined' && console.error) { console.error(err); } else { req.onError(err); } } } else {// factory非函数直接作为exports输出 exports = factory; } this.exports = exports; if (this.map.isDefine && !this.ignore) {// ignore属性对外不输出接口,比如jQuery插件 defined[id] = exports; if (req.onResourceLoad) {// req.onResourceLoad在requirejs中未定义 var resLoadMaps = []; each(this.depMaps, function (depMap) { resLoadMaps.push(depMap.normalizedMap || depMap); }); req.onResourceLoad(context, this.map, resLoadMaps); } } cleanRegistry(id); this.defined = true; } this.defining = false; if (this.defined && !this.defineEmitted) {// 触发defined插件,加载被依赖的模块 this.defineEmitted = true; this.emit('defined', this.exports); this.defineEmitComplete = true; } } }, // 加载插件,并使用插件编译模块文本或加载模块 callPlugin: function () { var map = this.map, id = map.id, pluginMap = makeModuleMap(map.prefix); this.depMaps.push(pluginMap); on(pluginMap, 'defined', bind(this, function (plugin) {// 插件加载完成以后执行函数 var load, normalizedMap, normalizedMod, bundleId = getOwn(bundlesMap, this.map.id),// 在这里处理模块束??? name = this.map.name, parentName = this.map.parentMap ? this.map.parentMap.name : null, // context.makeRequire(relMap,options)返回内部函数localRequire,调用时加载模块 // enableBuildCallback标志用于重新构建模块,config.shim未用define方法加载模块也有 localRequire = context.makeRequire(map.parentMap, { enableBuildCallback: true }); // define方法添加的模块执行makeModuleMap时未找到插件,插件加载完成后 // 以插件的normalize方法获取文件路径,获取模块文件并进行加载 if (this.map.unnormalized) { if (plugin.normalize) { name = plugin.normalize(name, function (name) { return normalize(name, parentName, true); }) || ''; } normalizedMap = makeModuleMap(map.prefix + '!' + name, this.map.parentMap); on(normalizedMap, 'defined', bind(this, function (value) { this.map.normalizedMap = normalizedMap; this.init([], function () { return value; }, null, { enabled: true, ignore: true }); })); normalizedMod = getOwn(registry, normalizedMap.id); if (normalizedMod) { this.depMaps.push(normalizedMap); if (this.events.error) { normalizedMod.on('error', bind(this, function (err) { this.emit('error', err); })); } normalizedMod.enable(); } return; } // 符合config.bundles模块束情况,重新获取url后,加载模块 if (bundleId) { this.map.url = context.nameToUrl(bundleId); this.load(); return; } // 插件中执行的回调函数,插件加载完成后加载模块,enabled=true时直接执行check方法 load = bind(this, function (value) { this.init([], function () { return value; }, null, { enabled: true }); }); // 为插件提供报错接口,以供调用 load.error = bind(this, function (err) { this.inited = true; this.error = err; err.requireModules = [id]; eachProp(registry, function (mod) { if (mod.map.id.indexOf(id + '_unnormalized') === 0) { cleanRegistry(mod.map.id); } }); onError(err); }); // 加载插件前执行脚本,作为依赖在load函数前执行,执行完触发load加载模块 load.fromText = bind(this, function (text, textAlt) { var moduleName = map.name, moduleMap = makeModuleMap(moduleName), hasInteractive = useInteractive; if (textAlt) { text = textAlt; } if (hasInteractive) { useInteractive = false; } getModule(moduleMap); if (hasProp(config.config, id)) { config.config[moduleName] = config.config[id]; } try { req.exec(text); } catch (e) { return onError(makeError('fromtexteval', 'fromText eval for ' + id + ' failed: ' + e, e, [id])); } if (hasInteractive) { useInteractive = true; } this.depMaps.push(moduleMap); context.completeLoad(moduleName); // 脚本作为依赖,load作为回调函数 localRequire([moduleName], load); }); // 调用插件的load方法对当前模块进行改写,如处理less、txt、img等 plugin.load(map.name, localRequire, load, config); })); context.enable(pluginMap, this);// 加载插件 this.pluginMaps[pluginMap.id] = pluginMap; }, // 判断依赖或插件绑定加载完成defined事件(触发当前模块加载事件),并为当前模块提供接口 // 所有依赖加载完成标记为this.depCount==0,执行enable方法时随依赖数量+1,依赖defined完成时-1 enable: function () { enabledRegistry[this.map.id] = this; this.enabled = true;// 是否执行enable方法标记 this.enabling = true;// enable方法执行中标记 each(this.depMaps, bind(this, function (depMap, i) { var id, mod, handler; if (typeof depMap === 'string') { // 加载模块符合config.map配置,传入skipMap=true,最终makeModuleMap、normalize对name不作处理,context.nameToUrl获取文件路径 depMap = makeModuleMap(depMap,(this.map.isDefine ? this.map : this.map.parentMap),false,!this.skipMap); this.depMaps[i] = depMap; handler = getOwn(handlers, depMap.id);// 依赖的模块中包含require、或exports、或module特殊模块 if (handler) { this.depExports[i] = handler(this); return; } this.depCount += 1;// 所有依赖是否加载完成标记 // defined事件触发时获取依赖模块的接口,this.depCount标记-1,执行当前模块的check方法,在依赖的check方法中触发 on(depMap, 'defined', bind(this, function (depExports) { if (this.undefed) { return; } this.defineDep(i, depExports); this.check(); })); if (this.errback) { on(depMap, 'error', bind(this, this.errback)); } else if (this.events.error) { on(depMap, 'error', bind(this, function(err) { this.emit('error', err); })); } } id = depMap.id; mod = registry[id]; if (!hasProp(handlers, id) && mod && !mod.enabled) { // context.enable方法,依赖及祖父级依赖完成加载,即依赖的enable、check方法;加载完成后触发当前模块的check方法 context.enable(depMap, this); } })); eachProp(this.pluginMaps, bind(this, function (pluginMap) { var mod = getOwn(registry, pluginMap.id); if (mod && !mod.enabled) { context.enable(pluginMap, this); } })); this.enabling = false; this.check(); }, // 绑定事件,回调函数存储在this.events[eventName]对象中 on: function (name, cb) { var cbs = this.events[name]; if (!cbs) { cbs = this.events[name] = []; } cbs.push(cb); }, // 触发事件,error事件清空error毁掉函数 emit: function (name, evt) { each(this.events[name], function (cb) { cb(evt); }); if (name === 'error') { delete this.events[name]; } } }; // 获取requirejs封装的Module模块,将模块添加到待加载对象registry中 function callGetModule(args) { if (!hasProp(defined, args[0])) { getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); } } // 节点移除事件 function removeListener(node, func, name, ieName) { if (node.detachEvent && !isOpera) { if (ieName) { node.detachEvent(ieName, func); } } else { node.removeEventListener(name, func, false); } } // script节点加载完成,移除load、error事件,返回节点和模块名 function getScriptData(evt) { var node = evt.currentTarget || evt.srcElement; removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange'); removeListener(node, context.onScriptError, 'error'); return { node: node, id: node && node.getAttribute('data-requiremodule') }; } // 获取并加载define方法添加的模块 function intakeDefines() { var args; takeGlobalQueue();// 将define方法添加的模块转存到defQueue中 while (defQueue.length) { args = defQueue.shift(); if (args[0] === null) { return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1])); } else { callGetModule(args); } } context.defQueueMap = {}; } // require上下文,向外提供makeRequire接口供require方法使用,require方法执行时获取define方法定义的模块并加载 // 提供configure供requirejs配置 context={ config: config, contextName: contextName,// 上下文名称,作为标识符 registry: registry,// 将添加的模块转化为requirejs封装的context.Module对象后,存储在registry中,以供取出 defined: defined,// 键值对村吃加载成功的模块 urlFetched: urlFetched, defQueue: defQueue,// 数组形式存储define方法添加的模块 defQueueMap: {},// 键值对形式存储define方法添加的模块id Module: Module,// requirejs封装的模块 makeModuleMap: makeModuleMap,// 将添加的模块转化为对象形式,存储id,plugin,parentModule,url等 nextTick: req.nextTick,// 调用setTimeout或者fn()方法执行fn函数 onError: onError, /** * cfg.baseUrl 所有requirejs模块加载时查找的相对路径,作为根路径 * cfg.shim 模块没有使用define声明依赖,shim用以声明依赖、以及导出函数名 * 如cfg.shim={ * "backbone":{ * deps:["underscore","jquery"], * exports:"Backbone" * }, * "underscore":{ * exports:"_" * }, * "foo":{ * deps:["bar"], * exports:"Foo", * init:function(bar){ * return this.Foo.noConflict(); * } * } * } * cfg.paths 映射模块的加载路径,"/"或"http:"取绝对路径,其余情况相对baseUrl * 如cfg.paths={ * jquery:"http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min" * } * cfg.map 对给定的模块前缀,使用一个不同的模块ID来加载该模块 * 如cfg.map={ * "some/newmodule":{ // "some/newmodule"模块require("foo")加载"foo1.2"模块 * "foo":"foo1.2" * }, * "some/oldmodule":{ // "some/oldmodule"模块require("foo")加载"foo1.1"模块 * "foo":"foo1.1" * } * } * cfg.config 将配置信息传递给模块 * 如cfg.config={ * "bar":{ * size:"large" * }, * }; * define(function(require,exports,module){// 加载依赖module,module.config()获取配置 * var size=module.config().size; * }); * cfg.bundles 声明模块束,果一个JS文件包含多个模块 * // jsUtil.js文件有两个模块"MathUtil","DateUtil" * cfg.bundles={jsUtil:["MathUtil","DateUtil"]} * cfg.packages 从包中加载模块 * 如cfg.packages=["cart","store"] * 或cfg.packages=[ * "cart", * { * name:"store", // 文件夹的名称 * main:"store", // 未设置,默认加载包下的main.js文件 * location:"/store" // "/"或"https:"设置取绝对路径 * } * ]; * require(["cart","store","store/util"],function(cart,store,util){ * });// 加载"cart/main.js","store/store.js","store/util.js" * cfg.deps,cfg.callback 初始化加载的依赖,及加载完成后的回调函数 * */ configure:function(cfg){ if ( cfg.baseUrl ){ if ( cfg.baseUrl.charAt(cfg.baseUrl.length-1)!=='/' ){ cfg.baseUrl+='/'; } } var shim=config.shim, objs={// obj[key]设置为真时,深拷贝cfg[key]对象;其余单个赋值拷贝 paths:true, bundles:true, config:true, map:true }; eachProp(cfg,function (value,prop){ if ( objs[prop] ){ if ( !config[prop] ){ config[prop]={}; } mixin(config[prop],value,true,true); }else{ config[prop]=value; } }); if ( cfg.bundles ){// 反转模块束 eachProp(cfg.bundles,function(value,prop){ each(value,function(v){ if ( v!==prop ){ bundlesMap[v]=prop; } }); }); } if ( cfg.shim ){ eachProp(cfg.shim, function(value,id){ if ( isArray(value) ){ value={ deps: value }; } if ( (value.exports || value.init) && !value.exportsFn ){ value.exportsFn=context.makeShimExports(value); } shim[id]=value; }); config.shim=shim; } if ( cfg.packages ){// config.paths存储包文件夹所在路径,config.pkgs存储主模块路径 each(cfg.packages,function(pkgObj){ var location,name; pkgObj=typeof pkgObj==='string' ? {name:pkgObj} : pkgObj; name=pkgObj.name; location=pkgObj.location; if ( location ){ config.paths[name]=pkgObj.location; } config.pkgs[name]=pkgObj.name+'/'+(pkgObj.main || 'main') .replace(currDirRegExp,'') .replace(jsSuffixRegExp,''); }); } // registry中有待加载的模块,通过makeModuleMap重新获取url,url可能被改变 eachProp(registry, function (mod, id) { if (!mod.inited && !mod.map.unnormalized) { mod.map = makeModuleMap(id, null, true); } }); if ( cfg.deps || cfg.callback ){ context.require(cfg.deps || [],cfg.callback); } }, // 没有使用define语法定义的模块使用shim声明init方法、expots属性导出该模块 makeShimExports:function(value){ function fn(){ var ret; if ( value.init ){ ret=value.init.apply(global,arguments); } return ret || (value.exports && getGlobal(value.exports)); } return fn; }, // require方法调用makeRequire,获取define方法定义的模块并加载,执行require方法回调 makeRequire: function (relMap, options) { options = options || {}; function localRequire(deps, callback, errback) { var id, map, requireMod; if (options.enableBuildCallback && callback && isFunction(callback)) { callback.__requireJsBuild = true; } if (typeof deps === 'string') { if (isFunction(callback)) { return onError(makeError('requireargs', 'Invalid require call'), errback); } //If require|exports|module are requested, get the //value for them from the special handlers. Caveat: //this only works while module is being defined. if (relMap && hasProp(handlers, deps)) { return handlers[deps](registry[relMap.id]); } //Synchronous access to one module. If require.get is //available (as in the Node adapter), prefer that. if (req.get) { return req.get(context, deps, relMap, localRequire); } // 通过makeModuleMap获取模块id后,取得defined下已加载的模块 map=makeModuleMap(deps,relMap,false,true); id=map.id; if ( !hasProp(defined, id) ){ return onError(makeError('notloaded', 'Module name "'+ id+ '" has not been loaded yet for context: '+ contextName+ (relMap ? '' : '. Use require([])'))); } return defined[id]; } // 获取并加载define方法添加的模块 intakeDefines(); //Mark all the dependencies as needing to be loaded. context.nextTick(function () { // define用以预定义模块,调用define方法时未完成js文件的加载,页面用script标签显示加载除外 // require方法直接加载js文件,同时完成加载define方法添加的父级依赖 intakeDefines(); // getModule方法将模块转化为requirejs封装的Module对象后取出 // require方法添加的模块也通过Module.init方法触发其回调函数执行,机理同define方法 requireMod = getModule(makeModuleMap(null, relMap)); // skipMap用来使符合config.paths配置的模块跳过name转化,通过name获取url插入script节点,完成加载 requireMod.skipMap = options.skipMap; requireMod.init(deps, callback, errback, { enabled: true }); // 通过调用Module.check方法创建script节点元素加载符合config.paths配置的cdn或http模块,加载依赖 checkLoaded(); }); return localRequire; } mixin(localRequire, { isBrowser: isBrowser, /** * Converts a module name + .extension into an URL path. * *Requires* the use of a module name. It does not support using * plain URLs like nameToUrl. */ toUrl: function (moduleNamePlusExt) { var ext, index = moduleNamePlusExt.lastIndexOf('.'), segment = moduleNamePlusExt.split('/')[0], isRelative = segment === '.' || segment === '..'; //Have a file extension alias, and it is not the //dots from a relative path. if (index !== -1 && (!isRelative || index > 1)) { ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); moduleNamePlusExt = moduleNamePlusExt.substring(0, index); } return context.nameToUrl(normalize(moduleNamePlusExt, relMap && relMap.id, true), ext, true); }, // 判断模块是否加载 defined: function (id) { return hasProp(defined, makeModuleMap(id, relMap, false, true).id); }, // 判断模块是否加载、待加载 specified: function (id) { id = makeModuleMap(id, relMap, false, true).id; return hasProp(defined, id) || hasProp(registry, id); } }); if (!relMap) { // 通过删除script节点、清理defQueue的方式移除模块,监听事件仍保留,方便模块再次被加载 localRequire.undef = function (id) { takeGlobalQueue(); var map = makeModuleMap(id, relMap, true), mod = getOwn(registry, id); mod.undefed = true; removeScript(id); delete defined[id]; delete urlFetched[map.url]; delete undefEvents[id]; eachReverse(defQueue, function(args, i) { if (args[0] === id) { defQueue.splice(i, 1); } }); delete context.defQueueMap[id]; if (mod) { if (mod.events.defined) { undefEvents[id] = mod.events; } cleanRegistry(id); } }; } return localRequire; }, // 依赖或插件顺序执行enable、check方法,完成加载,加载完成后执行当前模块的check方法 enable: function (depMap) { var mod = getOwn(registry, depMap.id); if (mod) { getModule(depMap).enable(); } }, // 模块加载完成后,为没有用define函数定义的模块添加依赖和输出等 completeLoad: function (moduleName) { var found, args, mod, // 未使用define方法定义的模块,confid.shim定义其依赖和输出exports shim = getOwn(config.shim, moduleName) || {}, shExports = shim.exports; // 将define加载的模块推送到registry待加载队列中 takeGlobalQueue(); while (defQueue.length) { args = defQueue.shift(); if (args[0] === null) { args[0] = moduleName; if (found) { break; } found = true; } else if (args[0] === moduleName) { found = true; } callGetModule(args); } context.defQueueMap = {}; mod = getOwn(registry, moduleName); // 依赖都不执行Module.init,require方法加载的模块则均执行init if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) { if (config.enforceDefine && (!shExports || !getGlobal(shExports))) { if (hasPathFallback(moduleName)) {// cdn或http加载的模块,不是相对于baseName return; } else { return onError(makeError('nodefine', 'No define call for ' + moduleName, null, [moduleName])); } } else { // 没有用define方法定义的模块添加依赖和输出exports,完成加载 callGetModule([moduleName, (shim.deps || []), shim.exportsFn]); } } // 通过调用Module.check方法创建script节点元素加载符合config.paths配置的cdn或http模块,加载依赖 checkLoaded(); }, // 根据config.paths|bundles|pkgs、baseUrl调整moduleName,获取加载文件的绝对路径 nameToUrl: function (moduleName, ext, skipExt) { var paths, syms, i, parentModule, url, parentPath, bundleId, pkgMain = getOwn(config.pkgs, moduleName); if (pkgMain) { moduleName = pkgMain; } bundleId = getOwn(bundlesMap, moduleName); if (bundleId) { return context.nameToUrl(bundleId, ext, skipExt); } // 有js后缀,原路输出 if (req.jsExtRegExp.test(moduleName)) { url = moduleName + (ext || ''); } else { // url中替换config.paths别名 paths = config.paths; syms = moduleName.split('/'); for (i = syms.length; i > 0; i -= 1) { parentModule = syms.slice(0, i).join('/'); parentPath = getOwn(paths, parentModule); if (parentPath) { if (isArray(parentPath)) { parentPath = parentPath[0]; } syms.splice(0, i, parentPath); break; } } // 按baseUrl进行调整 url = syms.join('/'); url += (ext || (/^data\:|\?/.test(url) || skipExt ? '' : '.js')); url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; } // config.urlArgs添加url参数 return config.urlArgs ? url + ((url.indexOf('?') === -1 ? '?' : '&') + config.urlArgs) : url; }, // 加载js文件,并完成载入成功、失败事件 load: function (id, url) { req.load(context, id, url); }, // callback为模块的执行函数,args为模块的依赖,exports为模块的输出 execCb: function (name, callback, args, exports) { return callback.apply(exports, args); }, // 节点加载完成后,移除监听事件,data获取节点及模块名,用define函数定义的模块添加依赖和输出 onScriptLoad: function (evt) { if (evt.type === 'load' || (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) { interactiveScript = null; var data = getScriptData(evt); context.completeLoad(data.id);// 模块加载完成后,为没有用define函数定义的模块添加依赖和输出等 } }, // 模块加载失败,尝试用config.paths文件路径加载依赖,否则抛出缺少依赖、加载不到文件的错误 onScriptError: function (evt) { var data = getScriptData(evt); if (!hasPathFallback(data.id)) { var parents = []; eachProp(registry, function(value, key) { if (key.indexOf('_@r') !== 0) { each(value.depMaps, function(depMap) { if (depMap.id === data.id) { parents.push(key); } return true; }); } }); return onError(makeError('scripterror', 'Script error for "' + data.id + (parents.length ? '", needed by: ' + parents.join(', ') : '"'), evt, [data.id])); } } }; context.require=context.makeRequire(); return context; } /*---------------------------------------------------------------------------------------------*/ // 配置requirejs,或者预加载模块、执行回调 req=requirejs=function(deps,callback,errback,optional){ var context,config, contextName=defContextName; if ( !isArray(deps) && typeof deps!=='string' ){// 首参不是数组和字符串时,为配置项 config=deps; if ( isArray(callback) ){ deps=callback; callback=errback; errback=optional; }else{ deps=[]; } } if ( config && config.context ){ contextName=config.context; } // 获取或创建requirejs上下文 context=getOwn(contexts,contextName); if ( !context ){ context=contexts[contextName]=req.s.newContext(contextName); } // 配置requirejs上下文 if ( config ){ context.configure(config); } return context.require(deps,callback,errback); }; // 配置requirejs req.config=function(config){ return req(config); }; // 调用setTimeout或者fn()方法执行fn函数 req.nextTick=typeof setTimeout!=='undefined' ? function (fn){ setTimeout(fn,4); } : function(fn){fn();}; if (!require){ require=req; } req.version = version; req.jsExtRegExp=/^\/|:|\?|\.js$/; req.isBrowser=isBrowser; s=req.s={ contexts:contexts, newContext:newContext }; req({}); // req添加'toUrl','undef','defined','specified'方法 each([ 'toUrl',// name + .ext转化为url路径 'undef',// 卸载模块 'defined',// 判断模块是否加载 'specified'// 判断模块是否加载、待加载 ], function (prop) { req[prop] = function () { var ctx = contexts[defContextName]; return ctx.require[prop].apply(ctx, arguments); }; }); if (isBrowser) { head = s.head = document.getElementsByTagName('head')[0]; // base标签存在时,appendChild方法在IE6中报错 baseElement = document.getElementsByTagName('base')[0]; if (baseElement) { head = s.head = baseElement.parentNode; } } req.onError=defaultOnError; // 浏览器段创建script节点 req.createNode=function(config,moduleName,url){ var node=config.xhtml ? document.createElementNS('http://www.w3.org/1999/xhtml','html:script') : document.createElement('script'); node.type=config.scriptType || 'text/javascript'; node.charset='utf-8'; node.async=true; return node; }; // 浏览器环境创建script节点加载模块文件,非浏览器环境通过importScripts函数加载js文件 req.load = function (context, moduleName, url) { var config = (context && context.config) || {}, node; if (isBrowser) { node = req.createNode(config, moduleName, url); if (config.onNodeCreated) {// script节点创建后执行函数 config.onNodeCreated(node, config, moduleName, url); } node.setAttribute('data-requirecontext', context.contextName); node.setAttribute('data-requiremodule', moduleName); if (node.attachEvent && !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && !isOpera) { useInteractive = true; node.attachEvent('onreadystatechange', context.onScriptLoad); } else { node.addEventListener('load', context.onScriptLoad, false); node.addEventListener('error', context.onScriptError, false); } node.src = url; currentlyAddingScript = node;// 随后赋值为null,针对ie 6-8 的缓存问题 if (baseElement) { head.insertBefore(node, baseElement); } else { head.appendChild(node); } currentlyAddingScript = null; return node; } else if (isWebWorker) { try { importScripts(url); context.completeLoad(moduleName); } catch (e) { context.onError(makeError('importscripts','importScripts failed for ' + moduleName + ' at ' + url,e,[moduleName])); } } }; // 获取最后一个已加载的script节点,(即当前执行的requirejs) function getInteractiveScript() { if (interactiveScript && interactiveScript.readyState === 'interactive') { return interactiveScript; } eachReverse(scripts(), function (script) { if (script.readyState === 'interactive') {// 已加载状态 return (interactiveScript = script); } }); return interactiveScript; } // 未设置baseUrl配置,获取最末一个已加载的script节点(即当前执行的requirejs),其data-main属性作为baseUrl,并加载该文件模块 if (isBrowser && !cfg.skipDataMain) { eachReverse(scripts(), function (script) { if (!head) { head = script.parentNode; } dataMain = script.getAttribute('data-main'); if (dataMain) { mainScript = dataMain; if (!cfg.baseUrl) { src = mainScript.split('/'); mainScript = src.pop(); subPath = src.length ? src.join('/') + '/' : './'; cfg.baseUrl = subPath; } mainScript = mainScript.replace(jsSuffixRegExp, ''); if (req.jsExtRegExp.test(mainScript)) { mainScript = dataMain; } //Put the data-main script in the files to load. cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript]; return true; } }); } // 定义模块,require方法时按需加载 define = function (name, deps, callback) { var node, context; if ( typeof name!=='string' ){ callback=deps; deps=name; name=null; } if ( !isArray(deps) ){ callback=deps; deps=null; } // deps依赖为空,callback有length属性时,获取模块中require方法加载的模块 // length为1时,添加依赖require;length为其他时,添加依赖require,exports,module if ( !deps && isFunction(callback) ){ deps=[]; if ( callback.length ){ callback // define声明的模块内部使用require(""),将该依赖更新到deps中 .toString() .replace(commentRegExp,'') .replace(cjsRequireRegExp,function(match,dep){ deps.push(dep); }); deps=(callback.length===1 ? ['require'] : ['require','exports','module']).concat(deps); } } //If in IE 6-8 and hit an anonymous define() call, do the interactive //work. if (useInteractive) { node = currentlyAddingScript || getInteractiveScript(); if (node) { if (!name) { name = node.getAttribute('data-requiremodule'); } context = contexts[node.getAttribute('data-requirecontext')]; } } if (context) { context.defQueue.push([name, deps, callback]); context.defQueueMap[name] = true; } else { // define方法添加的模块默认填入globalDefQueue队列,通过takeGlobalQueue方法获取 globalDefQueue.push([name,deps,callback]); } }; define.amd={ jQuery:true }; // 执行text脚本 req.exec=function(text){ return eval(text); }; req(cfg); }(this));