一:为什么要使用requireJS?

很久之前,我们所有的JS文件写到一个js文件里面去进行加载,但是当业务越来越复杂的时候,需要分成多个JS文件进行加载,比如在页面中head内分别引入a.js,b.js,c.js等,如下所示:

<script src="js/app/a.js"></script>

<script src="js/app/b.js"></script>

 <script src="js/app/c.js"></script>

我们现在先在浏览器下看看这些请求,如下所示:

这样的写法有如下缺点:

1. 页面在加载的时候,是从页面自上往下加载及渲染的,当页面上有多个分散的js文件时候,页面会先加载及解析头部的JS文件(同步加载),页面被堵塞了,其次分散的js请求数多了,网页失去响应的时间就会变长。

2. 由于JS文件存在依赖关系,比如上面的b.js要依赖于a.js,所以务必保证a.js优先引入到页面上来且先加载,要严格保证加载顺序,依赖性最大的文件一定要放到最后加载。但是当依赖关系很复杂的时候,代码的编写和维护就会变得困难了。

当然上面引入JS时候,对于第1点:首先:我们可以放在底部去加载,把所有JS放在</body>之前去,这样就会解决了游览器堵塞的问题,其次我们可以把所有的JS文件打包成一个JS文件,但是依赖性(也就是顺序)我们还是没有办法解决掉,所以我们引入了requireJS。

二:使用requireJS的优点有哪些?

1.  实现JS文件的异步加载,避免网页被堵塞。

2.  管理模块之间的依赖性,便于代码的编写和维护。

requireJS基本语法及使用.

 1.  首先我们需要到官网下载最新版本的requireJS源码包。下载地址:

在页面头部head标签内引入requireJS,如下:<script src="js/require.js"></script>,但是加载这个文件也会造成网页失去响应,我们可以加上 defer 和 async这个属性。如下:

<script src="js/require.js" defer async="true" ></script>

Async属性表明文件需要异步加载,IE不支持这个属性,只支持defer,所以上面把这2个属性都加上。接下来,看看requireJS启动加载脚本的初始化方式,requireJS支持属性 data-main 这个属性来加载初始化的JS文件,如下:

<script src="js/require.js" defer async="true" data-main="js/app.js"></script>

上面的意思是:先异步加载requireJS文件,完成后继续异步加载app.js文件,假如app.js内容为空的话,我们可以看看加载顺序如下:

上面的app.js后的.js可以去掉,因为requireJS源码已经默认都是以后缀JS文件结尾的。

  2.  如何定义模块文件?

RequireJS编写模块不同于其他脚本文件,它良好的使用define来定义一个作用域避免全局空间污染,它可以显示出其依赖关系,并以函数(定义此模块的那个函数)参数的形式将这些依赖进行注入。

下面我们来看下demo,如下新建一个项目文件:

我们先在app/b.js下添加基本的requireJS代码如下:

// b.js

define(function(){

var add = function(x,y) {

return x + y;

};

return {

add : add

}

});

使用define来定义模块,下面我们需要在app.js里面来加载b.js模块,如下在app.js里面来调用了。

require(['app/b'], function (b){

console.log(b.add(1,1));

});

我们接着看看文件加载的情况如下:

在head标签内动态生成文件,如下:

可以看到加载顺序 requirejs --> app.js --> b.js。

上面的是函数式的定义如上面方式编写代码(使用define定义一个函数),我们还可以编写简单的键值对,直接返回一个对象(可以解决全局变量的理念),我们现在在a.js里面返回这么一个对象,如下:

// a.js

define(function () {

return {

color: "black",

size: "unisize"

}

});

在app.js初始化代码如下:

require(['app/a'],function(a){

console.log(a);

});

我们在控制台上可以看到如下:

直接返回一个对象,通过使用上面的方法我们可以想到可以解决全局变量概念,比如全局变量全部使用define函数包围,什么时候需要全局变量的话,直接require([‘XX’],function(XX){})这样调用下,同时所有的JS都是异步的,并不会堵塞加载。

3. AMD模块规范

第一种写法:

define(function() {

return {

mix: function(source, target) {

}

};

});

第二种写法 有依赖项 如下:

define(['data', 'ui'], function(data, ui) {

// init here

});

第三种写法 直接一个对象

define({

data: [],

ui: []

});

第四种写法 具名模块 如下:

define('index', ['data','base'], function(data, base) {

// todo

});

第五种写法 包装模块 如下:

define(function(require, exports, module) {

var base = require('base');

exports.show = function() {

// todo with module base

}

});

书写格式和nodeJS比较像,可以使用require获取模块,使用exports或者module.exports导出API。

