java script require_模仿RequireJs的用法实现一个低配版的模块加载器

Contents

前言

回顾RequireJs的基本用法

实现原理

使用方法

总结

前言

前段时间一直想用单页开发技术写一个自己的个人网站(使用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标签来载入模块时需要写很多个,同时也要根据依赖关系来确定书写顺序,页面逻辑混乱,如下:

之前用过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']

}

},

});

2. 定义一个模块

define(name, ['moduleA', 'moduleB'], function(a, b){

...

return {

do: function() {

a.doSomething();

b.doAnother();

}

};

});

3. 引用一个模块

// 引用模块

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',

},

}

});

2. 数据请求请求过程分析

(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

总结

下载js代码时我用了ajax来实现,所以对于跨域文件和CDN会有点问题,这个可以改成创建script标签,指定标签src,最后将document.head.appendChild(script),这样来解决,其它的诸如使用XMLHttpRequest 2.0,iframe等也可以的,可以实验一下。

解析代码时我用了eval的方法,这个eval在JavaScript里面是众说纷纭,可以看看这个,如果是用了上面创建script标签的方法的话,就不用自己eval了。

发现一个bug,存在循环依赖时,代码会报错,还没去解决。RequireJs是这样处理的:模块a依赖b,同时b依赖a,这种情况下b的模块函数被调用时,被传入的a是undefined,所以需要自己在b里面手动require一下a。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值