RequireJS源码初探
前两天跟着叶小钗的博客,看了下RequireJS的源码,大体了解了其中的执行过程。不过在何时进行依赖项的加载,以及具体的代码在何处执行,还没有搞透彻,奈何能力不够,只能先记录一下了。
RequireJS的初探
看源码从头开始看,肯定是不切实际的。按照叶小钗的方法,是从data-main开始的,所以我们也从那里开始把!
首先,页面会有一段js标签,会去加载requirejs:
<script data-main="test.js" src="lib/require.js"></script>
Requirejs中,代码是一个自执行的方法:
var requirejs,require,define;
(function(global){
})(this);
源码中,主要是定义了三个全局的变量——requirejs,require,define,下面是一个自执行的方法。
那么主要就是看看这个方法里面都干了什么吧!
RequireJS主体方法
//定义环境变量
//定义各种方法
//检查requirejs,require,define
//核心部分
function newContext(){}//定义核心部分方法
req = requirejs = function(){//定义req
//...
return context.require();
};
req.config = function(){};
req({});//创建默认的上下文
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;
};
//洋洋洒洒,加载代码
req.load = function(){
node = req.createNode(config, moduleName, url);//创建节点
node.addEventListener('load', context.onScriptLoad, false);//添加load事件
if (baseElement) {//插入到head里面
head.insertBefore(node, baseElement);
} else {
head.appendChild(node);
}
};
if (isBrowser && !cfg.skipDataMain) {
//加载main.js
}
define = function(){};
req.exec =function(){};
req(cfg);//执行配置文件
上面的代码中,关键的方法定义其实只有两个:
- 定义了newContext()方法,用于配置上下文环境,并且仅会执行一次!后续都是使用同一个context!
- 定义req,它是后续使用的方法!
然后在上面的代码中,它做了下面三件事:
- 1 执行req({}),传入了空的对象,初始化context
- 2 if(isBrowser && xxxx)....,加载data-main所指向的js,读取配置
- 3 执行req(cfg),执行刚刚读取的配置,加载目标模块...
基本上就是这个套路了!
newContext()
RequireJS最精彩的部分,就在这个方法里面了!
function newContext(contextName){
function getModule(depMap) {
var id = depMap.id,
mod = getOwn(registry, id);
if (!mod) {
mod = registry[id] = new context.Module(depMap);
}
return mod;
}
function checkLoaded() {
}
context = {
//...
makeRequire: function (relMap, options) {
//核心
function localRequire(deps, callback, errback) {
//真正的核心
context.nextTick(function () {
intakeDefines();
requireMod = getModule(makeModuleMap(null, relMap));//主要看这里吧
requireMod.skipMap = options.skipMap;
requireMod.init(deps, callback, errback, {
enabled: true
});
checkLoaded();
});
}
return localRequire;
},
load: function (id, url) {
req.load(context, id, url);
},
onScriptLoad : function() {
context.completeLoad();
},
completeLoad : function() {
takeGlobalQueue();
while(defQueue.length){
}
mod = getOwn(registry, moduleName);
checkLoaded();
}
//...
}
Module.prototype = {
init : function(depMaps, factory, errback, options){
if (options.enabled || this.enabled) {
this.enable();
} else {
this.check();
}
},
fetch : function(){
if (this.shim) {//依赖
context.makeRequire(this.map, {
enableBuildCallback: true
})(this.shim.deps || [], bind(this, function () {
return map.prefix ? this.callPlugin() : this.load();
}));
} else {
//Regular dependency.
return map.prefix ? this.callPlugin() : this.load();//是否包含前缀 text!xxx
}
},
load: function () {
var url = this.map.url;
//Regular dependency.
if (!urlFetched[url]) {
urlFetched[url] = true;
context.load(this.map.id, url);
}
},
check : function(){
this.fetch();
},
enable : function(){
this.check();
}
};
context.require = context.makeRequire();//其实是把localRequire赋值给context.require
return context;
};
这个newContext()里面定义大量的加载模块、校验、检查等工作。可以看到这个方法,主要是定义了一个context对象和Module方法。
然后执行这个方法后,会自动调用context对象的makeRequire()方法,这个makeRequire实际上调用的又是内部定义的localRequire()。LocalRequire则是处理加载任务的核心——比如依赖的检查,模块的加载等等。
执行点
req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) {
setTimeout(fn, 4);
} : function (fn) { fn(); };
所有的加载都会交由这个nextTick执行,暂时没有搞清楚...
流程图
收获
- 1 原来RequireJS加载模块的时候,是检查data-main属性,然后去加载目标js。
- 2 加载到目标模块后,会按照它的依赖关系,进行加载,并且每个模块仅会加载一次。
- 3 加载模块的时候,会绑定一个load事件,当加载完会触发事件,执行该js
- 4 脚本实际上是通过创建了页面的script元素,然后添加到head里面。
RequireJS源码初探的更多相关文章
- Catalyst揭秘 Day2 Catalyst源码初探
Catalyst揭秘 Day2 Catalyst源码初探 这节课从源码角度来讲catalyst. 首先有一个观点要澄清,就是技术不是越底层就越是性能和效率更高.因为除了指令执行性能以外,更重要的是架构 ...
- Servlet源码初探
年底,公司的事情告一段落,就来捣鼓一下这个Servlet源码,为下一步的spingmvc源码初探做准备 1.Servlet接口 public interface Servlet { void init ...
- requireJS 源码(二) data-main 的加载实现
(一)requireJs 的整体结构: requireJS 源码 前192行,是一些 变量的声明,工具函数的实现 以及 对 三个全局变量(requirejs,require,define)若被占用后的 ...
- 从 RequireJs 源码剖析脚本加载原理
引言 俗话说的好,不喜欢研究原理的程序员不是好的程序员,不喜欢读源码的程序员不是好的 jser.这两天看到了有关前端模块化的问题,才发现 JavaScript 社区为了前端工程化真是煞费苦心.今天研究 ...
- requirejs源码分析: requirejs 方法–2. context.require(deps, callback, errback);
上一篇 requirejs源码分析: requirejs 方法–1. 主入口 中的return context.require(deps, callback, errback); 调用的是make ...
- RequireJs 源码解读及思考
写在前面: 最近做的一个项目,用的require和backbone,对两者的使用已经很熟悉了,但是一直都有好奇他们怎么实现的,一直寻思着读读源码.现在项目结束,终于有机会好好研究一下. 本文重要解读r ...
- 【一起学源码-微服务】Feign 源码一:源码初探,通过Demo Debug Feign源码
前言 前情回顾 上一讲深入的讲解了Ribbon的初始化过程及Ribbon与Eureka的整合代码,与Eureka整合的类就是DiscoveryEnableNIWSServerList,同时在Dynam ...
- 【requireJS源码学习01】了解整个requireJS的结构
前言 现在工作中基本离不开requireJS这种模块管理工具了,之前一直在用,但是对其原理不甚熟悉,整两天我们来试着学习其源码,而后在探寻其背后的AMD思想吧 于是今天的目标是熟悉requireJS整 ...
- 【requireJS源码学习02】data-main加载的实现
前言 经过昨天的学习,我们大概了解到了requireJS的主要结构,这里先大概的回顾一下 首先从总体结构来说,require这里分为三块: ① newContext之前变量声明或者一些工具函数 ② n ...
随机推荐
- protobuff 配合 libevent 在Linux 和windows 下的使用
protobuff 配合 libevent 在Linux 和windows 下的使用待补全. libprotobuf.lib libproto-lite.lib libprotoc.lib
- Spring MVC实例(增删改查)
数据库配置文件application-context-jdbc.xml <?xml version="1.0" encoding="UTF-8"?> ...
- Decorator(装饰)-对象结构型模式
1.意图 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator模式相比生成子类更为灵活. 2.别名 包装器 Wrapper. 3.动机 给某个对象而不是整个类添加一些功能.一个较为灵 ...
- centos 下添加epel源
来源于http://www.centoscn.com/CentOS/config/2014/0920/3793.html,收录备用 0.安装yum优先级插件 yum install yum-prior ...
- Centos7 安装MPlayer过程详解
使用自带的totem基本上都无法播放视频,各种格式都不支持,令人无语.想到了MPlayer,为了看片,决定编译安装,过程真的是折腾.如图是自带的Totem播放提示,安装了解码还是无法播放,反正要找其他 ...
- Segmetation fault你来的真不是时候
问题是这样的,今天一个简单的C程序,用gcc编译成汇编语言后,本来想在里面改点东西,结果运行时就报了"Segmetation fault".它丫来的还真不是时候,刚好最近正好烦它呢 ...
- 云计算和大数据时代网络技术揭秘(十二)自定义网络SDN
软件定义网络——SDN SDN是网络技术热点,即软件定义网络,OpenFlow是实现SDN思想的一个框架标准, open是指公开.开放,具体为控制平面的规则由各个通信厂家自定义变为公开的技术标准, f ...
- Uploadify 上传文件插件详解
Uploadify 上传文件插件详解 Uploadify是JQuery的一个上传插件,实现的效果非常不错,带进度显示.不过官方提供的实例时php版本的,本文将详细介绍Uploadify在Aspnet中 ...
- WebStorm License Activation (WebStorm许可证激活)
User or company name(用户或公司名称): EMBRACE License key(许可证密钥): ===== LICENSE BEGIN =====89374-12042010 ...
- 2004FBI树
题目描述 Description 我们可以把由“0”和“1”组成的字符串分为三类:全“0”串称为B串,全“1”串称为I串,既含“0”又含“1”的串则称为F串. FBI树是一种二叉树[1],它的结点类型 ...