require.js入门教程

RequireJS模块化加载

2014年10月19日   14779次浏览

引子

今天和一个朋友聊天的时候,谈到前端的模块化加载RequireJS,我瞬间短路了,我之前貌似没有用过RequireJS。朋友又问,那你们之间的模块化加载是怎么做的?我想了想,我们之前的很多项目大都是MVC 结构的。前端这块主要是控制器和视图,一般是即用即引,由于复杂度不够,没有搞什么模块化。但是我后来百度了一下,发现我对RequireJS并不陌生,很多项目中潜移默化的已经应用了,但是我没有注意罢了!例如,nodejs的引用方式和RequireJS差不多,nodejs集成的貌似是CommonJS。EXTJS5.0中,貌似集成了RequireJS。我之前在写EXTJS的时候,感觉好多写法就是RequireJS的写法(不是很确定,我也没有重新翻开extjs5.0进行查看验证,要是有说的不对的地方,大家留言指正一下!)

RequireJS模块化加载

本文主要是介绍RequireJS模块化加载,引进RequireJS,并不对其做深入研讨,因为我本身对RequireJS了解的不是很深入。引入RequireJS模块化加载,为后续继续学习打下基础。

RequireJS 简介

RequireJS 是一个非常小巧的 JavaScript 模块载入框架,是 AMD 规范最好的实现者之一。最新版本的 RequireJS 压缩后只有 14K,堪称非常轻量。它还同时可以和其他的框架协同工作,使用 RequireJS 必将使您的前端代码质量得以提升。 目前最新版本的 RequireJS 1.0.8 在 IE 6+、Firefox 2+、Safari 3.2+、Chrome 3+、Opera 10+ 上都工作的很好。

使用 RequireJS 加载 JavaScript 文件

即使您的 Web 前端由于种种历史原因还没有打算进行模块化的设计,RequireJS 作为 JavaScript 文件的加载器,还是可以帮助您来完成异步非阻塞的文件加载。

如案例一 所示,有两个 JavaScript 文件 a.js 和 b.js,里面各自定义了 myFunctionA 和 myFunctionB 两个方法,通过下面这个方式可以用 RequireJS 来加载这两个文件,在 function 部分的代码可以引用这两个文件里的方法。

案例一: 加载 JavaScript 文件

<scriptsrc="./js/require.js"></script><script> 
    require(["./js/a.js","./js/b.js"],function(){ 
             myFunctionA(); 
             myFunctionB();});</script>

require 方法里的这个字符串数组参数可以允许不同的值,当字符串是以”.js”结尾,或者以”/”开头,或者就是一个 URL 时,RequireJS 会认为用户是在直接加载一个 JavaScript 文件,否则,当字符串是类似”my/module”的时候,它会认为这是一个模块,并且会以用户配置的 baseUrl 和 paths 来加载相应的模块所在的 JavaScript 文件。配置的部分会在稍后详细介绍。

这里要指出的是,RequireJS 默认情况下并没有保证 myFunctionA 和 myFunctionB 一定是在页面加载完成以后执行的,在有需要保证页面加载以后执行脚本时,RequireJS 提供了一个独立的 domReady 模块,需要去 RequireJS 官方网站下载这个模块,它并没有包含在 RequireJS 中。有了 domReady 模块,案例一 的代码稍做修改加上对 domReady 的依赖就可以了。

案例二: 页面加载后执行 JavaScript

<scriptsrc="./js/require.js"></script><script> 
    require(["domReady!","./js/a.js","./js/b.js"],function(){ 
             myFunctionA(); 
             myFunctionB();});</script>

执行案例二的代码后,通过 Firebug 可以看到 RequireJS 会在当前的页面上插入为 a.js 和 b.js 分别声明了一个 < script> 标签,用于异步方式下载 JavaScript 文件。async 属性目前绝大部分浏览器已经支持,它表明了这个 < script> 标签中的 js 文件不会阻塞其他页面内容的下载。

案例三:RequireJS 插入的 < script>

<scripttype="text/javascript"charset="utf-8"async=""data-requirecontext="_"data-requiremodule="js/a.js"src="js/a.js"></script>

使用 RequireJS 来定义 JavaScript 模块

这里的 JavaScript 模块与传统的 JavaScript 代码不一样的地方在于它无须访问全局的变量。模块化的设计使得 JavaScript 代码在需要访问”全局变量”的时候,都可以通过依赖关系,把这些”全局变量”作为参数传递到模块的实现体里,在实现中就避免了访问或者声明全局的变量或者函数,有效的避免大量而且复杂的命名空间管理。

