gulp requirejss 现在还有人玩吗

JavaScript(12)
今天打开项目群,老大问我们:谁的项目现在在用CommonJS,由于我前段时间接手的项目用到了require来引入新的模块,所以立马说我用到了。之后发现不对经,我好像用的RequireJS模块。越想越觉得模糊,索性在网上找了些文档看了下,下面是我个人的感受。
CommonJS:就是用来规范JS的使用的,主要为了JS在后端的表现制定的,不太适合前端。它定了三个模块:模块引用的require,模块定义的exports和模块标识module。
AMD:是一种异步加载模块的方式,主要为前端JS的表现制定规范。只有一个模块define(id?,dependencies?,factory);
CMD:玉伯写了seaJS,就是遵从他提出的CMD规范。
关于RequireJS和SeaJS的区别和联系。
联系:两者都是js模块的加载器,倡导一种模块化开发的理念。
A.&&&& 定位差别。RequireJS不仅想成为浏览器端模块加载器,同时也设计后端的node部分;SeaJS则专注于WEB前端的。
B.&&&&& 遵循规范。RequireJS遵循AMD的异步加载;SeaJS遵循CMD的模式,更加简洁。
C.&&&&& 社区理念。RequireJS尝试让第三方库修改一满足它。而SeaJS则是自主封装来适应。
D.&&&& RequireJS的bug不明显,SeaJS若存在bug会很明显。
E.&&&&& 调试的支持。SeaJS通过插件实现一些RequireJS无法实现的功能。
F.&&&&&& 插件机制。RequireJS在源码中为插件预留接口;SeaJS开放自身,让插件开发者可以访问和修改,非常灵活。
CMD是在国内前端开发人员研究SeaJS的过程中发现的,它和AMD一样属于CommonJS规范的一种实现的定义,requireJS和seaJS是对应AMD和CMD的实践。
其中CMD的seaJS是一种按需加载的模式,定义一个模块的时候不需要立即制定模块之间的依赖模块,在需要用到的时候require就可以了;而AMD的requireJS相反,定义模块的时候需要定制依赖模块,并且所有的依赖模块都是先执行。
对应的seaJS在js程序中的执行顺序是按照顺序结构,从上到下一次加载,当遇到require某模块的时候再去调用某些模块。
对应的requireJS在js程序中的执行顺序是不确定的。在执行前,它会加载本模块依赖的其它模块,先将执行环境给构建好,然后在此基础之上再执行其他的语句。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:12921次
排名:千里之外
原创:21篇
(1)(3)(1)(3)(8)(1)(6)(1)(3)// define用以预定义模块,调用define方法时未完成js文件的加载,页面用script标签显示加载除外
// require方法直接加载js文件,同时完成加载define方法添加的父级依赖
// 模块的依赖通过Module.enable调用load或callPlugin完成加载,首先需要把相应模块推送到待加载对象registry中
// 该过程通过completeLoad调用takeGlobalQueue完成,将模块推送到待加载对象registry中
var requirejs, require,
(function (global) {
var req, s, head, baseElement, dataMain, src,
interactiveScript, currentlyAddingScript, mainScript, subPath,
= '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 = {},
globalDefQueue = [],// define方法添加的模块,模块以数组形式存储name,deps,callback
useInteractive =
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) {
for (i = 0; i & ary. i += 1) {
if (ary[i] && func(ary[i], i, ary)) {
// eachReverse反向遍历数组
function eachReverse(ary, func) {
if (ary) {
for (i = ary.length - 1; i & -1; i -= 1) {
if (ary[i] && func(ary[i], i, ary)) {
// 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) {
for (prop in obj) {
if (hasProp(obj, prop)) {
if (func(obj[prop], prop)) {
// 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 =/blog/=='object' && value &&
!isArray(value) && !isFunction(value) &&
!(value instanceof RegExp)) {
if (!target[prop]) {
target[prop] = {};
mixin(target[prop], value, force, deepStringMixin);
target[prop] =
// 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) {
// getGlobal(value)获取window对象的属性或方法,支持带.的value值
function getGlobal(value) {
if (!value) {
each(value.split('.'), function (part) {
g = g[part];
// makeError创建错误对象,错误对象中添加requireType、requireModules、originalError属性
function makeError(id, msg, err, requireModules) {
var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id);
e.requireType =
e.requireModules = requireM
if (err) {
e.originalError =
// 已经使用了另一个AMD模块化加载器,则不予加载requirejs插件;
if (typeof define !== 'undefined') {
// requirejs变量名已被使用,若非函数,cfg暂存该内容,require.js加载完成后重新赋值;
// 若为函数,不予加载requirejs插件;
if (typeof requirejs !== 'undefined') {
if (isFunction(requirejs)) {
requirejs =
// 因何不要作require是函数时返回、阻止require.js加载的处理???
//Allow for a require config object
if (typeof require !== 'undefined' && !isFunction(require)) {
//assume it is a config object.
function newContext(contextName) {
var inCheckLoaded, Module, context, handlers,
checkLoadedTimeoutId,
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) {
for (i = 0; i & ary. i++) {
part = ary[i];
if (part === '.') {
ary.splice(i, 1);
} else if (part === '..') {
if (i === 0 || (i === 1 && ary[2] === '..') || ary[i - 1] === '..') {
} else if (i & 0) {
ary.splice(i - 1, 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. i&0; i-=1 ){
nameSegment=nameParts.slice(0,i).join('/');
if ( baseParts ){
for ( j=baseParts. j&0; j-=1 ){
mapValue=/blog/getOwn(map, baseParts.slice(0,j).join('/'));
if ( mapValue ){
mapValue=/blog/getOwn(mapValue,nameSegment);
if ( mapValue ){
foundMap=mapV
break outerL
if ( !foundStarMap && starMap && getOwn(starMap,nameSegment) ){
foundStarMap=getOwn(starMap,nameSegment);
if ( !foundMap && foundStarMap ){
foundMap=foundStarM
foundI=starI;
if ( foundMap ){
nameParts.splice(0,foundI,foundMap);
name=nameParts.join('/');
// name指向config.pkgs的包名,替换为包下为main文件
pkgMain=getOwn(config.pkgs, name);
return pkgMain ? pkgMain :
// 当前上下文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);
// 加载模块符合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
// 以!分割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){
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);
normalizedName=name.indexOf('!')===-1 ?
normalize(name,parentName,applyMap) :
normalizedName=normalize(name,parentName,applyMap);
nameParts=splitPrefix(normalizedName);
prefix=nameParts[0];
normalizedName=nameParts[1];
isNormalized=
url=context.nameToUrl(normalizedName);
// name中带插件前缀,插件未加载,id中添加suffix
suffix=prefix && !pluginModule && !isNormalized ?
'_unnormalized'+(unnormalizedCounter+=1) : '';
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);
// 模块已完成加载,且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]);
mod = getModule(depMap);
if (mod.error && name === 'error') {
fn(mod.error);
mod.on(name, fn);
// 报错,或者直接调用errback回调函数,或者触发模块的error事件,或者直接抛出错误
function onError(err, errback) {
var ids = err.requireModules,// 数组形式,当前加载的模块
notified =
if (errback) {
errback(err);
each(ids, function (id) {
var mod = getOwn(registry, id);
if (mod) {
mod.error =
if (mod.events.error) {
notified =
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] =
defQueue.push(queueItem);
globalDefQueue = [];
// 依赖特殊模块require、exports、module,模块可以用commonjs风格书写
handlers = {
'require': function (mod) {// requirejs封装后的模块
if (mod.require) {
return mod.
return (mod.require = context.makeRequire(mod.map));
'exports': function (mod) {
mod.usingExports =
if (mod.map.isDefine) {
if (mod.exports) {
return (defined[mod.map.id] = mod.exports);
return (mod.exports = defined[mod.map.id] = {});
'module': function (mod) {
if (mod.module) {
return mod.
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.
if (mod.error) {
mod.emit('error', mod.error);
traced[id] =
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();
breakCycle(dep, traced, processed);
processed[id] =
// 模块符合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 =
if (inCheckLoaded) {
inCheckLoaded =
eachProp(enabledRegistry, function (mod) {
var map = mod.map,
modId = map.
if (!mod.enabled) {
if (!map.isDefine) {
reqCalls.push(mod);
if (!mod.error) {
if (!mod.inited && expired) {
// hasPathFallback加载模块符合config.map配置,先移除id名模块,通过context.makeReequire重新加载模块
if (hasPathFallback(modId)) {
usingPathFallback =
stillLoading =
noLoads.push(modId);
removeScript(modId);
} else if (!mod.inited && mod.fetched && map.isDefine) {
stillLoading =
if (!map.prefix) {// 加载失败的模块不予循坏加载,除非插件未获取成功
return (needCycleCheck = false);
if (expired && noLoads.length) {// 加载超时,且模块init方法未执行
err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads);
err.contextName = context.contextN
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();
inCheckLoaded =
Module = function (map) {
this.events = getOwn(undefEvents, map.id) || {};
this.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) {
this.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 =
this.inited =
this.ignore = options.
if (options.enabled || this.enabled) {// 模块已执行enable方法,则不予再次执行
this.enable();
this.check();
// 获取依赖模块的接口,数组形式存储在this.depExports中,供当前模块使用;this.depCount用以判断依赖是否加载完成
defineDep: function (i, depExports) {
if (!this.depMatched[i]) {
this.depMatched[i] =
this.depCount -= 1;
this.depExports[i] = depE
// 依赖没有通过init方法初始化(define方法加载的模块均不执行init方法,直接调用enable方法,require加载的模块则执行)
// 需要通过路径找到js文件完成加载,特别还需要通过shim转换为模块,或者经plugin处理
fetch: function () {
if (this.fetched) {
this.fetched =
context.startTime = (new Date()).getTime();
var map = this.
if (this.shim) {// 模块没有使用define声明依赖,shim用以声明依赖、以及导出函数名
context.makeRequire(this.map, {
enableBuildCallback: true
})(this.shim.deps || [], bind(this, function () {
return map.prefix ? this.callPlugin() : this.load();
return map.prefix ? this.callPlugin() : this.load();
// 通过context.load调用req.load方法加载js文件,并绑定载入成功、失败事件
load: function () {
var url = this.map.
if (!urlFetched[url]) {
urlFetched[url] =
context.load(this.map.id, url);
// 加载模块,并获得其exports接口
check: function () {
if (!this.enabled || this.enabling) {
var err, cjsModule,
id = this.map.id,
depExports = this.depExports,
exports = this.exports,
factory = this.
// 依赖没有通过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 =
if (this.depCount & 1 && !this.defined) {
if (isFunction(factory)) {
// context.execCb以exports为this关键字,depExports为传参,执行factory函数
exports = context.execCb(id, factory, depExports, exports);
} catch (e) {
// makeModuleMap方法中定性this.map.isDefine为携带name值
if (this.map.isDefine && exports === undefined) {
cjsModule = this.// 依赖了module模块,输出为module.exports
if (cjsModule) {
exports = cjsModule.
} else if (this.usingExports) {
exports = this.
if (err) {
if ((this.events.error && this.map.isDefine) || req.onError !== defaultOnError) {
err.requireMap = this.
err.requireModules = this.map.isDefine ? [this.map.id] :
err.requireType = this.map.isDefine ? 'define' : 'require';
return onError((this.error = err));
} else if (typeof console !== 'undefined' && console.error) {
console.error(err);
req.onError(err);
} else {// factory非函数直接作为exports输出
this.exports =
if (this.map.isDefine && !this.ignore) {// ignore属性对外不输出接口,比如jQuery插件
defined[id] =
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 =
this.defining =
if (this.defined && !this.defineEmitted) {// 触发defined插件,加载被依赖的模块
this.defineEmitted =
this.emit('defined', this.exports);
this.defineEmitComplete =
// 加载插件,并使用插件编译模块文本或加载模块
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 = normalizedM
this.init([], function () { }, 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();
// 符合config.bundles模块束情况,重新获取url后,加载模块
if (bundleId) {
this.map.url = context.nameToUrl(bundleId);
this.load();
// 插件中执行的回调函数,插件加载完成后加载模块,enabled=true时直接执行check方法
load = bind(this, function (value) {
this.init([], function () { }, null, {
enabled: true
// 为插件提供报错接口,以供调用
load.error = bind(this, function (err) {
this.inited =
this.error =
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 = useI
if (textAlt) {
text = textA
if (hasInteractive) {
useInteractive =
getModule(moduleMap);
if (hasProp(config.config, id)) {
config.config[moduleName] = config.config[id];
req.exec(text);
} catch (e) {
return onError(makeError('fromtexteval',
'fromText eval for ' + id +
' failed: ' + e,
if (hasInteractive) {
useInteractive =
this.depMaps.push(moduleMap);
pleteLoad(moduleName);
// 脚本作为依赖,load作为回调函数
localRequire([moduleName], load);
// 调用插件的load方法对当前模块进行改写,如处理less、txt、img等
plugin.load(map.name, localRequire, load, config);
context.enable(pluginMap, this);// 加载插件
this.pluginMaps[pluginMap.id] = pluginM
// 判断依赖或插件绑定加载完成defined事件(触发当前模块加载事件),并为当前模块提供接口
// 所有依赖加载完成标记为this.depCount==0,执行enable方法时随依赖数量+1,依赖defined完成时-1
enable: function () {
enabledRegistry[this.map.id] =
this.enabled =// 是否执行enable方法标记
this.enabling =// enable方法执行中标记
each(this.depMaps, bind(this, function (depMap, i) {
var id, mod,
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] = depM
handler = getOwn(handlers, depMap.id);// 依赖的模块中包含require、或exports、或module特殊模块
if (handler) {
this.depExports[i] = handler(this);
this.depCount += 1;// 所有依赖是否加载完成标记
// defined事件触发时获取依赖模块的接口,this.depCount标记-1,执行当前模块的check方法,在依赖的check方法中触发
on(depMap, 'defined', bind(this, function (depExports) {
if (this.undefed) {
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.
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 =
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) {
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);
node.removeEventListener(name, func, false);
// script节点加载完成,移除load、error事件,返回节点和模块名
function getScriptData(evt) {
var node = evt.currentTarget || evt.srcE
removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange');
removeListener(node, context.onScriptError, 'error');
node: node,
id: node && node.getAttribute('data-requiremodule')
// 获取并加载define方法添加的模块
function intakeDefines() {
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]));
callGetModule(args);
context.defQueueMap = {};
// require上下文,向外提供makeRequire接口供require方法使用,require方法执行时获取define方法定义的模块并加载
// 提供configure供requirejs配置
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:"_"
deps:["bar"],
exports:"Foo",
init:function(bar){
return this.Foo.noConflict();
* cfg.paths 映射模块的加载路径,"/"或"http:"取绝对路径,其余情况相对baseUrl
如cfg.paths={
jquery:"/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={
size:"large"
define(function(require,exports,module){// 加载依赖module,module.config()获取配置
var size=module.config().
* cfg.bundles 声明模块束,果一个JS文件包含多个模块
// jsUtil.js文件有两个模块"MathUtil","DateUtil"
cfg.bundles={jsUtil:["MathUtil","DateUtil"]}
* cfg.s 从包中加载模块
如cfg.packages=["cart","store"]
或cfg.packages=[
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,
eachProp(cfg,function (value,prop){
if ( objs[prop] ){
if ( !config[prop] ){
config[prop]={};
mixin(config[prop],value,true,true);
config[prop]=
if ( cfg.bundles ){// 反转模块束
eachProp(cfg.bundles,function(value,prop){
each(value,function(v){
if ( v!==prop ){
bundlesMap[v]=
if ( cfg.shim ){
eachProp(cfg.shim, function(value,id){
if ( isArray(value) ){
value=/blog/{
deps: value
if ( (value.exports || value.init) && !value.exportsFn ){
value.exportsFn=context.makeShimExports(value);
config.shim=
if ( cfg.packages ){// config.paths存储包文件夹所在路径,config.pkgs存储主模块路径
each(cfg.packages,function(pkgObj){
var location,
pkgObj=typeof pkgObj==='string' ? {name:pkgObj} : pkgO
name=pkgObj.
location=pkgObj.
if ( location ){
config.paths[name]=pkgObj.
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(){
if ( value.init ){
ret=value.init.apply(global,arguments);
return ret || (value.exports && getGlobal(value.exports));
// require方法调用makeRequire,获取define方法定义的模块并加载,执行require方法回调
makeRequire: function (relMap, options) {
options = options || {};
function localRequire(deps, callback, errback) {
var id, map, requireM
if (options.enableBuildCallback && callback && isFunction(callback)) {
callback.__requireJsBuild =
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);
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.skipM
requireMod.init(deps, callback, errback, {
enabled: true
// 通过调用Module.check方法创建script节点元素加载符合config.paths配置的cdn或http模块,加载依赖
checkLoaded();
return localR
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) {
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,
// 判断模块是否加载
defined: function (id) {
return hasProp(defined, makeModuleMap(id, relMap, false, true).id);
// 判断模块是否加载、待加载
specified: function (id) {
id = makeModuleMap(id, relMap, false, true).
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 =
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.
cleanRegistry(id);
return localR
// 依赖或插件顺序执行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.
// 将define加载的模块推送到registry待加载队列中
takeGlobalQueue();
while (defQueue.length) {
args = defQueue.shift();
if (args[0] === null) {
args[0] = moduleN
if (found) {
} else if (args[0] === moduleName) {
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 onError(makeError('nodefine',
'No define call for ' + moduleName,
[moduleName]));
// 没有用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 = pkgM
bundleId = getOwn(bundlesMap, moduleName);
if (bundleId) {
return context.nameToUrl(bundleId, ext, skipExt);
// 有js后缀,原路输出
if (req.jsExtRegExp.test(moduleName)) {
url = moduleName + (ext || '');
// url中替换config.paths别名
paths = config.
syms = moduleName.split('/');
for (i = syms. 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);
// 按baseUrl进行调整
url = syms.join('/');
url += (ext || (/^data\:|\?/.test(url) || skipExt ? '' : '.js'));
url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) +
// config.urlArgs添加url参数
return config.urlArgs ? url + ((url.indexOf('?') === -1 ? '?' : '&') + config.urlArgs) :
// 加载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 =
var data = /blog/getScriptData(evt);
pleteLoad(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 onError(makeError('scripterror', 'Script error for "' + data.id +
(parents.length ?
'", needed by: ' + parents.join(', ') :
'"'), evt, [data.id]));
context.require=context.makeRequire();
/*---------------------------------------------------------------------------------------------*/
// 配置requirejs,或者预加载模块、执行回调
req=requirejs=function(deps,callback,errback,optional){
var context,config,
contextName=defContextN
if ( !isArray(deps) && typeof deps!=='string' ){// 首参不是数组和字符串时,为配置项
if ( isArray(callback) ){
if ( config && config.context ){
contextName=config.
// 获取或创建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){
req.version =
req.jsExtRegExp=/^\/|:|\?|\.js$/;
req.isBrowser=isB
contexts:contexts,
newContext:newContext
// req添加'toUrl','undef','defined','specified'方法
'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.parentN
req.onError=defaultOnE
// 段创建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/script';
node.charset='utf-8';
node.async=
// 浏览器环境创建script节点加载模块文件,非浏览器环境通过importScripts函数加载js文件
req.load = function (context, moduleName, url) {
var config = (context && context.config) || {},
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 =
node.attachEvent('onreadystatechange', context.onScriptLoad);
node.addEventListener('load', context.onScriptLoad, false);
node.addEventListener('error', context.onScriptError, false);
node.src = /blog/
currentlyAddingScript =// 随后赋值为null,针对ie 6-8 的缓存问题
if (baseElement) {
head.insertBefore(node, baseElement);
head.appendChild(node);
currentlyAddingScript =
} else if (isWebWorker) {
importScripts(url);
pleteLoad(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 interactiveS
eachReverse(scripts(), function (script) {
if (script.readyState === 'interactive') {// 已加载状态
return (interactiveScript = script);
return interactiveS
// 未设置baseUrl配置,获取最末一个已加载的script节点(即当前执行的requirejs),其data-main属性作为baseUrl,并加载该文件模块
if (isBrowser && !cfg.skipDataMain) {
eachReverse(scripts(), function (script) {
if (!head) {
head = script.parentN
dataMain = script.getAttribute('data-main');
if (dataMain) {
mainScript = dataM
if (!cfg.baseUrl) {
src = /blog/mainScript.split('/');
mainScript = src.pop();
subPath = src.length ? src.join('/')
+ '/' : './';
cfg.baseUrl = subP
mainScript = mainScript.replace(jsSuffixRegExp, '');
if (req.jsExtRegExp.test(mainScript)) {
mainScript = dataM
//Put the data-main script in the files to load.
cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript];
// 定义模块,require方法时按需加载
define = function (name, deps, callback) {
if ( typeof name!=='string' ){
if ( !isArray(deps) ){
// deps依赖为空,callback有length属性时,获取模块中require方法加载的模块
// length为1时,添加依赖require;length为其他时,添加依赖require,exports,module
if ( !deps && isFunction(callback) ){
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
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] =
// define方法添加的模块默认填入globalDefQueue队列,通过takeGlobalQueue方法获取
globalDefQueue.push([name,deps,callback]);
define.amd={
jQuery:true
// 执行text脚本
req.exec=function(text){
return eval(text);}

我要回帖

更多关于 gulp requirejs 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信