RequireJS & SeaJS

模块化开发 开发以前,都是直接在页面上引入 script 标签来引用脚本的,当项目变得比较复杂,就会带来很多问题。

  • JS项目中的依赖只有通过引入JS的顺序来确定,项目会变得复杂难以维护。
  • 复杂的脚本会暴露很多全局变量, 比如 $,_. ... 。
  • 同步加载的时候,网页会停止渲染,加载时间越长,网页失去响应的事件就越长。

于是,AMD 规范就诞生了,AMD 即为异步模块定义,有效避免同步加载导致页面的假死现象。RequireJS 是一个 AMD 的实现,后来还有CMD规范,玉伯的 而SeaJS 是 CMD 的一个实现,两者最大的区别就是

RequireJS 是预加载,而sea.js 是懒加载,也就是按需加载

什么意思 ??

比如下面的代码

define(function(require,exports,module){
// do something
var mod1 = require('./mod1');
// do something
var mod2 = require('./mod2');
})

RequireJS 会全部找到这个模块的依赖,并在开始执行是就加载全部的依赖,

而SeaJS 则是按需加载,直到遇到 require 才会加载。

RequireJS 内部通过 Function.prototype.toString() ,然后使用正则匹配所有的require 方法,将其转化为

define(['./mod1',./mod2']function(mod1,mod2)

这种方式。

有很多关于这两种方式的争吵,由于我没有做过较大的项目,所以对于这两种方式在正真项目中使用的区别也不清楚。

回顾 RequireJS

主要接口有两个: require & define,define 是模块的定义方法,require 是模块的使用方法。

define 的参数为 define (id?,deps?,factory)。第一个为模块ID,第2个为依赖列表,第三个是工厂方法 。如果不定义ID,则为匿名方法,通常情况,模块ID 等于模块在工程的路径。deps 和 factory 有约定,deps 数组有多少个元素,factory 就会有多少个形参,形参对于与依赖模块工厂函数执行后的返回值。

define ("id",["mod1","mod2"],function(mod1,mod2){
return {
///
};
})

require 方法和 define 基本一致。

文件目录

└─ use-require/
├─ app/
│ ├─ js/
│ │ ├─ module/
│ │ │ ├─ a.js
│ │ │ └─ b.js
│ │ ├─ main.js
│ └─ lib/
│ │ └─ require.js
├─ index.html
└─ readme.md

现在,需要在 index.html 引导整个程序 。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head> <body>
<script src="./app/lib/require.js" data-main="./app/js/main">
</script>
</body>
</html>

引入 requirejs,并通过 data-main 申明 启动 js 文件。

注意: data-main 指向的文件所在路径就是 baseUrl,在这里就是app/js ,

baseUrl 也可以通过require.config 设置 。如果没有通过 data-main 属性指定 baseUrl ,也没有通过config的方式显示声明 baseUrl ,那么 baseUrl 默认为加载requirejs的那个页面所在的路径

下面 b 依赖 a ,那么就应该写 module/a,而 main 依赖 b ,写为 module/b ,这个路径会和 baseUrl 拼接,与a ,b 所在的路径无关 。有时候,你可能需要生成一个相对于模块的URL地址。你可以将require作为一个依赖注入进来,然后调用require.toUrl()以生成该URL 。

define(["require"], function(require) {
var url = require.toUrl("./style.js");

});

有时候你想避开"baseUrl + paths"的解析过程,而是直接指定加载某一个目录下的脚本。此时可以这样做:如果一个module ID符合下述规则之一,其ID解析会避开常规的"baseUrl + paths"配置,而是直接将其加载为一个相对于当前HTML文档的脚本:

注意是 相对于 HTML 文档,因为 js脚本是会被插入到 html 文档中执行的 。

  • 以 ".js" 结束 。
  • 以 "/" 开始。
  • 包含 URL 协议, 如 "http:" or "https:"。

一般来说,最好还是使用baseUrl及"paths" config去设置module ID。它会给你带来额外的灵活性,如便于脚本的重命名、重定位等。 同时,为了避免凌乱的配置,最好不要使用多级嵌套的目录层次来组织代码,而是要么将所有的脚本都放置到baseUrl中,要么分置为项目库/第三方库的一个扁平结构,如下:

└─ project/
├─ js/
│ ├─ app/
│ │ └─ sub.js
│ ├─ lib/
│ │ ├─ jquery.js
│ │ └─ require.js
│ └─ app.js
├─ index.html
└─ readme.md

下面自然就顺利成章

申明模块a

define(function() {
'use strict';
return {
name: "hello , i am a"
}
})

模块 b

define(["module/a"], function(a) {
'use strict';
var aName = a.name;
var name = "hello i am b";
return {
sayHello: function() {
console.log(name + " my brother is " + aName);
}
}
})

在模块b 里面依赖模块a 。

main.js

require(["module/b"], function(b) {
'use strict';
b.sayHello();
})

看看效果

如果使用 require.config() 配置呢?

将上面的目录复制一份,命名 use-require-config,目录结构完全一致

main.js

requirejs.config({
baseUrl: './app/js/module'
}) require([".b"], function(b) {
'use strict';
b.sayHello();
})

在这里,将配置放在 main.js 里面,等会儿会讲如果不放在它里面会又什么问题。

模块 b 依赖 于 a

define(["./a"], function(a) {
'use strict';
var aName = a.name;
var name = "hello i am b";
return {
sayHello: function() {
console.log(name + " my brother is " + aName);
}
}
})

由于将baseUrl 设置为 app/js/module ,所以这里依赖a 就可以直接写a

在这里,我使用 ./a, 与直接写 a 一模一样,还是使用 baseUrl+ moduleName `来寻找模块路径。

如果不将 requirejs.config 放在main中,而是另外在引入一个script 节点来放置 呢

<body>
<script src="./app/lib/require.js" data-main="./app/js/main">
</script>
<script>
requirejs.config({
baseUrl: './app/js/module'
})
</script>
</body>

在浏览器运行

发现报错了,怎么回事呢?

看它寻找 main.js 的路径

file:///E:/HFLib/module/code/use-require-config/app/js/module/main.js

是 baseUrl + main.js。 这说明,配置的 baseUrl 覆盖了为 data-main 配置的路径

也就是说,一旦使用requirejs.config 来配置 baseUrl,那么所有的路径都会以 baseUrl 为基准。

所以,data-main 应改为: ..\main,注意这里必须要使用反斜杠,正斜杠拼接总是会出现问题。

通过这样就可以解决 data-main 和 requirejs.config() 的冲突了 。

paths 和 shim 。

当模块名过长是,require.js 为我们提供了路径 paths 的方式 。

理想状况下,每个加载的脚本都是通过define()来定义的一个模块;但有些"浏览器全局变量注入"型的传统/遗留库并没有使用define()来定义它们的依赖关系,你必须为此使用shim config来指明它们的依赖关系。

这是我的文件目录

index.html

<body>
<script src="./app/lib/require.js" data-main="./app/main">
</script>
</body>

main.js

requirejs.config({
paths: {
jQuery: 'lib/jQuery'
},
shim: {
'jQuery': {
exports: '$'
}
}
}) require(['jQuery'], function($) {
'use strict';
console.log($);
})

可以看到,已经顺利注入了 jQuery 依赖。

require.js 中还有很多配置和用法,我用的就比较少,就简单介绍到这里。

下来,我要实现一个简化的模块加载器,类似于 require.js.

实现一个自己的模块加载器

太晚了 。

就在 实现一个自己的模块加载器(二)中实现一个类似 require.js 的Demo 吧 。

使用RequireJS并实现一个自己的模块加载器 (一)的更多相关文章

  1. 使用RequireJS并实现一个自己的模块加载器 (二)

    2017 新年好 ! 新年第一天对我来说真是悲伤 ,早上兴冲冲地爬起来背着书包跑去实验室,结果今天大家都休息 .回宿舍的时候发现书包湿了,原来盒子装的牛奶盖子松了,泼了一书包,电脑风扇口和USB口都进 ...

  2. 【模块化编程】理解requireJS-实现一个简单的模块加载器

    在前文中我们不止一次强调过模块化编程的重要性,以及其可以解决的问题: ① 解决单文件变量命名冲突问题 ② 解决前端多人协作问题 ③ 解决文件依赖问题 ④ 按需加载(这个说法其实很假了) ⑤ ..... ...

  3. 实现一个类 RequireJS 的模块加载器 (二)

    2017 新年好 ! 新年第一天对我来说真是悲伤 ,早上兴冲冲地爬起来背着书包跑去实验室,结果今天大家都休息 .回宿舍的时候发现书包湿了,原来盒子装的牛奶盖子松了,泼了一书包,电脑风扇口和USB口都进 ...

  4. 一个简单的AMD模块加载器

    一个简单的AMD模块加载器 参考 https://github.com/JsAaron/NodeJs-Demo/tree/master/require PS Aaron大大的比我的完整 PS 这不是一 ...

  5. js 简易模块加载器 示例分析

    前端模块化 关注前端技术发展的各位亲们,肯定对模块化开发这个名词不陌生.随着前端工程越来越复杂,代码越来越多,模块化成了必不可免的趋势. 各种标准 由于javascript本身并没有制定相关标准(当然 ...

  6. RequireJS 是一个JavaScript模块加载器

    RequireJS 是一个JavaScript模块加载器.它非常适合在浏览器中使用, 它非常适合在浏览器中使用,但它也可以用在其他脚本环境, 就像 Rhino and Node. 使用RequireJ ...

  7. SeaJS:一个适用于 Web 浏览器端的模块加载器

    什么是SeaJS?SeaJS是一款适用于Web浏览器端的模块加载器,它同时又与Node兼容.在SeaJS的世界里,一个文件就是一个模块,所有模块都遵循CMD(Common Module Definit ...

  8. 构建服务端的AMD/CMD模块加载器

    本文原文地址:http://trock.lofter.com/post/117023_1208040 . 引言:  在前端开发领域,相信大家对AMD/CMD规范一定不会陌生,尤其对requireJS. ...

  9. JavaScript AMD 模块加载器原理与实现

    关于前端模块化,玉伯在其博文 前端模块化开发的价值 中有论述,有兴趣的同学可以去阅读一下. 1. 模块加载器 模块加载器目前比较流行的有 Requirejs 和 Seajs.前者遵循 AMD规范,后者 ...

随机推荐

  1. NYOJ 1007

    在博客NYOJ 998 中已经写过计算欧拉函数的三种方法,这里不再赘述. 本题也是对欧拉函数的应用的考查,不过考查了另外一个数论基本定理:如何用欧拉函数求小于n且与n互质所有的正整数的和. 记eule ...

  2. 发布:.NET开发人员必备的可视化调试工具(你值的拥有)

    1:如何使用 1:点击下载:.NET可视化调试工具 (更新于2016-12-29 19:11:00) (终于彻底兼容了部分VS环境下无法使用的问题) 2:解压RAR后执行:CYQ.VisualierS ...

  3. C++中的事件分发

    本文意在展现一个C++实现的通用事件分发系统,能够灵活的处理各种事件.对于事件处理函数的注册,希望既能注册到普通函数,注册到事件处理类,也能注册到任意类的成员函数.这样在游戏客户端的逻辑处理中,可以非 ...

  4. Android数据存储之Android 6.0运行时权限下文件存储的思考

    前言: 在我们做App开发的过程中基本上都会用到文件存储,所以文件存储对于我们来说是相当熟悉了,不过自从Android 6.0发布之后,基于运行时权限机制访问外置sdcard是需要动态申请权限,所以以 ...

  5. [C#] 简单的 Helper 封装 -- SecurityHelper 安全助手:封装加密算法(MD5、SHA、HMAC、DES、RSA)

    using System; using System.IO; using System.Security.Cryptography; using System.Text; namespace Wen. ...

  6. SVD奇异值分解的基本原理和运用

    SVD奇异值分解: SVD是一种可靠的正交矩阵分解法.可以把A矩阵分解成U,∑,VT三个矩阵相乘的形式.(Svd(A)=[U*∑*VT],A不必是方阵,U,VT必定是正交阵,S是对角阵<以奇异值 ...

  7. jQuery.template.js 简单使用

    之前看了一篇文章<我们为什么要尝试前后端分离>,深有同感,并有了下面的评论: 我最近也和前端同事在讨论这个问题,比如有时候前端写好页面给后端了,然后后端把这些页面拆分成很多的 views, ...

  8. 深入理解javascript的getTime方法

    1.理解getTime getTime() 方法返回一个时间的格林威治时间数值. 可以使用这个方法把一个日期时间赋值给另一个Date 对象. 语法: dateObj.getTime() 参数: 无. ...

  9. css样式之background详解

    background用法详解: 1.background-color 属性设置元素的背景颜色 可能的值 color_name            规定颜色值为颜色名称的背景颜色(比如 red) he ...

  10. BPM助力企业数字化转型

    自九十年代末,流程管理开始引入国内,至今已经有20多年的历史了,由最初的部门级应用向企业级应用转变,大家的认知也经历了一系列的发展变化.不同阶段的信息化水平对企业的流程以及BPM平台也提出了不同的需求 ...