如同 CommonJS 的 AMD 规范所述,定义 JavaScript 模块是通过 define 方法来实现的。

下面我们先来看一个简单的例子,这个例子通过定义一个 student 模块和一个 class 模块,在主程序中实现创建 student 对象并将 student 对象放到 class 中去。

案例四: student 模块,student.js

 define(function(){return{ 
        createStudent:function(name, gender){return{ 
                 name: name, 
                 gender: gender 
             };}};});

案例五:class 模块,class.js

 define(function(){var allStudents =[];return{ 
            classID:"001", 
            department:"computer", 
            addToClass:function(student){ 
            allStudents.push(student);}, 
            getClassSize:function(){return allStudents.length;}};});

案例六: 主程序

require(["js/student","js/class"],function(student, clz){ 
 clz.addToClass(student.createStudent("Jack","male")); 
 clz.addToClass(student.createStudent("Rose","female")); 
 console.log(clz.getClassSize());// 输出 2 });

student 模块和 class 模块都是独立的模块,下面我们再定义一个新的模块,这个模块依赖 student 和 class 模块,这样主程序部分的逻辑也可以包装进去了。

案例七: 依赖 student 和 class 模块的 manager 模块,manager.js

 define(["js/student","js/class"],function(student, clz){return{ 
    addNewStudent:function(name, gender){ 
    clz.addToClass(student.createStudent(name, gender));}, 
    getMyClassSize:function(){return clz.getClassSize();}};});

案例八:新的主程序

require(["js/manager"],function(manager){ 
 manager.addNewStudent("Jack","male"); 
 manager.addNewStudent("Rose","female"); 
 console.log(manager.getMyClassSize());// 输出 2 });

通过上面的代码示例,我们已经清楚的了解了如何写一个模块,这个模块如何被使用,模块间的依赖关系如何定义。还是有一些使用技巧需要提示一下:

尽量不要提供模块的 ID,如 AMD 规范所述,这个 ID 是可选项,如果提供了,在 RequireJS 的实现中会影响模块的可迁移性,文件位置变化会导致需要手动修改该 ID。

每个 JavaScript 文件只定义一个模块,模块名称和文件路径的查找算法决定了这种方式是最优的,多个的模块和文件会被优化器进行优化。 避免模块的循环依赖,如果实在避免不了,可以模块中加上对”require”模块的依赖,在代码中直接用

require(”dependencyModuleName”)

配置 RequireJS

前面的介绍中,我们似乎忽略了一个基本问题,模块名字是怎么来的?当我在 require 一个模块时,这个模块是如何映射到具体的 JavaScript 文件上去?这就涉及到如何配置 RequireJS。

最简化的加载 RequireJS 的方式如案例2 所示,在这种情况下,我们没有指定一个 baseUrl 和 paths 给 RequireJS,如果通过如案例10 所示方式,则 data-main 指定了一个在当前 index.html 目录并行的文件夹下的 /js/main.js 作为程序入口,而 /js 目录也将作为 baseUrl 在其他模块定义时候使用。

案例九:载入 require.js

<scriptdata-main="js/main"src="scripts/require.js"></script>

因此,我们前面示例中的所有模块依赖,都可以去掉”js/”,直接写 ”student”, ”class”,”manager” 等。 一种更为直接的方式显示指定 baseUrl 和 paths 就是利用 require.config 来设置这些参数。如案例十 所示。

案例十. 配置 RequireJS

<scripttype="text/javascript"src="./js/require.js"></script><scripttype="text/javascript"> 
  require.config({ 
    baseUrl:"./js", 
    paths:{"some":"some/v1"}, 
 waitSeconds:10}); 
  require(["some/module","my/module","./js/a.js"],function(someModule,    myModule){});</script>

baseUrl指明的是所有模块的 base URL,比如”my/module”所加载的 script实际上就是 /js/my/module.js。注意,以 .js 结尾的文件加载时不会使用该 baseUrl,它们仍然会使用当前 index.html所在的相对路径来加载,所以仍然要加上”./js/”。如果 baseUrl没有指定,那么就会使用 data-main中指定的路径。

paths 中定义的路径是用于替换模块中的路径,如上例中的 some/module 具体的 JavaScript 文件路径是 /js/some/v1/module.js 。 waitSeconds 是指定最多花多长等待时间来加载一个 JavaScript 文件,用户不指定的情况下默认为 7 秒。

另外一个重要的配置是 packages,它可以指定其他符合 CommonJS AMD 规范的目录结构,由此带来了丰富的扩展性。如 Dojo、jQuery 等的模块也可以通过该配置来让 RequireJS 加载。

其他可配置的选项还包括 locale、context、deps、callback等,有兴趣的读者可以在 RequireJS 的官方网站查阅相关文档。

综合运用 RequireJS

当 RequireJS 与其他框架一起工作的时候,显然它是可以作为统一的加载器来加载其他框架。鉴于 jQuery、Dojo 等已经支持了 AMD ,那么就有可能在定义自己的模块的时候,通过适当配置,直接把其他框架的模块作为依赖对象。

RequireJS 和 Dojo

案例十是一个基本的 RequireJS 和 Dojo 集成配置方法,这里关键是将 dojo 和 dijit 都在 packages 里面注册一下。

案例十一:配置 RequireJS 和 Dojo

<script> 
 require ={ 
 packages:[{ 
 name:dojo”, 
 location:dojo”, 
 main:”lib/main-browser”, 
 lib:“.”},{ 
 name:dijit”, 
 location:dijit”, 
 main:”lib/main”, 
 lib:“.”}], 
 paths:{ 
 require:“./js}, 
 ready:function(){ 
 require([“my/module”],function(module){});}};</script>

在定义 module 这个模块时就可以直接将 dojo 和 dijit 里的模块作为依赖对象了。

RequireJS 和 jQuery

把 jQuery 作为一个依赖模块来使用也非常简单,只需要在 RequireJS 里的 config 里做相应的配置就可以了。

案例十二:配置 RequireJS 和 jQuery

require.config({ 
 paths:{"jquery":"./js/jquery-1.7"}});require(["jquery"],function(jQ){ 
 console.log(jQ);});

RequireJS 和 Web Workers

Web Workers 是多线程的 JavaScript,每个 worker 里面的脚本都 会启动一个新的线程来执行,通过在 worker 里面用 importsScript 来加载 require.js 可以直接在 worker 里面使用 RequireJS 。

案例十三: RequireJS 和 Web Workers

 importScripts('./js/require.js');require(["require"],function(require){ 
        postMessage("test");});

层次组织

/public

|------require.js、config.js、index.html

  |-------lib/

    |-------a.js、b.js

  |-------orthers/

    |-------c.js

文件功能

require.js:

  其中requirejs的核心代码就是require.js文件,可以从官方网站上下载:

config.js:

  用于配置requirejs的相关内容,可以设置文件目录,加载模块命名匹配,以及一些依赖关系等等。

index.html:

  我们的测试页面或者网址首页。

/lib/a.js和/lib/b.js 以及 /others/c.js

  是测试的模块js文件。

代码详情

index.html

复制代码
<doctype html><html><meta http-quiv="Content-Type" content="text/html;charset=UTF-8"><script data-main="config" src="require.js"></script></html><body><h1>This is test for RequireJS</h1><script type="text/javascript">
        alert("index");
    </script></body>
复制代码

  其中,data-main指定主要的配置文件;src为requirejs的文件。

config.js

复制代码
require.config({
    // baseUrl:'/',    paths:{
        "a":"lib/a",
        "b":"lib/b",
        "c":"others/c"
    }
    });
require(['a','b','c'],function(a,b,c){
    a.hello();
    b.hello();
    c.hello();
});
复制代码

  baseUrl指定所有文件的主要目录,paths配置模块名字以及其匹配的加载路径。

  当有需要使用某些模块时,就可以通过require([xxx],function(xxx){xxx});的方式使用。

a.js

复制代码
define([], function() {
  return {
    hello: function() {
      alert("hello, a");
    }
  }
});
复制代码

b.js

复制代码
define([], function() {
  return {
    hello: function() {
      alert("hello, b");
    }
  }
});
复制代码

c.js

复制代码
define([], function() {
  return {
    hello: function() {
      alert("hello, c");
    }
  }
});
复制代码

  模块化文件,一般是一个功能是一个文件。文件的名字,就是上面requireconfig中配置的模块名字。require加载文件时,会自动加上.js后缀。

  当某些模块依赖其他模块时,可以通过define([xxx],function(xxx){yyy});的方式添加依赖关系,require会在异步加载后,自动调整次序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值