当然requireJS是遵循AMD的规范的,所以一般情况下也具有上面的书写代码方式。

对于第四种写法 具名模块写法我们并不推荐的,因为不书写模块名我们一样可以调用,且在合并代码的时候,我们也可以根据代码自动生成模块名,如果我们现在写死了模块名,当某个时候,b.js我要移动到其他目录时候,JS也要跟着改,所以代码维护方面不好,所以不建议书写模块名。对于第五种写法,requireJS中也是支持的,通过内部调用require来处理依赖模块,我们也可以试着做demo看看就知道了,还是app.js,我想初始化a.js代码,我改成这样的方式,如下代码:

define(function(require, exports, module) {

var a = require('app/a');

console.log(a);

exports.show = function() {

// todo with module base

}

});

通过控制台也可以看到已经打印出 a 出来。

注意:1、 书写requireJS遵循一个文件一个模块。

2、 不要手动写模块名标示。

4. requireJS配置项如下:

1.baseUrl: 指定本地模块的基准目录,即本地模块的路径是相对于那个目录的,该属性通常有requireJS加载时的data-main属性指定。比如如下代码:

项目目录结构还是上面的。

在页面顶部<head>中引入 <script src="js/require.js" defer async="true" data-main="js/app"></script>

在app.js如下代码:

requirejs.config({

baseUrl: 'js/app'

});

requirejs(['a','b','c'],function(a,b,c){

});

在浏览器页面游览可以看到如下请求:

如上可以看到,index.html和js是同一个目录下的,都是放在requireJS文件夹里面的,所以定义baseUrl:’js/app’ 会自动解析成 requireJS/js/app/ 所以requirejs([‘a’,’b’,’c’])的话,会自动到requireJS/js/app/目录下去查找a.js,b.js,c.js.找到了就可以加载出来。

如果未显示设置baseUrl,则默认值是加载require.js的html所处的位置,如果使用了data-main属性的话,则该路径变成了baseUrl.如下代码:

Index.html代码如下:

<script src="js/require.js" defer async="true" data-main="js/app"></script>

App.js代码如下:

requirejs(['a','b','c'],function(a,b,c){

});

那么在浏览器下会被解析成如下:

