一个简单的AMD模块加载器
一个简单的AMD模块加载器
参考
https://github.com/JsAaron/NodeJs-Demo/tree/master/require
PS Aaron大大的比我的完整
PS
这不是一个通用的模块加载器, 实际上只是require define 函数的实现
所以每个模块的js都要引入到html文件中
如何防止变量冲突呢?
实际上就是用函数把他们包起来
(function(exp){
var mod = {
name:'modA'
};
exp[mod.name] = mod;
})(exp);
exp是一个全局变量 保存所有的模块 然后require一个模块的时候实际上就是去exp这个全局变量中找
AMD是如何使用模块的呢
//定义一个模块
define('a', function(){
console.log('this is mod A');
return {
name: 'a'
}
});
//调用一个模块
require('a',function(a){
console.log(a.name)
})
require('a', function(a){
console.log(a.name + ' 2 ');
})
Step1
var require; //两个全局量
var define;
(function() {
var modules = {};
require = function(id, callback) {
if (!modules[id]) {
throw "module " + id + " not found";
}
if (callback) {
var module = build(modules[id]);
callback(module)
return module;
}
};
define = function(id, factory) { //模块名,模块本身
if (modules[id]) {
throw "module " + id + " 模块已存在!";
}
modules[id] = {
id: id,
factory: factory
};
}
//module {id: '' factory: function(){...}} //factory的function就是一个模块function中的内容
function build(module) {
var factory = module.factory,
id = module.id;
module.exports = factory() || {};
return module.exports; //模块return的结果
}
})();
那么这样一来 上面require的执行结果是
this is mod A
a
a.js:2 this is mod A
a 2
模块确实是加载了 但是模块A执行了两次
Step2
那么凡是require过的模块 下次再次require 应该从保存的地方取出来而不是再次执行factory
需要一个全局变量 pushStack = {} 来保存
function build(module) {
var factory = module.factory,
id = module.id;
if(pushStack[id]){
return pushStack[id];
}
module.exports = factory() || {};
pushStack[id] = module.exports;
return module.exports; //模块return的结果
}
Step3 require增加多依赖支持
有了多依赖支持就可以这样做
require(['a', 'b'], function(a,b){
console.log(a.name);
console.log(b.name);
});
改造require就可以了
require = function(id, callback) {
if(Object.prototype.toString.call(id) == '[object Array]'){
var ids = id, allMod = {};
ids.forEach(function(id){
allMod[id] = build(modules[id]);
});
callback.apply(null, allMod);
}else{
if (!modules[id]) {
throw "module " + id + " not found";
}
if (callback) {
var module = build(modules[id]);
callback(module)
return module;
}
}
};
Step4
有时候模块本身存在依赖 比如
define('c',['a','b'],function(a,b){
});
那么define的时候就要对参数做判断 如果是三个参数表示是有依赖的模块
var require; //两个全局量
var define;
(function() {
var modules = {},pushStack = {};
//require的时候才是真的执行模块
require = function(id, callback) {
if(Object.prototype.toString.call(id) == '[object Array]'){
var ids = id, allMods = [];
ids.forEach(function(id){
allMods.push(build(modules[id]));
});
callback.apply(null, allMods);
}else{
if (!modules[id]) {
throw "module " + id + " not found";
}
if (callback) {
var module = build(modules[id]);
callback(module)
return module;
}
}
};
//define的时候是把模块存放起来 存到modules[]中
//define的参数 若是2个 那么是 模块名,模块本身, 若有三个参数 则是 模块名,依赖列表,模块本身
define = function(id) {
var deps, factory;
if (modules[id]) {
throw "module " + id + " 模块已存在!";
}
if(arguments.length > 2){
deps = arguments[1];
factory = arguments[2];
modules[id] = {
id: id,
deps: deps,
factory: factory
};
}else{
factory = arguments[1];
modules[id] = {
id: id,
factory: factory
};
}
}
function build(module) {
var factory = module.factory,
id = module.id;
if(pushStack[id]){
return pushStack[id];
}
if(module.deps){
var deps = [];
module.deps.forEach(function(id){
deps.push(build(modules[id]));
});
module.exports = factory.apply(null,deps);
}else{
module.exports = factory() || {};
}
pushStack[id] = module.exports;
return module.exports; //模块return的结果
}
})();
如何加入CMD支持
完成上面,就可以通过下面的方式定义模块
define('a',function(){
});
define('c',['a','b'],function(a,b){
});
require('a','b',function(a,b){}})
在Seajs中CMD类似于这样
define('c', function(require, exports, module) {
var a = require('a')
var b = require('b');
});
实际上就是改造require (require的时候才是执行模块的定义函数)
require原本都是接受2个参数 依赖名以及一个函数 现在require可以只接受一个参数
var require; //两个全局量
var define;
(function() {
var modules = {},pushStack = {};
//require的时候才是真的执行模块
require = function(id, callback) {
if(Object.prototype.toString.call(id) == '[object Array]'){
var ids = id, allMods = [];
ids.forEach(function(id){
allMods.push(build(modules[id]));
});
callback.apply(null, allMods);
}else{
if (!modules[id]) {
throw "module " + id + " not found";
}
if (callback) {
var module = build(modules[id]);
callback(module);
return module;
}else{
return build(modules[id]);
}
}
};
//define的时候是把模块存放起来 存到modules[]中
//define的参数 若是2个 那么是 模块名,模块本身, 若有三个参数 则是 模块名,依赖列表,模块本身
define = function(id) {
var deps, factory;
if (modules[id]) {
throw "module " + id + " 模块已存在!";
}
if(arguments.length > 2){
deps = arguments[1];
factory = arguments[2];
modules[id] = {
id: id,
deps: deps,
factory: factory
};
}else{
factory = arguments[1];
modules[id] = {
id: id,
factory: factory
};
}
}
function build(module) {
var factory = module.factory,
id = module.id;
if(pushStack[id]){
return pushStack[id];
}
if(module.deps){
var deps = [];
module.deps.forEach(function(id){
deps.push(build(modules[id]));
});
module.exports = factory.apply(module,deps);
}else{
module.exports = factory(require, module.exports, module) || {};
}
pushStack[id] = module.exports;
return module.exports; //模块return的结果
}
})();
之后就可以这样通过CMD的方式引入模块了
define('c', function(require, exports, module) {
var a = require('a');
var b = require('b');
return {
name: 'c' + a.name + b.name
}
});
一个简单的AMD模块加载器的更多相关文章
- 实现简单的 JS 模块加载器
实现简单的 JS 模块加载器 1. 背景介绍 按需加载是前端性能优化的一个重要手段,按需加载的本质是从远程服务器加载一段JS代码(这里主要讨论JS,CSS或者其他资源大同小异),该JS代码就是一个模块 ...
- JavaScript AMD 模块加载器原理与实现
关于前端模块化,玉伯在其博文 前端模块化开发的价值 中有论述,有兴趣的同学可以去阅读一下. 1. 模块加载器 模块加载器目前比较流行的有 Requirejs 和 Seajs.前者遵循 AMD规范,后者 ...
- 实现一个类 RequireJS 的模块加载器 (二)
2017 新年好 ! 新年第一天对我来说真是悲伤 ,早上兴冲冲地爬起来背着书包跑去实验室,结果今天大家都休息 .回宿舍的时候发现书包湿了,原来盒子装的牛奶盖子松了,泼了一书包,电脑风扇口和USB口都进 ...
- 【模块化编程】理解requireJS-实现一个简单的模块加载器
在前文中我们不止一次强调过模块化编程的重要性,以及其可以解决的问题: ① 解决单文件变量命名冲突问题 ② 解决前端多人协作问题 ③ 解决文件依赖问题 ④ 按需加载(这个说法其实很假了) ⑤ ..... ...
- 小矮人Javascript模块加载器
https://github.com/miniflycn/webkit-dwarf 短小精悍的webkit浏览器Javascript模块加载器 Why 我们有许多仅基于webkit浏览器开发的应用 无 ...
- 使用RequireJS并实现一个自己的模块加载器 (一)
RequireJS & SeaJS 在 模块化开发 开发以前,都是直接在页面上引入 script 标签来引用脚本的,当项目变得比较复杂,就会带来很多问题. JS项目中的依赖只有通过引入JS的顺 ...
- 构建服务端的AMD/CMD模块加载器
本文原文地址:http://trock.lofter.com/post/117023_1208040 . 引言: 在前端开发领域,相信大家对AMD/CMD规范一定不会陌生,尤其对requireJS. ...
- 使用RequireJS并实现一个自己的模块加载器 (二)
2017 新年好 ! 新年第一天对我来说真是悲伤 ,早上兴冲冲地爬起来背着书包跑去实验室,结果今天大家都休息 .回宿舍的时候发现书包湿了,原来盒子装的牛奶盖子松了,泼了一书包,电脑风扇口和USB口都进 ...
- SeaJS:一个适用于 Web 浏览器端的模块加载器
什么是SeaJS?SeaJS是一款适用于Web浏览器端的模块加载器,它同时又与Node兼容.在SeaJS的世界里,一个文件就是一个模块,所有模块都遵循CMD(Common Module Definit ...
随机推荐
- c# 另存为excel
去网上找了一下 看了一个比较简单的新建excel然后另存为. 要引用Microsoft.Office.Interop.Excel命名空间,如果没有的话 ,百度比我懂. 直接付代码: Microsof ...
- eclipse 使用maven 创建springmvc + mybatis
接着eclipse 使用maven 创建纯spring mvc项目 毕竟项目都要访问数据库的, 所以加上mybatis的支持也就是网上大多时候说的 SSM框架的搭建(Spring + Spring M ...
- 转 jquery插件--241个jquery插件—jquery插件大全
241个jquery插件—jquery插件大全 jquery插件jqueryautocompleteajaxjavascriptcoldfusion jQuery由美国人John Resig创建,至今 ...
- GitHub问题之恢复本地被删除的文件
折腾了真久,GitHub commit之后,我手痒把本地的一个文件给删了,然后一直Git pull都发现不能恢复.远程库里面还是有该文件的.就是我想将远程库的文件回到本地被删除了的位置. 特别的是,我 ...
- Linux01--文件管理,常用命令 权限管理
一.Ø文件系统 1.Linux文件系统特点 • Linux文件系统为单根的树状结构 •文件系统根为”/” •文件名大小写敏感,除了”/”都是可用字符文件名以”.”开始的为隐藏文件 •文件路径使 ...
- block,inline,inline-block
block元素的特点是: 总是在新行上开始: 高度,行高以及顶和底边距都可控制: 宽度缺省是它的容器的100%,除非设定一个宽度 <div>, <p>, <h1>, ...
- [问题解决] VNC连接黑屏或者灰屏+命令行
1.修改配置文件/root/.vnc/xstartup 注:有的系统配置里有 –nolisten tcp 和 –nohttpd ,这两个是阻止Xwindows登陆和HTTP方式VNC登陆的,如果需要图 ...
- iOS中如何呼出另一个应用
我们经常会遇到在一个应用里面呼出另一个应用的需求,比如在文档里面点击地址,调用safari来打开网页:比如在文件浏览器里面点击某种文件,自动激活一个应用来打开文件. iOS里面对于这样的需求使用URL ...
- docker端口映射设置
摘自 Docker学习笔记(3)-- 如何使用Dockerfile构建镜像 # 映射一个端口 EXPOSE port1 # 相应的运行容器使用的命令 docker run -p port1 image ...
- Android中数据库的操作流程详解
Android中数据库的操作方法: 1.Android平台提供了一个数据库辅助类来创建或打开数据库. 这个辅助类继承自SQLiteOpenHelper类.继承和扩展SQLiteOpenHelper类主 ...