說明:
內容主要包括三部分:
1.按源碼的結構順序 對 全部的變量及方法的說明
2.requirejs運行流程
三、流程相關圖片node
爲了方便比對源碼,按源碼的結構順序展現。數組
var requirejs, require, define; (function (global, setTimeout) { var req, s, head, baseElement, dataMain, src, interactiveScript, currentlyAddingScript, mainScript, subPath, version = '2.3.5', commentRegExp = /\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/mg, //去除註釋 cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,//提取require函數的arguments jsSuffixRegExp = /\.js$/, currDirRegExp = /^\.\//, op = Object.prototype, ostring = op.toString, hasOwn = op.hasOwnProperty, isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document), isWebWorker = !isBrowser && typeof importScripts !== 'undefined', readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ? /^complete$/ : /^(complete|loaded)$/, defContextName = '_', isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]', contexts = {}, cfg = {}, globalDefQueue = [], useInteractive = false; //返回singlePrefix或空 function commentReplace(match, singlePrefix) {} //判斷函數 function isFunction(it) {} //判斷數組 function isArray(it) {} //執行函數func(ary[i], i, ary);返回真值,跳出循環 function each(ary, func) {} //與each序列反 function eachReverse(ary, func) {} //判斷obj是否有prop function hasProp(obj, prop) {} //返回obj上的prop function getOwn(obj, prop) {} //循環調用func(obj[prop], prop);返回真值,跳出循環 function eachProp(obj, func) {} //混合source屬性值(target沒有同名的)到target //force爲真,target同名覆蓋,deepStringMixin爲真,深混合 function mixin(target, source, force, deepStringMixin) {} //返回逆名函數,執行爲obj調用fn函數 function bind(obj, fn) {} //返回script元素的集合 function scripts() {} //throw err; function defaultOnError(err) {} //例getGlobal("aa.bb");爲global.aa.bb function getGlobal(value) {} //生成一個錯誤 function makeError(id, msg, err, requireModules) {} if (typeof define !== 'undefined') { return; } if (typeof requirejs !== 'undefined') { if (isFunction(requirejs)) { return; } cfg = requirejs; requirejs = undefined; } if (typeof require !== 'undefined' && !isFunction(require)) { cfg = require; require = undefined; } function newContext(contextName) { var inCheckLoaded, Module, context, handlers, checkLoadedTimeoutId, config = { waitSeconds: 7, baseUrl: './', paths: {}, bundles: {}, pkgs: {}, shim: {}, config: {} }, registry = {}, enabledRegistry = {}, undefEvents = {}, defQueue = [], defined = {}, urlFetched = {}, bundlesMap = {}, requireCounter = 1, unnormalizedCounter = 1; //ary中.刪除此項;..刪此項和前一項除(i === 0 || (i === 1 && ary[2] === '..') || ary[i - 1]==='..') function trimDots(ary) {} //路徑處理.config.pkgs有name值優先,無值按相對路徑轉化,apply是否啓用地圖配置 function normalize(name, baseName, applyMap) {} //刪除data-requiremoduley爲name和data-requirecontext === context.contextName的script function removeScript(name) {} //先移除再加載模塊; function hasPathFallback(id) {} //第一個"!"分離的先後數據 return [prefix, name]; function splitPrefix(name) {} //返回模塊的屬性對象 // return { // prefix: prefix, // name: normalizedName, // parentMap: parentModuleMap, // unnormalized: !!suffix, // url: url, // originalName: originalName, // isDefine: isDefine, // id: (prefix ? // prefix + '!' + normalizedName : // normalizedName) + suffix // }; function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) {} //registry[id]有值取值,沒值生成新的context.Module對象,並賦給registry[id] function getModule(depMap) {} //模塊加載完且name爲defined 或 加載出錯且name爲error ? 執行fn : 模塊綁定事件 function on(depMap, name, fn) {} //errback ? 執行errback(err) : mod.emit('error', err)執行刪除操做 function onError(err, errback) {} //將globalDefQueue推入defQueue function takeGlobalQueue() {} //commonjs風格 handlers = { //mod.require ? 返回mod.require : localRequire 'require': function (mod) {}, 'exports': function (mod) {}, 'module': function (mod) {} }; //清除registry[id]、enabledRegistry[id] function cleanRegistry(id) {} //遞歸mod.depMaps,執行mod.check(); function breakCycle(mod, traced, processed) {} //檢查加載狀態,不一樣狀態執行不一樣操做 function checkLoaded() {} Module = function (map) { this.events = getOwn(undefEvents, map.id) || {}; this.map = map; this.shim = getOwn(config.shim, map.id); this.depExports = []; this.depMaps = []; this.depMatched = []; this.pluginMaps = {}; this.depCount = 0; }; Module.prototype = { //初始化,根據options.enabled ? this.enable() : this.check() init: function (depMaps, factory, errback, options) {}, //經過this.depCount判斷依賴是否加載完成 defineDep: function (i, depExports) {}, // map.prefix ? this.callPlugin() : this.load(); fetch: function () {}, //經過context.load調req.load加載js文件 load: function () {}, //define模塊調用 check: function () {}, //加載依賴 callPlugin: function () {}, //data-main上的模塊調用,define模塊調用 enable: function () {}, //將cb推入this.events[name] on: function (name, cb) {}, //name === 'error'刪this.events[name];不然循環this.events[name]執行cb(evt); emit: function (name, evt) {} }; //module.init內執行check()非enable(); function callGetModule(args) {} //移除監聽事件 function removeListener(node, func, name, ieName) {} //移除監聽事件,返回節點 function getScriptData(evt) {} // 獲取並加載defQueue中的模塊 function intakeDefines() {} context = { config: config, contextName: contextName, registry: registry, defined: defined, urlFetched: urlFetched, defQueue: defQueue, defQueueMap: {}, Module: Module, makeModuleMap: makeModuleMap, nextTick: req.nextTick, onError: onError, //配置參數 調用context.require(cfg.deps || [], cfg.callback); configure: function (cfg) {}, //返回閉包接口供調用 makeShimExports: function (value) {}, //返回閉包接口供調用 makeRequire: function (relMap, options) { //makeRequire的實際執行函數,生成宏任務; function localRequire(deps, callback, errback) { return localRequire; } mixin(localRequire, { isBrowser: isBrowser, toUrl: function (moduleNamePlusExt) {}, defined: function (id) {}, specified: function (id) {} }); if (!relMap) { localRequire.undef = function (id) {}; } return localRequire; }, //調用 module的enable() enable: function (depMap) {}, //完成加載後 completeLoad: function (moduleName) {}, //根據moduleName獲取url nameToUrl: function (moduleName, ext, skipExt) {}, //調用req.load() load: function (id, url) {}, //return callback.apply(exports, args); execCb: function (name, callback, args, exports) {}, //加載完成後 onScriptLoad: function (evt) {}, //加載錯誤 onScriptError: function (evt) {} }; context.require = context.makeRequire(); return context; } //入口函數 req = requirejs = function (deps, callback, errback, optional) {}; //return req(config); req.config = function (config) {}; //宏任務 req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) { setTimeout(fn, 4); } : function (fn) { fn(); }; //req賦值給require if (!require) { require = req; } req.version = version; req.jsExtRegExp = /^\/|:|\?|\.js$/; req.isBrowser = isBrowser; s = req.s = { contexts: contexts, newContext: newContext }; //初始調用 req({}); each([ 'toUrl', '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]; baseElement = document.getElementsByTagName('base')[0]; if (baseElement) { head = s.head = baseElement.parentNode; } } req.onError = defaultOnError; //建立節點 req.createNode = function (config, moduleName, url) {}; //節點綁定事件,添加到頭部,並返回節點 req.load = function (context, moduleName, url) {}; //返回狀態爲interactive的節點 function getInteractiveScript() {} //data-main上的值解析賦給cfg 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 && mainScript.indexOf('!') === -1) { 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; } cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript]; return true; } }); } //定義模塊的函數 define = function (name, deps, callback) {}; define.amd = { jQuery: true }; req.exec = function (text) {}; //將data-main的值解析代入req函數; req(cfg); }(this, (typeof setTimeout === 'undefined' ? undefined : setTimeout)));
一、初始化變量;
二、執行 req({})閉包
req({})
context = contexts[contextName] = req.s.newContext(contextName);
說明:contextName='_',返回context這個東西,context.require = context.makeRequire();=localRequire;調用makeRequire實際調用makeRequire裏的localRequireapp
context.configure(cfg);
說明:cfg=config={},什麼都沒幹函數
return context.require(deps, callback, errback);
說明:調用makeRequire裏的localRequire;deps=[];requirejs
intakeDefines();
takeGlobalQueue();
說明:intakeDefines的子函數,二者什麼都沒執行fetch
context.nextTick(function () { intakeDefines(); requireMod = getModule(makeModuleMap(null, relMap)); requireMod.skipMap = options.skipMap; requireMod.init(deps, callback, errback, { enabled: true }); checkLoaded(); });
說明:產生一個 宏任務1 函數;req({})函數完ui
三、執行 req(cfg)
獲取data-main上的值並解析成cfgthis
req(cfg);
說明:cfg={baseUrl:"data-main解析值1",deps:[data-main解析值2]}url
context = getOwn(contexts, contextName);
說明:獲取以前產生的context;
context.configure(cfg);
說明:cfg=config={baseUrl:"data-main解析值1",deps:[data-main解析值2]}
config[prop] = value;
說明:屬性值給config
context.require(cfg.deps || [], cfg.callback);
說明:調用makeRequire裏的localRequire;deps=[data-main解析值2];
intakeDefines(); takeGlobalQueue();
說明:什麼都沒執行
context.nextTick(function () {
說明:產生一個 宏任務2 函數;
return context.require(deps, callback, errback);
說明:調用makeRequire裏的localRequire;deps=[];
intakeDefines(); takeGlobalQueue();
說明:什麼都沒執行
context.nextTick(function () {
說明:產生一個 宏任務3 函數;req(cfg);函數完
四、第一個宏任務開始
intakeDefines(); takeGlobalQueue();
說明:什麼都沒執行
requireMod = getModule(makeModuleMap(null, relMap));
makeModuleMap(null, relMap)
說明:返回一個對象obj
nameParts = splitPrefix(name);
說明:name="_@r2"
normalizedName = normalize(name, parentName, applyMap); url = context.nameToUrl(normalizedName);
parentModule = syms.slice(0, i).join('/');
說明:parentModule="_@r2"
getModule(obj)
new context.Module(depMap)
說明:depMap=上面返回的對象obj;getModule返回context.Module實例requireMod
requireMod.init(deps, callback, errback, {enabled: true}); this.enable(); enabledRegistry[this.map.id] = this; this.check(); cleanRegistry(id);
this.emit('defined', this.exports); checkLoaded();
說明:什麼都沒執行,第一個宏任務完。
五、第二個宏任務開始
intakeDefines(); takeGlobalQueue();
說明:什麼都沒執行
requireMod = getModule(makeModuleMap(null, relMap));
makeModuleMap(null, relMap)
說明:返回一個對象obj
nameParts = splitPrefix(name);
說明:name="_@r3"
normalizedName = normalize(name, parentName, applyMap); url = context.nameToUrl(normalizedName);
parentModule = syms.slice(0, i).join('/');
說明:parentModule="_@r3"
getModule(obj)
new context.Module(depMap)
說明:depMap=上面返回的對象obj;getModule返回context.Module實例requireMod
requireMod.init(deps, callback, errback, {enabled: true});
說明: deps 變爲 data-main解析值2
this.enable(); enabledRegistry[this.map.id] = this; depMap = makeModuleMap(depMap,(this.map.isDefine ? this.map : this.map.parentMap), false,!this.skipMap); nameParts = splitPrefix(name); normalizedName = normalize(name, parentName, applyMap); url = context.nameToUrl(normalizedName); " on(depMap, 'defined', bind(this, function (depExports) {this.defineDep(i, depExports);this.check();})); " mod = getModule(depMap); mod = registry[id] = new context.Module(depMap); mod.on(name, fn); getModule(depMap).enable(); this.check(); checkLoaded();
說明: 宏任務2結束
六、宏任務3同宏任務1
七、執行data-main引入的文件的require函數
require(['./example'],function(example){example.test();}); req = requirejs = function (deps, callback, errback, optional) { return context.require(deps, callback, errback); intakeDefines(); takeGlobalQueue(); context.nextTick(function () {
說明: 產生一個宏任務4函數;require函數結束
onScriptLoad: function (evt) { var data = getScriptData(evt); context.completeLoad(data.id); shim = getOwn(config.shim, moduleName) || {}, takeGlobalQueue(); callGetModule([moduleName, (shim.deps || []), shim.exportsFn]);
說明: require函數結束後執行
八、宏任務4同宏任務2
說明: deps變爲 要加載的依賴