如上显示:默认情况下是从data-main文件入口去加载js/app.js代码的,但是现在app.js中并没有设置config配置项,所以使用requirejs([‘a’,’b’,’c’],function(a,b,c))的时候会继续加载js下面的a.js,b.js,c.js,如果找到就加载,没有找到就显示404 not found,如上所示。

    2.paths:  paths是映射那些不直接放在baseUrl指定的目录下的文件,设置paths的起始位置是相对于baseUrl的,除非该path设置是以”/”开头或含有URL协议(http://或者https://).

如下在app.js代码:

requirejs.config({

baseUrl: 'js/lib',

paths: {

app: '../app'

}

});

requirejs(['app/a'],function(a){

});

在页面上加载显示如下:

可以看到paths是相对于baseUrl配置项生成的,baseUrl:’js/lib’下的所有js文件,但是paths下的 app:’../app’是相对于js/lib下设置的,’..’的解析到js目录下,然后就解析成js/app下,再require([‘app/a’]),就解析到js/app/a.js了。

如果app.js代码注释掉baseUrl时,变成如下代码:

requirejs.config({

//baseUrl: 'js/lib',

paths: {

app: '../app'

}

});

requirejs(['app/a'],function(a){  });

那么就被加载成这个样子了,如下所示:

直接把app/a.js放在项目文件requirejs下了。

3. shim参数 解决了使用非AMD方式定义的模块(如jquery插件)及其载入顺序,为那些没有使用define()来声明依赖关系,设置模块的”浏览器全局变量注入”型脚本做依赖和导出配置。

在js/app目录下新建文件 depBase.js 代码如下:

define(function(){

return {

"a":11

}

})

接着在app.js文件里面把代码改成如下:

require.config({

baseUrl: 'js/lib',

shim: {

'app/depBase': ['jquery']

},

paths: {

app: '../app'

}

});

require(['app/depBase'],function(base){

console.log(base);

});

然后在浏览器查看请求如下:

由上面可以看到,我require(['app/depBase'],function(base){console.log(base);});这个,它先加载baseUrl中的配置 js/lib下的jquery文件,然后再加载js/app/depBase.js文件。也就是说shim这个参数可以解决没有使用define(function(){})这样的文件包围的代码或者一些全局变量注入,可以确保此文件先加载,然后再加载其他文件。

但是如果我不使用shim这个参数的话,在最新版的requirejs2.1.15中(以前的版本我不太清楚),也可以通过require([‘XX’])来解决,如下演示:

比如我在js/app文件下新建global.js文件,现在的目录如下:

其中global.js代码如下:

names = 1111;

创造一个全局变量names,其中js/app/depBase.js代码变成如下:

define(function(){

return {

'name':names

}

})

也就是说我在app.js代码如下初始化如下:

require.config({

baseUrl: 'js/app'

});

require(['global','depBase'],function(global,base){

console.log(base);

});

我先global初始化引入全局变量names,接着打印出depBase的返回值,截图如下:

也可以看到,可以引入到全局变量names的值。

4.Map参数:Map参数是用来解决同一个模块不同版本的问题,比如在项目开发中,开发初期使用了jquery1.7版本,但是由于业务的需求需要引入jquery1.9以上的版本时候,但是又担心有些是依赖于jquery1.7的代码升级到1.9以上的时候会有问题,因此可以让一部分代码还是依赖于jquery1.7,薪增的代码依赖于jquery1.9.

下面我们来看看我们目录结构如下所示:

我在lib文件下新增jquery1.7.js和 jquery1.9.1.js,现在我在入口文件app.js添加如下代码:

requirejs.config({

map: {

'app/a': {

'jquery': 'js/lib/jquery1.7.js'

},

'app/b': {

'jquery': 'js/lib/jquery1.9.1.js'

}

}

});

require(['app/a'],function(jq){

});

require(['app/b'],function(jq){

});

然后在app/a.js添加如下代码:

// a.js

define(function (require, exports, module) {

var a = require(['jquery']);

});

在app/b.js添加如下代码:

// b.js

define(function (require, exports, module) {

var b = require(['jquery']);

});

在app.js中

require(['app/a'],function(jq){

});时候,在加载app/a.js的时候会加载jquery1.7.js文件,在加载app/b.js的时候会加载jquery1.9.1.js.如下截图所示:

如果在app.js中把下面这行b.js代码初始化注释掉

require(['app/b'],function(jq){

});

那么就只会加载app/a.js及对应的jquery1.7.js,截图如下:

相应的 如果把app/a.js初始化代码注释掉,把app/b.js代码初始化打开,那么只会加载jquery1.9.1,可以看到如果我想app/b.js中使用jquery1.9的话,那么可以这样使用了。

  5.config参数。 config是指需要将配置信息传给一个模块,这些配置往往是application级别的信息,需要一个手段将他们向下传递给模块。在requireJS中,基于requirejs.config()的config配置项来实现。要获取这些信息的模块可以加载特殊的依赖 ”moudle” ,并调用module.config().

首先我们可以还是试着做demo来理解下上面话的意思吧,我现在在项目requirejs下js/app文件下新建一个d.js. 然后在app.js初始化文件加入如下代码:

requirejs.config({

config: {

'app/c': {

size: 'large'

},

'app/d': {

color: 'blue'

}

}

});

require(['app/c'],function(c){

console.log(c);

});

require(['app/d'],function(dss){

console.log(d);

});

在c.js里面这样写代码:

define(function (require, exports, module) {

//其值是'large'

var size = module.config().size;

return size;

});

在控制台下运行可以看到能打印出 large值出来,这说明我们可以通过config配置项来给app/c.js传递一个模块信息,比如如上面的一个对象{size:large},而在c.js里面直接可以通过module.config()方法来获取size的值。

下面我们可以使用一个依赖数组来做同样的事情,如下d.js代码:

define(['module'], function (module) {

//Will be the value 'blue'

var color = module.config().color;

return color;

});

在控制台看 也一样可以打印出color值出来。

6. 内部机制:

RequireJS加载的每个模块作为script Tag,使用head.appendChild()方法。

在模块的定义时,requireJS等到所有的依赖都加载完毕,会为函数的调用计算出正确的顺序,然后在函数中通过正确的顺序进行调用。

7. requireJS函数增加了第三个参数errbacks

还是做demo来演示下,我们还是在入口文件app.js下增加代码,如下:

本来加载b模块是app/b  但是我故意写错成b 所以就不会执行第一个回调函数,转而到第二个回调函数内。如下弹框:

8.在模块载入失败回调中可以使用undef函数移除模块的注册。

如下代码:

代码:

require(['b'], function ($) {

//Do something with $ here

}, function (err) {

var failedId = err.requireModules && err.requireModules[0];

if (failedId === 'b') {

requirejs.undef(failedId);

}

});

requireJS(版本是2.1.15)学习教程(一)的更多相关文章

  1. 【分享】SAS统计分析软件学习教程电子书合集下载

    SAS是著名的统计分析软件,全称为Statistics Analysis System,最早由北卡罗来纳大学的两位生物统计学研究生编制,并于1976年成立了SAS软件研究所,正式推出了SAS软件. 转 ...

  2. ActiveMQ学习教程/1.简要介绍与安装

    ActiveMQ学习教程(一)——简要介绍与安装 一.名词: 1.JMS:即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的 ...

  3. WebPack 简明学习教程

    WebPack 简明学习教程 字数1291 阅读22812 评论11 喜欢35 WebPack是什么 一个打包工具 一个模块加载工具 各种资源都可以当成模块来处理 网站 http://webpack. ...

  4. 深入浅出的javascript的正则表达式学习教程

    深入浅出的javascript的正则表达式学习教程 阅读目录 了解正则表达式的方法 了解正则中的普通字符 了解正则中的方括号[]的含义 理解javascript中的元字符 RegExp特殊字符中的需要 ...

  5. Deep Learning 7_深度学习UFLDL教程:Self-Taught Learning_Exercise(斯坦福大学深度学习教程)

    前言 理论知识:自我学习 练习环境:win7, matlab2015b,16G内存,2T硬盘 练习内容及步骤:Exercise:Self-Taught Learning.具体如下: 一是用29404个 ...

  6. Deep Learning 6_深度学习UFLDL教程:Softmax Regression_Exercise(斯坦福大学深度学习教程)

    前言 练习内容:Exercise:Softmax Regression.完成MNIST手写数字数据库中手写数字的识别,即:用6万个已标注数据(即:6万张28*28的图像块(patches)),作训练数 ...

  7. ArcGis学习教程免费版在线观看

    ArcGis学习教程免费版在线观看 作者:池建    文章来源:清华大学出版社    点击数:150220    更新时间:2013-8-8 摘要:Arcgis学习视频教程根据书籍章节逐步讲解较为详细 ...

  8. 【Java的JNI快速学习教程】

    1. JNI简介 JNI是Java Native Interface的英文缩写,意为Java本地接口. 问题来源:由于Java编写底层的应用较难实现,在一些实时性要求非常高的部分Java较难胜任(实时 ...

  9. ASP.NET MVC 5 学习教程:添加查询

    原文 ASP.NET MVC 5 学习教程:添加查询 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 通过控 ...

随机推荐

  1. js(jquery)绑定点击事件

    <button type="submit" id="test">test</button> 第一种 $("#test" ...

  2. mybatis中使用if标签比较两个字符串是否相等

    <!-- 此处使用if比较是否相等 --> 范例一: <select id="findClientIds" parameterType="map&quo ...

  3. web前端素材整理汇总

    最近一直搞前端开发,整理下前端用的一些常用素材,分享给大家 框架类 Vue:https://cn.vuejs.org/ iview:https://www.iviewui.com/ 插件类 Jquer ...

  4. Python的列表

    1. Python的列表简介 1. 1 列表的定义 列表是Python中最基本的数据结构,列表是最常用的Python数据类型,列表的数据项不需要具有相同的类型.列表中的每个元素都分配一个数字 ,即它的 ...

  5. 使用AOP记录应用调用链开销

    最近系统出现了一次线上的性能问题,本来以为目前的QPS应该是不会出现任何问题的,结果微服务还是比较容易因为某个点的问题导致雪崩的...出了性能问题就要做分析,正统的思路是要不断进行压测用JProfil ...

  6. string的内存管理问题

    string的内存管理是通过仅仅增不减的方式进行的,string的clear方法不会真正的释放内存,假设须要真正的释放内存可以连续调用string的这两个方法.resize(0),reserve(). ...

  7. hadoop用mutipleInputs实现map读取不同格式的文件

    mapmap读取不同格式的文件这个问题一直就有,之前的读取方式是在map里获取文件的名称,依照名称不同分不同的方式读取,比如以下的方式 //取文件名 InputSplit inputSplit = c ...

  8. 【quickhybrid】API规划

    前言 当一切就绪后,就要开始进行API规划,这一块是整个Hybrid框架中非常重要的内容,毕竟对于前端页面来说,只会通过JS API来调用功能. 基本上,API调用起来是否方便简洁影响着整个体验. 这 ...

  9. junit初探

    由于公司规模不大,所以测试方面一直不是很正规,都是做完一个功能,稍微测试一下,没有做单元测试,所以自然也没有接触过类似于junit这类测试的工具. 今天有空研究了一下junit,顾名思义,这是给jav ...

  10. nginx+redis缓存微信的token数据

    上一篇文章我们讲了如何在负载均衡的项目中使用redis来缓存session数据,戳这里. 我们在项目的进展过程中,不仅需要缓存session数据,有时候还需要缓存一些别的数据,比如说,微信的acces ...