关闭

require.js 入门学习

标签: 模块化amdrequire.js
3850人阅读 评论(1) 收藏 举报
分类:

require.js 介绍

//-------------------------------------------------------------

AMD规范

前端开发在近一两年发展的非常快,JavaScript作为主流的开发语言得到了前所未有的热捧。大量的前端框架出现了,这些框架都在尝试着解决一些前端开发中的共性问题,但是实现又不尽相同。在这个背景下,CommonJS社区诞生了,为了让前端框架发展的更加成熟,CommonJS鼓励开发人员一起在社区里为一些完成特定功能的框架制定规范。AMD(Asynchronous Module Definition)就是其中的一个规范。 

传统JavaScript代码的问题

让我们来看看一般情况下JavaScript代码是如何开发的:通过<script>标签来载入JavaScript文件,用全局变量来区分不同的功能代码,全局变量之间的依赖关系需要显式的通过指定其加载顺序来解决,发布应用时要通过工具来压缩所有的JavaScript代码到一个文件。当Web项目变得非常庞大,前端模块非常多的时候,手动管理这些全局变量间的依赖关系就变得很困难,这种做法显得非常的低效。

AMD的引入

AMD提出了一种基于模块的异步加载JavaScript代码的机制,它推荐开发人员将JavaScript代码封装进一个个模块,对全局对象的依赖变成了对其他模块的依赖,无须再声明一大堆的全局变量。通过延迟和按需加载来解决各个模块的依赖关系。模块化的JavaScript代码好处很明显,各个功能组件的松耦合性可以极大的提升代码的复用性、可维护性。这种非阻塞式的并发式快速加载JavaScript代码,使Web页面上其他不依赖JavaScript代码的UI元素,如图片、CSS以及其他DOM节点得以先加载完毕,Web页面加载速度更快,用户也得到更好的体验。

CommonJS的AMD规范中只定义了一个全局的方法,如清单1所示。
清单1.AMD规范

define(id?, dependencies?, factory);  

该方法用来定义一个JavaScript模块,开发人员可以用这个方法来将部分功能模块封装在这个define方法体内。

id表示该模块的标识,为可选参数。
dependencies是一个字符串Array,表示该模块依赖的其他所有模块标识,模块依赖必须在真正执行具体的factory方法前解决,这些依赖对象加载执行以后的返回值,可以以默认的顺序作为factory方法的参数。dependencies也是可选参数,当用户不提供该参数时,实现AMD的框架应提供默认值为[“require”,”exports”,“module”]。
factory是一个用于执行改模块的方法,它可以使用前面dependencies里声明的其他依赖模块的返回值作为参数,若该方法有返回值,当该模块被其他模块依赖时,返回值就是该模块的输出。
CommonJS在规范中并没有详细规定其他的方法,一些主要的AMD框架如RequireJS、curl、bdload等都实现了define方法,同时各个框架都有自己的补充使得其API更实用。

require.js意义

//-------------------------------------------------------------

模块化

其实在计算机领域,模块化的概念被推崇了近四十年。软件总体结构体现模块化思想,即把软件划分为一些独立命名的部件,每个部件称为一个模块,当把所有模块组装在一起的时候,便可获得问题的一个解。
模块化以分治法为依据,但是否意味着我们把软件无限制的细分下去。事实上当分割过细,模块总数增多,每个模块的成本确实减少了,但模块接口所需代价随之增加。要确保模块的合理分割则须了解信息隐藏,内聚度及耦合度。
这里的JavaScript模块与传统的JavaScript代码不一样的地方在于它无须访问全局的变量。模块化的设计使得JavaScript代码在需要访问”全局变量”的时候,都可以通过依赖关系,把这些”全局变量”作为参数传递到模块的实现体里,在实现中就避免了访问或者声明全局的变量或者函数,有效的避免大量而且复杂的命名空间管理。
如同CommonJS的AMD规范所述,定义JavaScript模块是通过define方法来实现的。

命名空间

Javascript因为天生的缺点,语言本身没有集成命名空间的概念,所以变量名、函数名很容易发生冲突。这些年,大家想尽了各种办法,给 js 添加命名空间的概念,其中最成熟的套路,就是 RequireJS 这种。

如果你对 c# 或 java 语言的命名空间有一点点了解,那么,RequireJS 的用法和作用,可以用 c# 中的两行代码来类比说明:

using xx=wojilu.Core;
namespace MyApp {
    public class MyClass {
        public void MyTest() {}
    }
}
require.js主要的功能,就是实现c#代码的第一行和第二行的功能:1)引入需要使用的命名空间(顺便加个别名也行);2)将自己的代码放到命名空间中,避免全局污染。

