最近写一个小游戏的时候用的是RequireJs
构建项目,顺便补了一下RequireJs
,下面讲解一些基础和进阶的用法。html
AMDAsync Module Definition
表明的意思为异步模块定义,是Javascript
模块化的浏览器解决方案,它采用异步的方式加载模块,模块的加载不影响它后面语句的运行。全部依赖这个模块的语句,都定义在回调函数中,等到加载完成以后,这个回调函数才会运行。jquery
AMD规范定义了一个函数define
,经过define
方法定义模块:git
define(id?, dependencies?, factory);
而且采用require()
语句加载模块:github
require([module], callback);
引入的模块和回调函数不是同步的,因此浏览器不会由于引入的模块加载不成功而假死。api
RequireJS
是一个基于AMD
规范实现的JavaScript
文件和模块加载器。它针对浏览器内使用进行了优化,而且对其余JS
环境(Rhino和Node)
作了兼容。使用RequireJS
这样的模块化脚本加载器能够提升代码的速度和质量。浏览器
根据官方文档和项目实例,接下来讲一下ReuqireJS
的基本使用:app
下载最新版的RequireJS。异步
下面是官方示例的RequireJS
项目结构,对内容作了小小的改动,www
做为项目的根目录,lib
中存放项目依赖即须要的一些JS
库,app.js
为主入口文件,app
中存放本身写的模块文件。模块化
1. index.html函数
index.html
中定义了一个script
标签来引入require.js
,其中data-main
属性是一个自定义属性,这个属性指定在加载完 reuqire.js
后,就将属性指定路径下的JS
文件并运行,这个文件即入口文件,这里的app.js
的js
后缀被省略掉了。
<!DOCTYPE html> <html> <head> <script data-main="app" src="lib/require.js"></script> </head> <body> <h1>Hello World</h1> </body> </html>
若是
<script/>
~~~~ 标签引入
require.js 时没有指定
data-main 属性,则以引入该
js 的
html 文件所在的路径为根路径,若是有指定
data-main 属性,也就是有指定入口文件,则以入口文件所在的路径为根路径。
2. app.js
Main.js
加载主模块而且配置项目依赖,要改变 RequireJS
的默认配置,可使用require.config
函数传入一个可选参数对象。下面是一些可使用的配置:
// For any third party dependencies, like jQuery, place them in the lib folder. // Configure loading modules from the lib directory, // except for 'app' ones, which are in a sibling // directory. requirejs.config({ // 模块加载的根路径。 baseUrl: ".", // 用于一些经常使用库文件或者文件夹路径映射,js后缀能够省略 paths: { app: "app/", fmt: "lib/fmt", }, }); // Start loading the main app file. Put all of // your application logic in there. requirejs(["app/main"]);
若是在
require.config() 中有配置
baseUrl,则以
baseUrl 的路径为根路径,这条规则会覆盖上面
data-main
的效果。
3. app/
Main.js
中咱们经过require
函数加载了一个message
模块,该模块用于打印一些定义好的字符串。
define(function (require) { var msg = require("./message"); msg.helloWorld(); });
Main.js
中使用的模块定义在message.js
中,他引入了一个输出依赖fmt
。
define(["fmt"], function (fmt) { return { helloWorld: function () { fmt.println("hello word"); }, }; });
这两种依赖的加载方式又和不一样稍后介绍。
4. lib/
Lib/fmt.js
中我定义一个 js
模块模拟go
的fmt
包,经过return
对外暴露出接口。注意,暴露的对象就是引入的对象。
define(function () { var print = function (msg) { console.log(msg); }; var println = function (msg) { console.log(msg + "\n"); }; return { moduleName: "fmt", print: print, println: println, }; });
要改变RequireJS
的默认配置,可使用require.config
函数传入一个可选参数对象,其实这个对象能够配置五个属性:
require.config({ baseUrl: '.', paths: { app: "app/", fmt: "lib/fmt", }, shim: { 'backbone': { deps: ['underscore', 'jquery'], exports: 'Backbone' }, 'underscore': { exports: '_' } }, config: { 'app/main': { ENV: 'development' } }, map: { 'script/newmodule': { 'foo': 'foo1.2' }, 'script/oldmodule': { 'foo': 'foo1.0' } }, });
1. baseUrl
baseUrl
做为加载模块的根路径。在配置这个属性后,以后全部的路径定义都是基于这个根路径的(包括配置和依赖引入中)。
2. path
用于一些经常使用库文件或者文件夹路径映射,定义以后能够直接在依赖引入中使用。
3. shim
虽然目前已经有一部分流行的函数库符合 AMD 规范,但还有不少库并不符合。shim
就是为了加载这些非AMD
规范的js
,并解决其载入顺序的,好比上面的backbone
。
4. config
config
将配置信息传给一个模块。这些配置每每是application
级别的信息,须要一个手段将它们向下传递给模块。
config: { 'app/main': { ENV: 'development' } }
能够经过加载特殊的依赖module
来获取这些信息。
define(['module'], function (module) { var ENV = module.config().ENV; // development var msg = require("./message"); msg.helloWorld(); });
5. map
对于给定的模块前缀,使用一个不一样的模块 ID 来加载该模块。该手段对于某些大型项目很重要。好比上面配置之后,不一样的模块会使用不一样版本的foo
。
当some/newmodule
调用了require('foo')
,它将获取到foo1.2.js
文件,当oldmodule
调用 require('foo')
,时它将获取到 foo1.0.js
文件。
map
还支持*
,意思是“对于全部的模块加载,使用本 map
配置”。若是还有更细化的 map
配置,会优先于*
配置。
requirejs.config({ map: { '*': { 'foo': 'foo1.2' }, 'some/oldmodule': { 'foo': 'foo1.0' } } });
1. 对象
若是一个模块仅含键值对,没有任何依赖,能够直接在define
中定义。
define({ foo: "foo", bar: function(){} });
2. 须要预处理的对象
define(function () { console.log("Do something..."); return { foo: "foo", bar: function(){} } });
3. 只有一个函数
没有任何依赖的函数直接这么定义:
define(function () { return function (){}; });
调用时直接打()
调用:
require(['lib/foo'],function (foo) { foo(); });
4. 须要其余的依赖:
define(['jquery'],function($){ return function (){}; });
假设咱们有以下 a、b
两个互相依赖的模块,咱们若是调用 b
模块的 b
方法。
// app/a.js define(['app/b'],function(b){ return function() { b() } }); // app/b.js define(['app/a'],function(a){ return function() { a() } });
会发现 b
调用 a
正常,可是 a
中调用 b
方法会报 undefined
错误。
require(['app/b'],function (b) { b(); // b is not defined. });
解决:
循环依赖比较罕见,对于循环依赖,只要依赖环中任何一条边是运行时依赖,这个环理论上就是活的。而若是所有边都是装载时依赖,这个环就是死的。
对模块 a
进行以下修改,即再也不依赖前置加载。而是经过引入 require
依赖,而后再经过 require()
方法去载入模块 b
,并在回调中去执行。
// app/a.js define(['require'],function(require){ var b = require('b') return function() { b() } });