最近发现require火了一段时间了,然而好久没有接触前端框架了。以下本人学习笔记和实战,参考了多篇文章,里面可能有错误或不准确,请批评指正^_^。
requirejs存在的意义:保证js加载顺序
RequireJS 入口
1.data-main属性
当你下载RequireJS之后,你要做的第一件事情就是理解RequireJS是怎么开始工作的。当RequireJS被加载的时候,它会使用data-main属性去搜寻一个脚本文件(它应该是与使用src加载RequireJS是相同的脚本)。data-main需要给所有的脚本文件设置一个根路径。根据这个根路径,RequireJS将会去加载所有相关的模块。下面的脚本是一个使用data-main例子:
<script src="./require/require.js" data-main="main"></script>
如果是js文件,.js后缀名在配置时一般都可以省略。data-main指的是当前文件下的main.js(这个路径表明main.js和html页面在同级目录)。mainjs配置了所有的需要使用的js脚本,代码如下:
require.config({
baseUrl: '/requireLearning/script/',
paths: {
jquery:'jquery/jquery',
highchart: 'highchart/js/highcharts'
},
shim: {
highchart: {
deps: ['jquery']
}
}
})
require([
'app'
], function (app) {
});
上面,经测试requirejs.config与require.config效果是一样的。
Config设置说明
● baseUrl——用于加载模块的根路径。
● paths——左侧为别名,在define声明依赖包时会引用到。右侧为相对与根路径的相对路径,浏览器requirejs会按照baseUrl + paths来加载脚本。
● shims——理解为声明哪个模块需要依赖其他的模块,这个例子中声明highchart需要依赖jquery模块。
● deps——加载依赖关系数组
baseUrl亦可通过RequireJS config手动设置。如果没有显式指定config及data-main,则默认的baseUrl为包含RequireJS的那个HTML页面的所属目录。
如果一个module ID(requirejs提倡一个模块一个文件,一个moudule ID是指paths左侧内容),如果符合下述规则之一,其ID解析会避开常规的”baseUrl + paths”配置,而是直接将其加载为一个相对于当前HTML文档的脚本:
● 以 “.js” 结束.
● 以 “/” 开始.
● 包含 URL 协议, 如 “http:” or “https:”.
shim和deps是什么?
shim为那些没有使用define()来声明依赖关系、设置模块的”浏览器全局变量注入”型脚本做依赖和导出配置。比如Jquery,由于是异步加载,$和Jquery会依然导出,但是Jquery和它的插件加载顺序不能保证,因此可以用shim保证依赖,保证加载顺序;
(个人理解)一篇博客上说,jquery $不会被导出到全局里面是错误的,jquery源码有一句
..}(typeof window !== "undefined" ? window : this, function( window, noGlobal )..
...
// Expose jQuery and $ identifiers, even in
// AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
// and CommonJS for browser emulators (#13566)
if ( !noGlobal ) {
window.jQuery = window.$ = jQuery;
}
说明是会被导出的!只不过是加载顺序不能保证~因此要保证jquery比它的插件先加载。
require config后,执行require函数,这是这个整个程序声明我要加载app作为程序入口(你可以自己加载多个,看需求)。
2.define全局函数的使用(定义模块)
通常模块定义函数会把前面的数组中的依赖模块按顺序做为参数接收。例如,下面是一个简单的模块定义(app.js 紧接着上面的main.js):
define([
'highchart'
],function () {
console.log("test")
}
)
定义了一个模块,模块声明highchart依赖,在加载app.js时会加载highchart,然后执行console.log函数
注意:在define 的function里参数是否与difine依赖完全相对应是可选的。如果依赖项已经把自己的功能对象导出到全局,就不需要在function里写对应参数,写在function里的参数依赖项需要依赖的的模块返回它的api,否则会调用出错!highchart 路径在main.js里已经定义过。
你可能会看到一些define()中包含了一个模块名称作为首个参数:
//Explicitly defines the "foo/title" module:
define("foo/title",
["my/cart", "my/inventory"],
function(cart, inventory) {
//Define foo/title object in here.
}
);
这些常由优化工具生成。你也可以自己显式指定模块名称,但这使模块更不具备移植性——就是说若你将文件移动到其他目录下,你就得重命名。一般最好避免对模块硬编码,而是交给优化工具去生成。优化工具需要生成模块名以将多个模块打成一个包,加快到浏览器的载人速度。。
define()中的相对模块名: 为了可以在define()内部使用诸如require(“./relative/name”)的调用以正确解析相对名称,记得将”require”本身作为一个依赖注入到模块中:
define(["require"], function(require) {
var mod = require("./relative/name");
});
经过测试上述的语法报错,require is not a function
define(["require"], function() {
var mod = require("./relative/name");
});
此语法通过,requirejs里把require API输出到全局,require是一个全局函数。
requirejs要求一个模块对应一个文件,但是在使用插件时会发现,插件文件里把多个模块都集成到一个js文件里了。此时即使模块返回了它的API,用define加载的整个文件也并不能获得它的api对象。
举个例子ACE一个代码编辑器插件:在ace.js里面定义了多个模块例如:
define("ace/ext/error_marker",。。。。
define("ace/ace",["require",
那么就不能以一个文件来表示一个模块,因此加载完后,也不能根据module ID来调用某个模块的api:
define([
'angular',
'ace'
], function (ace) {
var module = angular.module('controllers');
module.controller('contractDetailController', ['$scope', '$window',function ($scope,$window) {
s.editorDetail= ace.edit(el);
s.editorDetail.getSession().setMode("ace/mode/java");
}
]);
});
直接调用ace edit函数会出错,因为ace并不是ace的一个api。可以用两种方式调用:
define([
'angular',
'ace'
], function () {
var module = angular.module('controllers');
module.controller('contractDetailController', ['$scope', '$window',function ($scope,$window) {
s.editorDetail=$window. ace.edit(el);
s.editorDetail.getSession().setMode("ace/mode/java");
}
]);
});
一种用require获得模块,此时并不破坏异步加载,ace.js 已经加载过 了:
define([
'angular',
'ace',
"require"
], function () {
var module = angular.module('controllers');
module.controller('contractDetailController', ['$scope', '$window',function ($scope,$window) {
s.editorDetail=require("ace/ace").edit(el);
s.editorDetail.getSession().setMode("ace/mode/java");
}
]);
});
3.使用require函数
在RequireJS中另外一个非常有用的函数是require函数。require函数用于加载模块依赖但并不会创建一个模块。例如:下面就是使用require定义了能够使用jQuery的一个函数。
require(['jquery'], function ($) {
//jQuery was loaded and can be used now
});
循环依赖(摘)
I如果你定义了一个循环依赖(a依赖b,b同时依赖a),则在这种情形下当b的模块函数被调用的时候,它会得到一个undefined的a。b可以在模块已经定义好后用require()方法再获取(记得将require作为依赖注入进来):
//Inside b.js:
define(["require", "a"],
function(require, a) {
//"a" in this case will be null if a also asked for b,
//a circular dependency.
return function(title) {
return require("a").doSomething();
}
}
);
模块(Modules)在它们自己的上下文中进行评估,并且拥有全局变量exports以供模块使用。变量exports只是个普通的JavaScript对象(plain old JavaScript object),甚至你也可以往它上面附加内容,与我们上面展示的命名空间对象类似。为了访问某个模块,你要调用全局函数require,并指明你请求的包的标示符(identifier for the package)。然后评估该模块,并且无论返回什么都会附加到exports上。此模块将会缓存起来,以便后来的require函数调用来使用。
// calculator.js
exports.add = function(n1, n2) {
};
// app.js
var calculator = require('./calculator');
calculator.add(2, 2);
参考文章:
http://www.ruanyifeng.com/blog/2012/10/asynchronous_module_definition.html
http://kb.cnblogs.com/page/132461/
https://www.oschina.net/translate/getting-started-with-the-requirejs-library
http://www.requirejs.cn/docs/api.html#data-main