下面我们看一下 RequireJS 的具体写法,新建一个独立的 wojilu.test1.js 文件,然后输入如下代码:
define( ['wojilu.Core'], function(xx) {
    return {
        MyTest : function() {alert( 'wojilu1 module' );}
    };
});
和上面的c#代码对比一下,require.js 同样也做了两件事情:
1、引入命名空间 wojilu.Core,同时给它取了一个别名 xx;
2、将自己的所有代码放在 define 中,避免全局化的污染冲突。
总之,require.js定义了(define)一个命名空间,在定义的时候,顺便引用了需要使用其他命名空间。我们注意到,按照require.js 的术语,它把命名空间叫做“模块”。注意,在这里,require.js 定义的模块(命名空间)是匿名的,没有取名,这是和c#不同的地方。


require.js实战

//-------------------------------------------------------------

引入require.js

<script src="require.js"></script>

有人可能会想到,加载这个文件,也可能造成网页失去响应。解决办法有两个,一个是把它放在网页底部加载,另一个是写成下面这样:

<script src="js/require.js" defer async="true" ></script>
async属性表明这个文件需要异步加载,避免网页失去响应。IE不支持这个属性,只支持defer,所以把defer也写上。

这种写法虽然简单,但其实并不推荐,一般的写法还要再加个属性:

<script data-main="js/main" src="js/require-jquery.js"></script>
就像一个c程序总有一个 main 方法作为入口一样,上面这种写法,做了几件事情:
1、加载了 require-jquery.js 文件。注意,官方提供了 require.js和 jquery 的打包版本,推荐。
2、在加载之后,加载入口文件 js/main.js ,注意,main.js 写进去的时候,不需要后缀名。
你的所有其他 js 模块文件,都可以写在 main.js 里面,通过 main.js 加载。

require.js用法(一):define定义模块

define( ['wojilu.test1'], function( t1 ) {
    return {
        NewTest : function() { t1.MyTest();}
    };
});

通过文件名(不需要后缀名),引入了 wojilu.test1 命名空间,并给它取了别名 t1,然后在代码中使用 t1.MyTest() 方法。

按照 RequireJS 的规范,所有的模块定义,都必须放在 return {} 对象中。也就是说,你的代码都要写在 return 返回的 {} 对象里面。这会不会导致代码臃肿难看?当然不会。你可以重构一下,比如这样做:

define代码

define(['wojilu.test1'], function (t1) {
    function someFunc1() {
        // 实际主要代码
    }
    function someFunc2() {
        // 实际主要代码
    }
    // 通过 return 方式,将需要公开的函数暴露出来,供其他 js 调用
    return {
        NewTest: function () {
            t1.MyTest();
        },
        fun1: someFunc1,
        fun2: someFunc2
    };
});

require.js用法(二):require加载js文件

到此为止,我们遇到了两个关键词,一个是 define ,可以用来定义模块(命名空间),第一部分我们讲了;还有一个是 require,可以直接加载其他 js。它除了简单的用法:

<script>
require( ["some" ] );
</script>
之外,还有和 define 类似的复杂用法:
<script> 
require(["aModule", "bModule"], function() { 
    myFunctionA(); // 使用 aModule.js 中的函数 myFunctionA
    myFunctionB(); // 使用 bModule.js 中的函数 myFunctionB
}); 
</script> 

总结一下,define 是你定义自己的模块的时候使用,可以顺便加载其他js;require 直截了当,供你加载用的,它就是一个加载方法,加载的时候,可以定义别名。

require.js用法(三):require.config()对模块加载行为自定义

require.config()就写在主模块(main.js)的头部。参数就是一个对象,这个对象的paths属性指定各个模块的加载路径。

 require.config({
    paths: {
      "jquery": "lib/jquery.min",
      "underscore": "lib/underscore.min",
      "backbone": "lib/backbone.min"
    }
  });


另一种则是直接改变基目录(baseUrl)。

require.config({
    baseUrl: "js/lib",
    paths: {
      "jquery": "jquery.min",
      "underscore": "underscore.min",
      "backbone": "backbone.min"
    }
  });

如果某个模块在另一台主机上,也可以直接指定它的网址,比如:

require.config({
    paths: {
      "jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min"
    }
});

require.js用法(四):define模块返回函数个数问题

1、define 的return只有一个函数,require的回调函数可以直接用别名代替该函数名。

2、define 的return当有多个函数,require的回调函数必须用别名调用所有的函数。

require(['selector','book'], function(query,book) {
    var els = query('.wrapper');

    book.fun1();
    book.fun2();
});
此处query 函数是1的情况,book 函数是2的情况。

require.js用法(五):加载的js文件不是AMD规范定义

当调用的js文件不是使用define定义,直接使用其全局变量或函数就可以了,没有影响。

这个时候只是控制了js 文件的加载顺序。


@@部分资料来自网络,感谢伟大的乐于分享的程序猿们!  

require.js:http://www.requirejs.org

require.js入门教程:http://www.verydemo.com/demo_c110_i2031.html

使用RequireJS优化Web应用前端:http://www.csdn.net/article/2012-09-27/2810404

require.js链接汇总:http://webeginner.diandian.com/post/2013-12-04/40060318517


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人网站
    个人资料
    • 访问:92542次
    • 积分:1437
    • 等级:
    • 排名:千里之外
    • 原创:43篇
    • 转载:28篇
    • 译文:1篇
    • 评论:12条
    最新评论