博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
RequireJS源码初探
阅读量:6619 次
发布时间:2019-06-25

本文共 4776 字,大约阅读时间需要 15 分钟。

前两天跟着叶小钗的博客,看了下RequireJS的源码,大体了解了其中的执行过程。不过在何时进行依赖项的加载,以及具体的代码在何处执行,还没有搞透彻,奈何能力不够,只能先记录一下了。

RequireJS的初探

看源码从头开始看,肯定是不切实际的。按照叶小钗的方法,是从data-main开始的,所以我们也从那里开始把!

首先,页面会有一段js标签,会去加载requirejs:

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里面。
本文转自博客园xingoo的博客,原文链接:,如需转载请自行联系原博主。
你可能感兴趣的文章
哈尔滨—越南芽庄国际航线成功首航
查看>>
国家药品集中采购方案出炉 医保局:药品降价不影响制造成本
查看>>
面向中国,Google软件开发代理商计划正式开启报名
查看>>
Vue2.0数据通信详解
查看>>
从零收拾一个hybrid框架(二)-- WebView容器基础功能设计思路
查看>>
逐步学习什么是递归?通过使用场景来深入认识递归。
查看>>
Money 20/20 | 未来金融数字化转型:数字化半径与全栈式战略观
查看>>
蚂蚁金服首席数据科学家漆远:AI技术开放,与业界融合共创
查看>>
J2Cache二级缓存'没有自动更新'
查看>>
玩转iOS开发:装逼技术RunTime的应用(三)
查看>>
从基础概念到实现,小白如何快速入门PyTorch
查看>>
用深度学习自动生成HTML代码
查看>>
渔人和Rxjs的故事,这次一定教会你前端必会的Rxjs
查看>>
Android彻底组件化demo发布
查看>>
Flutter http请求库dio 1.0正式发布
查看>>
[Scikit-learn教程] 02.05 综合实践
查看>>
接手前端新项目?这里有些注意点你可能需要留意一下
查看>>
【火炉炼AI】深度学习005-简单几行Keras代码解决二分类问题
查看>>
nodejs 中的依赖管理
查看>>
[译] 5 个可以立刻在你的 Ionic App 中用上的动画包
查看>>