依傍RequireJs的用法写一个低配版的模块加载器

学RequireJs的用法写一个低配版的模块加载器


Contents

  1. 前言
  2. 回顾RequireJs的着力用法
  3. 实现原理
  4. 使方法
  5. 总结

前言


前段时间一直怀念就此单页开发技术写一个和谐的民用网站(使用es2015),写了同一组成部分后,发现单页应用为只出一个页面,所以首先次等加载index.html时虽如下载所有js文件,并且为好管理各个组成部分的状态,需要划分页面的逐一功能区为顺序模块,es2015本身是无支持有模块规范的(比如AMD、CMD、CommonJs等),所以不得不如此效仿实现:

  // global
  var spa = (function(){...})();

  // module blog
  spa.blog = (function(){
    ...
    return {
      do1: do1,
      do2: do2,
    };
  })();

  // module model
  spa.model = (function(){...})();

  // module shell
  spa.model = (function(){...})();

又逐一模块之间又有有据关系,在index.html里面写script标签来载入模块时要写很多只,同时也如依据依赖关系来规定书顺序,页面逻辑混乱,如下:

  <script type="text/javascript" src="/javascripts/spa.utils.js"></script>
  <script type="text/javascript" src="/javascripts/spa.model.js"></script>
  <script type="text/javascript" src="/javascripts/spa.mock.js"></script>
  <script type="text/javascript" src="/javascripts/spa.chat.js"></script>
  <script type="text/javascript" src="/javascripts/spa.blog.js"></script>
  <script type="text/javascript" src="/javascripts/spa.action.js"></script>
  <script type="text/javascript" src="/javascripts/spa.shell.js"></script>

事先用了RequireJs(一个兴的JavaScript模块加载器),它是为此以及构js的架构来描写的,所以node.js环境下为能够用。我思念自己得品味一下写一个低配版的js模块加载器
requireJs-nojsja
来应付一下自我之单页网站,当然只是约模仿了根本作用。

回顾RequireJs的基本用法


  1. 配置模块信息

  requirejs.config({
      baseUrl: '/javascripts',  // 配置根目录
      paths: {
        moduleA: 'a.js',
        moduleB: 'b.js',
        moduleC: 'c.js',
      },
      shim: {  // 配置不遵循amd规范的模块
        moduleC: {
          exports: 'log',
          deps: ['moduleA']
        }
      },
  });
  1. 概念一个模块

  define(name, ['moduleA', 'moduleB'], function(a, b){
    ...
    return {
      do: function() {
        a.doSomething();
        b.doAnother();
      }
    };
  });
  1. 引用一个模块

  // 引用模块
  require(['moduleA', 'moduleB'], function(a, b) {
    a.doSomething();
    b.doAnother();
  });

兑现原理


  1. config方法确定各个模块的指关系

  /* 记录模块访问地址和模块的依赖等信息 */
  Require.config({
    baseUrl: '/javascripts/',
    paths: {
      'moduleA': './moduleA.js',  // 相对于当前目录
      'moduleB': '/javascripts/moduleB.js',  // 不使用baseUrl
      'moduleC': 'moduleC.js',

      'moduleD': {
        url: 'moduleD.js',
        deps: ['moduleE', 'moduleF'],
      },
      ...
    },
    shim: {
      'moduleH': {
        url: 'moduleH.js',
        exports: 'log',
      },
    }
  });
  1. 数据要过程分析

(1) config配置模块信息经常并无会见硌网络要
(2)
在index.js主入口文件里采取require方法引用多独模块时,根据config配置文件构造一下具备模块的依分析树。按深度优先或广度优先来遍历这个仗树,将装有因按照依赖顺序放上一个累组,最后进行反复组去再处理,因为见面产出负还的图景

  var dependsTree = new Tree('dependsTree');
  var dependsArray = [];
  var dependsFlag = {};  // 解决循环依赖

  // 创建树
  setDepends(depends, dependsTree);
  // 得到依赖数组
  sortDepends(dependsArray, dependsTree);
  // 数据去重
  arrayFilter(dependsArray);

  return dependsArray;

(3)
创建XHR对象异步下载数组里面的有所js文件,按照依赖顺序依次解析js代码,解析完成后点发回调函数,回调函数里传开各个模块的援

  // ajax下载代码文件
  Utils.request(url, 'get', null, function(responseText){
    // 暂时保存
    _temp[module_name] = responseText;
  });

  // 文件下载完成后eval解析代码
  array.map(function(jsText){
    ...
    eval(jsText);
    ...
  });

  // 调用回调函数
  callback.apply(null, [dep1, dep2, dep3]);

使用方式


详细说明: github
README.md

总结


  1. 下载js代码时自所以了ajax来促成,所以对于跨域文件与CDN会有点问题,这个好更改成为创script标签,指定标签src,最后以document.head.appendChild(script),这样来化解,其它的像使用XMLHttpRequest
    2.0,iframe等啊可的,可以尝试一下。
  2. 解析代码时自己用了eval的办法,这个eval在JavaScript里面是许多说纷纭,可以省这个,如果是为此了上面创建script标签的章程吧,就甭自己eval了。
  3. 察觉一个bug,存在循环依赖时,代码会报错,还没有夺化解。RequireJs是如此处理的:模块a依赖b,同时b依赖a,这种状态下b的模块函数被调用时,被传的a是undefined,所以要好于b里面手动require一下a。