首页 > Javascript > RequireJS学习笔记(一)

RequireJS学习笔记(一)

RequireJs已经流行很久了,我们在项目中也打算使用它。它提供了以下功能:

   1. 声明不同js文件之间的依赖

   2. 可以按需、并行、延时载入js库

   3. 可以让我们的代码以模块化的方式组织

在html中引入requirejs

在HTML中,添加这样的 <script> 标签:

<script src="/path/to/require.js" data-main="/path/to/app/config.js"></script>

通常我们只需要导入requirejs即可,然后指定一个入口文件,不需要显式导入其它的js库,因为这个工作会交给requirejs来做。

属性 data-main 是告诉requirejs:你下载完以后,马上去载入真正的入口文件(相当于java中的main)。它一般用来对requirejs进行配置,并且载入真正的程序模块。

如果使用相对路径,data-main有更简单的写法,requirejs会自动下载同级目录下的config.js,完整代码如下:

<!doctype html>
<html>
    <head>
        <title>requirejs入门(一)</title>
        <meta charset="utf-8">
        <script src="/path/to/require.js" data-main="config"></script>
    </head>
    <body>
     
    </body>
</html>

config.js中有两个函数:require.configrequire(或者requirejs.configrequirejs

require.config用来配置一些参数,它将影响到requirejs库的一些行为,require.config的参数是一个JS对象,常用的配置有baseUrlpathsurlArgs等。

首先介绍urlArgs参数,该参数是给每个模块的js文件后附加参数,在调试过程中非常管用,本文所有的案例都添加了该参数以禁用浏览器缓存,等到上线项目再注释掉即可:

urlArgs: "t=" + (new Date()).getTime()


使用require.config通常分为2种情况:一种是支持AMD方式定义的模块,另一种是不支持AMD方式定义的模块。

1. 例如jquery从1.7以后是支持AMD方式的,例如:

require.config({
    paths: {
        "jquery": "jquery-1.7.1.min"
    },
    urlArgs: "t=" + (new Date()).getTime()
});
 
require(["jquery"], function($) {
    alert($().jquery);
});

这里配置了paths参数,使用模块名“jquery”,其实际文件路径jquery-1.7.1.min.js(后缀.js可以省略)。

如果使用requirejs2.0及以上版本,可以配置一个数组按照顺序加载,例如使用google cdn的jquery库失效时再加载本地的jquery文件。

require.config({
    baseUrl: "./",
    paths: {
        "jquery": [
            "http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min",
            "jquery-1.7.1.min"
        ]
    },
    urlArgs: "t=" + (new Date()).getTime()
});

require(["jquery"], function($) {
    alert($().jquery);
});

当google cdn无法访问时会引入同级目录下的jquery-1.7.1.min.js。


2. 另一种是不支持AMD方式定义的模块,这时需要使用shim导出他们定义的全局变量,例如:

require.config({
    baseUrl: "./",
    paths: {
        //左边的是模块的标识(可以随便指定,唯一即可),右边的是模块的文件名
        "app": "app"
    },
    shim: {
        //左边的是模块的标识(key,和paths的标识一致),hello是要暴露的变量(该变量是app.js的全局变量)
        "app": { exports: "hello" }
    },
    urlArgs: "t=" + (new Date()).getTime()
});

require(["app"], function(app) {
    app();
});

app.js的代码如下:

function hello() {
	alert("hello world");
}

运行弹出"hello world",如果你去掉shim方法,你将会得到:Uncaught TypeError: app is not a function。

3. 如果自己写的模块需要暴露2个或以上的变量,这时就不能使用exports了,必须换成init方法。

config.js的代码如下:

require.config({
    baseUrl: "./",
    paths: {
        //左边的是模块标识(可以随便指定,唯一即可),右边的是模块的文件名
        "app": "app"
    },
    shim: {
        "app": {
            init: function() {
                return {
                    //注意:左边的是要暴露的变量别名,右边的是要暴露的变量
                    hello: hello,
                    hello2: hello2
                }
            }
        }
    },
    urlArgs: "t=" + (new Date()).getTime()
});
 
//注意,第一个参数是数组,其值是需要require的模块,第二个参数是模块对象,对象参数应该与第一个参数是数组元素一一对应
require(["app"], function(app) {
    //调用模块标识为app的hello和hello2方法
    app.hello();
    app.hello2();
});

app.js的代码修改如下:

function hello() {
    alert("hello world");
}
function hello2() {
    alert("hello world, again~");
}

exportsinit同时存在的时候, exports 将被忽略。

通过上面的3个注意可以很清晰看出模块标识模块对象模块变量之间的关系。


除了require.configrequire之外,requirejs还提供了define方法让我们编写依赖于requirejs的模块(插件),例如我们将app.js按照requirejs的方式来重构代码:

//可以直接使用匿名函数作为define的参数,没有依赖的模块
define(function() {
    return {
        "hello": function() {
            alert("hello world");
        },
        "hello2": function() {
            alert("hello world, again~");
        }
    }
});

或者

//如果app.js有依赖的模块,可以将各个模块标识以数组作为第一个参数传入,模块对象与匿名函数的参数一一对应
define(["require"], function(require) {
    return {
        "hello": function() {
            alert("hello world");
        },
		"hello2": function() {
            alert("hello world, again~");
        }
    }
});

重构后就不需要使用shim方法,也不用担心使用exports还是init,直接require后调用即可。


如何完全不让jquery污染全局的$

可以引入 map 配置,一劳永逸地解决这样问题:

map: {
        // '*' means all modules will get 'jquery-private'
        // for their 'jquery' dependency.
        '*': { 'jquery': 'jquery-private'},
        // 'jquery-private' wants the real jQuery module
        // though. If this line was not here, there would
        // be an unresolvable cyclic dependency.
        'jquery-private': { 'jquery': 'jquery'}
    },


我们可以先新建一个jquery-private.js,定义一个模块,仅仅为了执行这句代码:

define(["jquery"], function(jQuery) {
    return jQuery.noConflict(true);
});

然后在入口处先调用它

require.config({
    baseUrl: "./",
    paths: {
        "jquery": "jquery-1.7.1.min",
        "jquery-private": "jquery-private",
        "app": "app"
    },
    map: {
        "*": { "jquery": "jquery-private"},
        "jquery-private": { "jquery": "jquery"}
    },
    urlArgs: "t=" + (new Date()).getTime()
});
//注意,第一个参数是数组,其值是需要require的模块,第二个参数是模块对象,对象参数应该与第一个参数是数组元素一一对应
require(["jquery", "app"], function($, app) {
    alert($().jquery);
    app.hello();
});

以下可以证明在app.js中无法获取到$:

将app.js中的hello方法弹出值改写:

define(["require"], function(require) {
    return {
        "hello": function() {
            alert($().jquery);
        },
		"hello2": function() {
            alert("hello world, again~");
        }
    }
});

再次运行,在hello()方法处会报Uncaught TypeError: $ is not a function错误。

这样,我们就将jquery隔离运行了。如果不信,可以将map段代码去掉,保证弹出2次jquery的版本号。

下一篇主要是介绍requirejs如何配合使用jquery插件。RequireJS学习笔记(二)

本文地址:http://blog.zhengshuiguang.com/js/requirejs-1.html

转载随意,但请附上文章地址:-)

标签:requirejs 异步加载 模块

相关文章

评论已关闭