require.config

require.config设置require.js模板载入选项

    // 定义config
req.config = function (config) {
return req(config);
};
// 载入config配置项
req = requirejs = function (deps, callback, errback, optional) {
var context, config,
contextName = defContextName; // 假设deps是config对象
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;
}
// 获取默认的context
context = getOwn(contexts, contextName);
if (!context) {
context = contexts[contextName] = req.s.newContext(contextName);
}
// 配置模块载入选项
if (config) {
context.configure(config);
}
// 当deps为config时,此调用木有实质作用
return context.require(deps, callback, errback);
};
// newContext中定义configure
configure: function (cfg) {
// 确保baseUrl以/结尾
if (cfg.baseUrl) {
if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') {
cfg.baseUrl += '/';
}
} var shim = config.shim,
objs = {
paths: true,
bundles: true,
config: true,
map: true
}; // 将cfg中的配置信息合并到默认的context的config中
eachProp(cfg, function (value, prop) {
if (objs[prop]) {
if (!config[prop]) {
config[prop] = {};
}
mixin(config[prop], value, true, true);
} else {
config[prop] = value;
}
}); //Reverse map the bundles
if (cfg.bundles) {
eachProp(cfg.bundles, function (value, prop) {
each(value, function (v) {
if (v !== prop) {
bundlesMap[v] = prop;
}
});
});
} // 对shim进行对应处理保存于默认context的config中
if (cfg.shim) {
eachProp(cfg.shim, function (value, id) {
// 规范化shim结构
// e.g. jquery-datepicker: ['jquery'] -> jquery-datepicker: {deps: ['jquery']}
if (isArray(value)) {
value = {
deps: value
};
}
// value.exports: 非requirejs定义文件的载入modulename,用于推断是否载入成功
// value.init: 是否存在初始化工作
if ((value.exports || value.init) && !value.exportsFn) {
value.exportsFn = context.makeShimExports(value);
}
shim[id] = value;
});
config.shim = shim;
}
...
}

综上:require.config将模块载入选项进行对应处理后,保存于默认的context.config中

require()

require()载入模块文件,

e.g.

