实现简易版requireJS

179 阅读2分钟

RequireJS:javascript的模块加载器,非常适合在浏览器中使用(兼容性强)

思考:如何实现define 、module、require(模块名加载、远程资源加载、nodejs端加载)

先熟悉requireJs的使用方法

第一步

//在页面中引入require.js,指定主文件main.js
<script src="js/require.js" data-main="js/main"></script>

第二步

//vue.js 定义vue模块,以及依赖的javascript模块
define('vue',['javascript'],function (){
    return orange
});

//javascript.js 定义javascript模块,无依赖
define('javascript',[],function (){
    return { 
        name:'js', 
        type:'脚本语言', 
        content: '学无止尽', 
    }
});

第三步

//在main文件中,使用定义好的vue模块
require(['vue'], function (vm){
    console.log(vm)
    //===>输出 
    { 
        name:'js', 
        type:'脚本语言', 
        content: '学无止尽', 
    }
});

猜测运行过程

  1. 加载requireJs,其中定义了define、require等方法
  2. 取得script标签中的data-main属性
  3. 加载main.js文件
  4. 文件中的require方法请求vue模块,异步加载vue
  5. vue模块中异步加载javascript模块
  6. math加载成功后,执行回调函数,打印出了javascript

函数

require:require(dep?,cb) 简易实现:

//标记已经加载成功的个数
var REQ_TOTAL = 0;
//模块导出
window.exports = {};
//记录各个模块的顺序
var exp_arr = [];
function require(arr, callback) {
    //默认为数组类型,且未处理路径问题
    var req_list = arr 
    var req_len = req_list.length;
  //模块逐个加载,动态加载script标签
    for(var i=0;i<req_len;i++) {
        var req_item = req_list[i];
        var $script = createScript(req_item, i);
        var $node = document.querySelector('head');
    (function($script) {
      //检测script 的onload事件
      $script.onload = function() {
        REQ_TOTAL++;
        var script_index = $script.getAttribute('index');
        exp_arr[script_index] = exports;
        window.exports = {};
        //所有链接加载成功后,执行callback,并传入导出的exports数组
        if(REQ_TOTAL == req_len) {
          callback && callback.apply(exports, exp_arr);
        }
      }
      $node.appendChild($script);
    })($script);
  }
}
//创建一个script标签,用index标记顺序
function createScript(src, index) {
  var $script = document.createElement('script');
  $script.setAttribute('src', src);
  $script.setAttribute('index', index);
  return $script;
}

define:define(name?,[]? , callback);

这个name可以省掉,默认是文件名称;也可以自定义,一旦定义了name,define函数内部其实就是把这个name以及依赖模块、回调函数作为一个对象存储在全局的数组当中,也就是 defQueue.push([name,deps,callback]);这个name就是这个组件注册的的ID。并且通过defQueueMap对象以id为key建立对应的映射关系。

    //对象字面量,存储对应的module
    var moudules={};
    function define(name,deps,callback){
        for(var i=0;i<deps.length;i++){
        //modules存在则直接取
            deps[i]=moudules[deps[i]]; 
        }
        //name为id调用callback方法并缓存
        moudules[name]=callback.apply(callback,deps);
        console.log(moudules[name]);
    }
    //获取module的方法
    function get(name){
        return moudules[name];
    }
    return {
        define:define,
        get:get
    };

待优化

1.定义模块映射表,也就是require.config
2.循环依赖的问题:将依赖放入一颗全局依赖树,进行去重操作