requirejs:杏仁的优化(almond)
这里只是调侃一下,“杏仁”其实指的是almond,requirejs作者的另一个开源项目,它的定位是作为requirejs的一个替代品。
本文概要:
1. 使用场景
2. 打包例子:未使用almond
3. 打包例子:使用almond
4. 如何暴露公共API
5. 限制 & 支持的特性
6. 写在后面 & demo下载
使用场景
什么情况下需要使用almond
呢?假设你手头有个基于requirejs的小项目,所有业务代码加起来就几十K(压缩后可能更小).出于性能优化的考虑,你可能在想:如果能够去掉requirejs的依赖就好了,毕竟,gzip后的requirejs还有大概20k(2.1.6版本)。
almond就是为了这个目的而诞生的,开发过程,你可以照常使用requirejs来管理你的依赖,而到了打包上线阶段,替换成almond就行了。gzip后的almond只有大约1k,优化幅度相当大。
例子:未使用almond
这一小节主要举个requirejs+r.js
打包的例子,下一小杰会在本小节的基础上,通过almond
进行进一步的优化。代码很简单,扫一下就可以了
目录结构如下:
demo.html
build.js
js/
js/main.js
js/cookie.js
js/util.js
demo.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>demo</title>
</head>
<body>
<h1>简单的requirejs例子 - almond</h1>
<script type="text/javascript" src="js/require.js" data-main="js/main-built.js"></script>
<!-- <script type="text/javascript" src="js/main-almond-built.js"></script> -->
</body>
</html>
js/main.js
requirejs.config({
baseUrl: 'js'
});
require(['cookie', 'util'], function(Cookie, Util){
Cookie.say('hello');
Util.say('hello');
});
js/cookie.js
define([], function(){
return {
say: function(msg){
alert('cookie: '+msg);
}
};
});
js/util.js
define([], function(){
return {
say: function(msg){
alert('util: '+msg);
}
};
});
用r.js打包
首先,在build.js
里声明打包的配置
({
baseUrl: "js",
name: "main",
optimize: "none",
out: "js/main-built.js"
})
然后,下载打包工具r.js
npm install -g requirejs
最后,通过r.js
打包
r.js -o build.js
恭喜!可以看到js
目录下生成了打包后的文件main-built.js
js/main-built.js
define('cookie',[], function(){
return {
say: function(msg){
alert('cookie: '+msg);
}
};
});
define('util',[], function(){
return {
say: function(msg){
alert('util: '+msg);
}
};
});
requirejs.config({
baseUrl: 'js'
});
require(['cookie', 'util'], function(Cookie, Util){
Cookie.say('hello');
Util.say('hello');
});
define("main", function(){});
运行demo
为了检验打包后的结果是运行的,我们需要到浏览器里验证一下。首先我们要把demo.html
里的资源引用修改下
<script type="text/javascript" src="js/require.js" data-main="js/main-built.js"></script>
在浏览器里打开demo.html
,看到下面的弹窗,搞定
例子:使用了almond
我们看到,上面的例子打包后生成了main-built.js
,gzip后看下文件多大
gzip main-built.js
可以看到只有174B,这种情况下,在页面中引用requirejs有点不划算,这个时候我们就要引入almond了
-rw-r--r-- 1 user staff 174B 4 20 22:03 main-built.js.gz
很简单,首先下载almond,并放置到js
目录下
然后,运行下面命令,通过r.js + almond
生成打包后的文件main-almond-built.js
r.js -o baseUrl=js name=almond include=main out=js/main-almond-built.js wrap=true optimize=none
js/main-almond-built.js
/**
* @license almond 0.2.9 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved.
* Available via the MIT or new BSD license.
* see: http://github.com/jrburke/almond for details
*/
// almond的代码篇幅略长,这里略过...
define("cookie",[],function(){return{say:function(e){alert("cookie: "+e)}}}),define("util",[],function(){return{say:function(e){alert("util: "+e)}}}),requirejs.config({baseUrl:"js"}),require(["cookie","util"],function(e,t){e.say("hello"),t.say("hello")}),define("main",function(){});
同样,在修改修改main.js
的链接后,在浏览器里访问demo.html
,done!
<script type="text/javascript" src="js/main-almond-built.js"></script>
看下gzip后的main-almond-built.js
多大,只有1.6k!
-rw-r--r-- 1 user staff 1.6K 4 20 22:34 main-almond-built.js.gz
通过配置文件打包
上面打包的命令行有点长,对于楼主这样对命令行有恐惧症的人来说,还是比较习惯写个配置文件,命令行则越简短越好
build-almond.js
({
baseUrl: "js",
name: "almond",
include: "main",
out: "js/main-almond-built.js",
wrap: true
})
接下来就很简单了,很短的一行命令
r.js -o build-almond.js
暴露公共API
上面的例子,如果没有加上wrap: true
这个选项,打包后生成的文件,你是可以访问到之前的定义的模块的,比截图所
但加上wrap: true
后就完全不一样了,因为所有的代码都会被包在一个匿名的闭包里,大致如下
(function () {
//almond will be here
//main and its nested dependencies will be here
}());
此时就访问不到之前定义的模块了,包括require
都成了匿名函数里的一个局部变量
这种情况下,如果我们想要访问模块里的方法,该怎么做呢?可以修改下配置文件
build-almond-frag.js
({
baseUrl: "js",
name: "almond",
include: "main",
out: 'js/main-built-almond-public.js',
wrap: {
startFile: 'js/start.frag.js',
endFile: 'js/end.frag.js'
}
})
js/start.frag.js
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define([], factory);
} else {
root.Main = factory();
}
}(this, function () {
//almond, and your modules will be inlined here
js/end.frag.js
return {
cookie: require('cookie'),
util: require('util')
};
}));
打包
r.js -o build-almond-frag.js
生成的文件结构如下
start.frag
almond.js
modules for your lib, including 'main'
end.frag
现在,可以在浏览器里继访问我们暴露的API了
一些限制 & 支持的特性
毫无意外,almond只是支持了requirejs
功能的子集,所以,在使用前需要了解下它的支持哪些特性,有哪些限制。
限制:
- 需要将所有的模块打包成一个文件
- 不支持模块动态加载
- 只能调用一次
requirejs.config()
(原来可以调用两次??) - 不能通过
var require = {};
来传递配置参数 - 不支持多版本/上下文
- 不要使用
require.toUrl()
、require.nameToUrl()
- (不了解packages,直接附上原文了)
do not use packages/packagePaths config. If you need to use packages that have a main property, volo can create an adapter module so that it can work without this config. Use the amdify add command to add the dependency to your project.
支持的特性
- 使用相对路径的依赖(dependencies with relative IDs.)
- define('id', {}) definitions.(不知道肿么翻译)
- define(), require() and requirejs() 调用。
- 符合这样特性的插件:能够将资源内联进打包优化后的文件,并通过同步的方式访问内联后的资源。比如text插件、CoffeeScript插件
写在后面
本文简单介绍了下如何通过almond
对依赖requirejs的项目进行进一步的优化。当然,almond
也存在着一些限制,比如无法动态加载模块、只能将模块打包成一个文件等,具体的可以参考这里。是否在打包阶段使用almond
替代requirejs
,得看具体场景,这里就不展开,后面有时间再简单介绍下。
github博客:https://github.com/chyingp/blog
新浪微博:http://weibo.com/chyingp
站酷主页:http://www.zcool.com.cn/u/346408/