require(['jquery'], function() {
...
});
req = requirejs = function (deps, callback, errback, optional) {

        var context, config,
contextName = defContextName; ... // 获取默认的context
context = getOwn(contexts, contextName);
if (!context) {
context = contexts[contextName] = req.s.newContext(contextName);
} ...
// 通过环境变量载入模块,newContext倒数几行代码中写到context.require = context.makeRequire()
return context.require(deps, callback, errback);
};
makeRequire: function (relMap, options) {
options = options || {}; function localRequire(deps, callback, errback) {
var id, map, requireMod; ... context.nextTick(function () {
// 将之前已define()定义的模块载入进来
intakeDefines();
// 创建require中默认的Module
requireMod = getModule(makeModuleMap(null, relMap)); ... // 初始化require信息,载入deps依赖项
requireMod.init(deps, callback, errback, {
enabled: true
});
// 根据config中waitSeconds来检查js载入是否超时。waitSeconds为0,无载入超时机制
// checkLoaded採用setTimeout,若未载入完即50ms后检查再次调用checkLoaded推断是否载入完成
checkLoaded();
}); return localRequire;
}
// Module中init的定义
init: function (depMaps, factory, errback, options) { ... if (options.enabled || this.enabled) {
// 将deps依赖项进行处理变为Module,进行载入
this.enable();
} else {
this.check();
}
}
enable: function () {
// 对depMaps的全部依赖项进行处理
each(this.depMaps, bind(this, function (depMap, i) {
var id, mod, handler; if (typeof depMap === 'string') { ... on(depMap, 'defined', bind(this, function (depExports) {
if (this.undefed) {
return;
}
// 跟进defineDep能够看到。将dep.exports保存在this.depExports中。用于载入后回调函数的參数传递,这是按顺序来的,回调函数调用详见以下check()中
this.defineDep(i, depExports);
this.check();
})); ... // 检查每一个Module是否初始化、载入完模块
this.check();
}
check: function () {
if (!this.enabled || this.enabling) {
return;
} var err, cjsModule,
id = this.map.id,
depExports = this.depExports,
exports = this.exports,
factory = this.factory; if (!this.inited) {
// 获取模块所对应的js文件,源代码跟踪fetch()发现实际是调用了context.load即req.load
this.fetch();
} else if (this.error) {
this.emit('error', this.error);
} else if (!this.defining) {
// deps依赖项已载入完成。准备回调
if (this.depCount < 1 && !this.defined) {
if (isFunction(factory)) { // 通过context.execCb调用回调函数,将刚保存的depExports的參数传入回调函数中。完成模块的依赖注入
if ((this.events.error && this.map.isDefine) ||
req.onError !== defaultOnError) {
try {
exports = context.execCb(id, factory, depExports, exports);
} catch (e) {
err = e;
}
} else {
exports = context.execCb(id, factory, depExports, exports);
}
} ...
req.load = function (context, moduleName, url) {
var config = (context && context.config) || {},
node;
// 是浏览器还是Webworker
if (isBrowser) {
// 通过 document.createElement('script')创建script节点,同一时候设置node.async = true实现异步载入
node = req.createNode(config, moduleName, url); node.setAttribute('data-requirecontext', context.contextName);
node.setAttribute('data-requiremodule', moduleName);
// 根据的浏览器的不同加入script的load、error事件
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);
}
// 设置script的src
node.src = url; currentlyAddingScript = node;
if (baseElement) {
head.insertBefore(node, baseElement);
} else {
head.appendChild(node);
}
currentlyAddingScript = null; return node;
} else if (isWebWorker) {
try {
// WebWorker採用importScripts载入js文件
importScripts(url); context.completeLoad(moduleName);
} catch (e) {
context.onError(makeError('importscripts',
'importScripts failed for ' +
moduleName + ' at ' + url,
e,
[moduleName]));
}
}
}

综上:require()就是对须要的依赖模块进行对应的url、是否在path、或存在shim等的处理后转换为Module。

载入js文件有以下两种方式:

Browser:document.createElement创建script,然后通过设置asyn=true。head.appendChild(node)

WebWorker:importScripts(url)

define()

define = function (name, deps, callback) {
var node, context; // 对实參不同情况做对应处理
... if (!deps && isFunction(callback)) {
deps = [];
// 通过toString()然后正則表達式将define()定义的依赖项保存在deps中
if (callback.length) {
callback
.toString()
.replace(commentRegExp, '')
.replace(cjsRequireRegExp, function (match, dep) {
deps.push(dep);
}); ...
}
} ... // 将define定义的信息存放在默认context.defQueue或globalDefQueue中,等定义define()的js文件载入完后。在通过来处理这些define信息
// 上面require()中js文件载入过程分析。可知js文件载入后将会调用事件函数onScriptLoad
(context ? context.defQueue : globalDefQueue).push([name, deps, callback]);
}
onScriptLoad: function (evt) {
// 对文件载入的状态进行推断
if (evt.type === 'load' ||
(readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) { interactiveScript = null; // 将script的加入的事件函数进行清除,同一时候获取node的信息
var data = getScriptData(evt);
// 通过默认context来载入该data所对应的Module
context.completeLoad(data.id);
}
}
completeLoad: function (moduleName) {
var found, args, mod,
shim = getOwn(config.shim, moduleName) || {},
shExports = shim.exports; // 将globalDefQueue合并到context.defQueue中
takeGlobalQueue(); // 找到moduleName所对应的的Module
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;
} // 获取args模块
callGetModule(args);
} ...
}
function callGetModule(args) {
// 若该模块没有被载入。即载入该模块,然后调用Module.init(),这个过程在require()已分析,即初始化、载入模块了
if (!hasProp(defined, args[0])) {
getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]);
}
}

综上:define()模块定义,仅将模块信息载入到默认context.defQueue或者globalDefQueue中,而处理这些信息是在定义define()的js文件载入完后进行的。

总体过程

  • 载入require.js库文件,生产默认的context
  • 查找data-main入口点,require.config()将模块载入选项做对应处理,然后加入默认的context中
  • 载入require()依赖项,通过document.createElement和设置asyn或者importScripts来载入js文件
  • 载入过程中发现define()模块定义。将模块定义信息载入到默认的context.defQueue或者globalDefQueue中,等定义define()的js文件载入后再处理这些模块定义信息

    当中:

  • js文件若未载入成功,採用setTimeout 50ms方式来循环推断是否载入完成

  • define()的依赖项提取採用function.toString() + 正則表達式
  • url的转换、Module的生产等处理过程可详见源代码

require.js源代码解密完成,是不是认为原来就这样实现的= =

事实上非常多事情并没有那么难。仅仅要你去分析它就会清楚非常多

小小吐槽:

require.js源代码代码规范、结构等感觉有点乱…

看过后仅仅想说”呵 呵”

解密javascript模块载入器require.js的更多相关文章

  1. JavaScript模块载入框架sea.js 学习一

    简单总结sea.js 学习 文件文件夹结构   /sea/sea.js      下载地址  http://seajs.org/docs/#downloads   /sea/jquery-sea.js ...

  2. JavaScript中模块化工具require.js

    什么是require.js? RequireJS是一个非常小巧的JavaScript模块载入框架,是AMD规范最好的实现者之一.它还同时可以和其他的框架协同工作,使用RequireJS必将使您的前端代 ...

  3. Javascript模块化编程:require.js的用法

    摘自:http://blog.jobbole.com/30046/ 这个系列的第一部分和第二部分,介绍了Javascript模块原型和理论概念,今天介绍如何将它们用于实战. 我采用的是一个非常流行的库 ...

  4. Javascript模块化编程(三)require.js的用法及功能介绍

    这个系列的第一部分和第二部分,介绍了Javascript模块原型和理论概念,今天介绍如何将它们用于实战.我采用的是一个非常流行的库require.js感兴趣的朋友可以了解下啊 我采用的是一个非常流行的 ...

  5. Javascript模块化编程-require.js

    转自:https://www.cnblogs.com/digdeep/p/4607131.html Javascript模块化编程(一):模块的写法 随着网站逐渐变成"互联网应用程序&quo ...

  6. JavaScript模块化编程之require.js与sea.js

    为什么要模块化:当今,网站以不再是一个简单的页面,JavaScript也不再是做一些简单的脚本验证,随着WEB.20时代到来,前端工程师面临的必将是越来越庞大的JavaScript代码,越来越复杂的内 ...

  7. javascript模块化编程库require.js的用法

    随着javascript的兴起,越来越多的公司开始将JS模块化,以增加开发的效率和减少重复编写代码的.更是为了能更加容易的维护日后的代码,因为现在的随着人们对交互效果的越来越强烈的需求,我们的JS代码 ...

  8. Javascript模块化工具require.js教程

    转自:http://www.w3cschool.cc/w3cnote/requirejs-tutorial-1.html, http://www.w3cschool.cc/w3cnote/requir ...

  9. Javascript模块化编程(三):require.js的用法

    Javascript模块化编程(三):require.js的用法 原文地址:http://www.ruanyifeng.com/blog/2012/11/require_js.html 作者: 阮一峰 ...

随机推荐

  1. qt介绍

    http://www.oschina.net/p/qt Qt 是一个跨平台的C++图形用户界面应用程序框架.它提供给开发者建立图形用户界面所需的功能,广泛用于开发GUI程序,也可用于开发非GUI程序. ...

  2. centos 6.5安装GitLab全过程和问题记录

    GitLab,是一个使用 Ruby on Rails 开发的开源应用程序,与Github类似,能够浏览源代码,管理缺陷和注释,非常适合在团队内部使用. 官方只提供了Debian/Ubuntu系统下的安 ...

  3. Linux内核中常见内存分配函数(一)

    linux内核中采 用了一种同时适用于32位和64位系统的内存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系 统中,用到了四级页表. * 页全局目录(Page Global Dir ...

  4. C# NameValueCollection集合 .

    案例: NameValueCollection nameValueCollection = Request.Params;//获得连接地址中的所有参数 //获取各个参数,eg:            ...

  5. jquery第四期:对象转换的小实例

    为了更清晰的看懂jquery对象是一个数组,我们这一期来改变每一个<li>中的值,在前面加上序号. 代码如下: <!DOCTYPE html PUBLIC "-//W3C/ ...

  6. bootstrap注意事项(六)按钮

    1.预定义样式 使用下面列出的类可以快速创建一个带有预定义样式的按钮 <!DOCTYPE HTML><html><head> <link rel=" ...

  7. java enum的用法

    原始的常量定义: public static fianl MON=“Mon”; public static final TUE="Tue"; 语法(定义) 创建枚举类型要使用 en ...

  8. Why Study JavaScript?

    JavaScript is one of the 3 languages all web developers must learn: 1. HTML to define the content of ...

  9. MySQL AUTO_INCREMENT 简介

    可使用复合索引在同一个数据表里创建多个相互独立的自增序列,具体做法是这样的:为数据表创建一个由多个数据列组成的PRIMARY KEY OR UNIQUE索引,并把AUTO_INCREMENT数据列包括 ...

  10. verilog中阻塞复制,非阻塞复制,顺序块,并行块之间的关系

    这几个概念是不一样的 顺序块:顺序块中的语句是按顺序执行的,每条语句中的延迟值是与其前一条语句执行的仿真时间有关. 并行块:并行块语句是并行执行的,它里面的每条语句中指定的延迟值都是相对于语句块开始执 ...