github地址:
https://github.com/requirejs/requirejs
好的文章:https://www.jianshu.com/p/8687f539642c
require.js 是一个JavaScript文件和模块加载器,也是模块管理工具
requireJs 作用是什么,优势是什么? 它解决什么难题?
1. 有效防止了命名冲突
2. 声明了不同 JS 文件之间的依赖关系
3. JS代码以模块化的 方式组织。
requireJs 为解决前端代码库的组织难题,它的解决方案是:
模块化组织js文件
异步加载js文件
JavaScript模块化编程
JavaScript模块化编程的目的是为了让开发者 仅需实现核心的业务逻辑,其他的都加载别人写好的模块就行。
但是JavaScript并不是模块化的语言,ES6才正式支持类和模块,之前的版本不支持类,更不用说module.
什么是模块?
模块的原始写法:
将不同函数及变量简单的放在一起,看作一个模块。
var a = xxx;
function func1(){...}
function func2(){...}
缺点是:污染全局变量,无法保证 不与其他模块发生命名冲突。而且不能清晰的看出模块成员之间关系;
模块的对象写法
将模块定义为一个对象,所有模块成员放在对象里边。
//将属性和操作都封装在对象中
var module = new Object({
_prop:11,
func1:function(){...}
func2:function(){...}
})
//使用时直接调用对象的属性;
module.func1();
确定:暴露了模块成员,内部状态 可以被外部改写。
module._prop = 100;
立即执行函数写法:
IIFE( Immediately-Invoked Function Express) 可以达到不暴露私有成员的目的
var module = (function(){
var _prop = 0;
var fn1 = function(){...};
var fn2 = function(){...};
return {fn1:fn1, fn2:fn2};
})();
放大模式/ 继承模式 (augmentation).
如果一个模块很大,必须被分成几个部分,或者一个模块需要继承另外一个模块。
var module = (function(mod){
mod.fn = function(){...};
return mod;
})(module);
宽放大模式(Loose Augmentation): 放大模式的改进
浏览器环境中 各个模块 通常都是通过网络获取的。采用放大模式,第一个执行的部分 可能使用一个不存在的空对象,
此时需要要使用宽放大模式
var module = (function(mod){
mod.fn = function(){...};
return mod;
})(window.module || {});
如何保证模块的独立性?如何使用全局变量(其他模块)?
独立性是模块的重要特点,模块内部最好不要与程序的其他模块直接交互。为了在模块内部使用全局变量,
必须显示地将其他变量 作为参数输入模块
var module = (function($){
})(jQuery);
AMD规范
Js目前没有官方规范,通用的JS 模块规范有2种,CommonJS和AMD
CommonJS
老实说在浏览器环境下,没有模块并不是特别大的问题,毕竟网页的复杂性有限。但是对于服务端编程,
一定要有模块,否则无法编程。
2009年,美国程序员Ryan Dahl创建了NodeJS项目,将JS用于服务端编程。由此标志着JS模块化编程的正式诞生。
NodeJS的模块系统是参照CommonJS规范实现的,在CommonJS中有一个全局方法require()
,用于加载模块。
var match = require("math");
match.add(1,2)
由于一个重大的局限,使得CommonJS规范不适用于浏览器环境。问题是对于服务器而言模块都放在本地,可同步加载等待时间只是硬盘读取时间。但是当浏览器中使用服务端的模块时,等待时间取决于网速快慢,长时间的等待会造成浏览器处于“假死”状态。
因此浏览器端的 模块 不能使用“同步加载(synchronous)", 只能采用“异步加载(asynchronous)"方式,
这就是AMD规范诞生的背景。
AMD (Asynchronous Module Definition) 异步模块加载。
所有依赖模块的语句都定义在一个回调函数中,等到加载完成后,回调函数才会执行。
AMD也采用了require()
语句加载模块,不同于CommonJS的是,它要求两个参数。
// module参数为一个数组,里面的成员是要加载的模块
// callback参数是模块加载成功后执行的回调函数
require([module], callback)
// math模块与math.add()加载不是同步的,浏览器不会发生假死,因此AMD比较适合浏览器环境。
require(["math"], function(math){
math.add(1, 2);
});
RequireJS
加载资源文件
<script src="https://cdn.bootcss.com/require.js/2.3.5/require.js"></script>
在引入require.js文件之后,整个windows 对象就有 require()方法。可以通过require()方法来加载其他JS文件。
RequireJS的入口是 引入时指定的data-main属性,在require.js引入后,会自动执行指向data-main属性所指定的入口文件。
由于引入requireJS本身可能会造成页面失去响应,解决的方式可将其放在网页底部加载。或使用延迟加载。
<script src="./assets/scripts/require-2.3.5.js" data-main="js/main" async="true" defer></script>
主模块:
data-main
加载的是主模块
baseUrl 可通过 requirejs.config手动设置,若没有显示指定config及data-main,则默认的baseUrl为包含requireJS的
那个html页面所属的目录。
$ vim js/main.js
/**
* RequireJS全局配置文件
*/
requirejs.config({
//设置项目路径,项目会以baseUrl作为相对路径去查找模块文件
baseUrl:"./js",
//预加载JS文件的配置项,默认可不用添加.js后缀
paths:{
//RequireJS默认假定所有的依赖资源都是JS脚本,因此无需再module ID上再加上js后缀。
jquery:"../scripts/jquery-3.3.1"
}
});
正常情况下,主模块是依赖其他模块的,此时要使用AMD规范定义的require()函数。
require([module], function(module){...});
/**
* RequireJS全局配置文件
*/
require.config({
//设置项目路径,项目会以baseUrl作为相对路径去查找模块文件
baseUrl:"./js",
//预加载JS文件的配置项,默认可不用添加.js后缀
paths:{
//RequireJS默认假定所有的依赖资源都是JS脚本,因此无需再module ID上再加上js后缀。
jquery:"https://cdn.bootcss.com/jquery/3.3.1/jquery",
bootstrap:"https://cdn.bootcss.com/bootstrap/4.1.1/js/bootstrap"
}
});
require(['jquery', 'bootstrap'],function($, undefined){
});
RequireJS要求每个模块是一个单独的JS文件,如果多加载几个模块会发出多次HTTP请求,
实际上,虽然部分的函数库符合AMD规范,但更多的库并不符合。
RequireJS 如何加载非规范的模块呢?
在使用require()
之前,需在require.config()
函数中定义非规范模块的特征。
require.config()
接收一个配置对象,此对象除了paths
属性之外,还有一个shim
属性,专门用来配置不兼容的模块。每个模块需要定义exports
值即输出的变量名,表明这个模块外部调用名称。其次deps
数组属性表明该模块的依赖性。
RequireJS源码解析
requireJS工作流程
- 载入模块
- 通过模块名解析出模块信息并计算出URL
- 通过创建
script
的形式将模块加载到页面 - 判断被加载脚本若存在依赖则加载,若不存在则直接执行
factory()
。 - 等待所有脚本都加载完毕后执行回调函数