Dojo的编译系统, 以下简称build, 提供了组合Dojo以及你其它Javascript资源和CSS文件的方法。 以使这些资源在你应用程序的产品环境中更加高效的被使用。
"Building" Dojo 或 Javascript ?
如果你之前使用过其它编程语言, 你可能好奇为什么我们要讨论 "Building" Dojo或 Javascript。 因为 building 经常暗指将代码编译成机器的字节码。 但我们讨论的building Dojo, 我们提及的是缩小文件,优化文件, 连接文件 和 删除无效代码。
每当你发送代码从服务器到客户端解释, 如Javascript, HTML 和 CSS. 它会占去带宽和时间。 文件的大小和发送到服务器的请求数量都会决定了延迟的时间。而不管你代码执行的如何快,你都不能在它到达客户端前开始执行。
Dojo 编译器工具用于完成代码以最高效的传速到客户端的任务。 包含处理自定义代码, 模块和CSS的能力。 如果处理得当,你可以为你的应用创建一个易于管理和维护的编译系统。
基础
Dojo 编译系统是复杂和高度自定义化的。它被设计为可扩展的, 但在这里我们不会讲到如何扩展。 另外, 虽然1.7引入的新编译系统大部分都是兼容老版本的, 但你最好还是重写你的build, 特别是如果你将你的应用转到AMD模式下。
在我们开始讲解之前, 这里有许多核心的概念你需要知道, 以及在贯穿整个教程中我们会使用到的束语。
模块和包 Modules and Packages
在阅读本教程前,希望你已经理了什么是模块。 如果没有,你先回顾一下
模块的定义 教程, 实际上这是Dojo 1.9的基础。 这些模块然后会组成包, 然后构成了模块的逻辑分组。 在Dojo 1.9, 包一个包都应该含 有一个package.json 用来描述这个包。许多包也可有含有一个package.js文件, 它提供了Dojo特有的编译信息。
Dojo配置 Dojo Configuration
在内部,Dojo 有大量的配置选项,你可能需要在你的应用程序里用到。 这种配置不仅对于你应用程序正常运行很重要,它对于你编译应用程序也很重用,可能提供的配置是编译处理的一部分。 如果还没有非常熟悉如何对 Dojo进行配置, 你应该回顾下
Configuration Dojo with dojoConfig
层 Layers
层的本质是 单个Javascript 文件包含 了多个模块 以及在某些情况下是其它的资源。 一个层通常是你想要编译的主要输出, 创建这个文件,然后它成为你应用程序的” 发行版“。 一个层可以是一个”引导”层,它包括Dojo的引导代码,引导代码允许Dojo加载其它的模块。 不论是什么层,它们的内容都 高度依赖于你的应用程序和设计。
创建配置文件 Profiles
创建的配置文件是非常小的Javascript文件, 它们向编译器提供了如何处理代码的信息。 在旧的编译系统里, uitl/buildscripts/profiles会包含你的配置文件。 Dojo 1.7及以后, 变成了分散管理, 每一个包应该包含它自已的编译配置文件,然后你会有“主的” 编译配置文件,主配置文件会指示编译器编译哪些包,哪些层需要编译,以及在优化代码时提供配置选项。
文件最小化
它的概念是通过压缩工具先获得 Javascript文件,然后压缩代码,同时保证代码的功能。 从高效的观点上说它是非常有用的, 许多开发者喜欢用它混淆的代码。 但它使得调试很难进行。 这也是你不想在 "编译"后的代码上开发的原因之一。 Dojo 编译器利用两个工具集来做文件最小化。 第一个为ShrinkSafe, 它是1.7版本之前唯 一的工具。 Dojo 1.7 后, 编译器可以利用 Google的 Closure Compiler.
无效代码路径删除
Google Clousre Compiler 有一个很强大的优势是可以检测到不能访问的代码(无效代码), 然后删除它。 许多前年, 我们有开始过一个 Dojo Linker的项目来解决类似的问题, 但是没有时间来完成它。 所以我们很高兴有一个可选择的解决方案。 Dojo编译器在设计时已经考虑到这一点。 它有多个“旋转开关”,可以编译过程中进行设置, 然后编译器后以“硬编码"的方式将所有的路径代码都输出到输出路径中, 然后当优化时, Closure Compiler 会从最小的文件里检测到无法到达的代码,然后删除它。
无效代码路径以硬编码应该是如下意思
if(0){do something } 在编译后有很多用于特征检测,如果没有通过, 会设置if(0){} , 这相当于一条路径,也会被编译器以硬编码的方法输出到目标文件里。 如果通过 设置if(1). if(0)为无效路径,在优化时会被删除。
创建控制器,转化器 和解析器
编译系统是由几个基础编译模块组成。 通常你没有直接意思到它们的存在, 但如果你对更高级的编译器感兴趣, 它们可以被改进用来做更有趣的事情。 控制器是编译的指令集, 它可以读取配置文件,并确定需要使用什么转化器和解析器。 转化器处理逻辑,先取得某些东西,在将它们转化其它的东西。 解析器用来在编译时,解析AMD插件模块。 例如,如果你正在使用 dojo/text 来加载一个窗口部件的模板, 在编译时解析器读取这个文本(模板文件) 并用内嵌到压缩文件里。
你需要什么
为了能够使用Dojo编译系统,你必须复制整个full Dojo SDK. Dojo 的"标准版” 已经是编译系统编译过的。 编译工具依赖Java(或者更快的Node.js), 所以请确保你已经安装这些软件。
布置你的应用程序
它是最大的挑战之一, 特别是当你配置一个复杂的应用时。 我们注意到很多人在从 Dojo 1.6或之前的版本中迁移到新版本,会有许多奇怪的应用程序结构。 意味着大量的人迁移到1.7时,会遇到很大的挑战。 所以在继续你的应用之前,你需要考虑如何布置它。
形式上需要有有一个src的根目录,然后所有的包都在这个根目录里。通常结构如下:
主要的Dojo包( dojo, digit, 和 dojox) 跟你自定义的包, 加上dojo 实用工具(util) 等都是同级的。
!* 如果不想从头开始创建项目,
Dojo Boilerplate 向你提供了一个模板。 它不仅有基本的应用程序布局, 而且已经配置好了。
包
为了能通过编译,两个文件必须在你的主应用包文件夹内。 第一个文件是遵循
CommonJS Packages/1.0 的包描述文件, 通常命名为package.json. 位于包文件夹的根目录内。 第二个文件是一个profie, 它包含 编译工具应该如何处理包的内容的信息。这里有两种约定的命名方法, 第一种是<packagename>.profile.js,位于包文件夹的根目录内。 另一种约定是 package.js,也是位于包文件夹的根目录内。 你在
Dojo Foundatio Packages 里会经常看到第二种约定方法.
!* 通常,你只会有一个包,包含整 个应用程序。 但如果你已经将你的代码划分到多个包(例如,shared/common分别在不同的包里), 你需要为每个包创建package.json 和 package.js.
包描述器
包描述器文件(package.json) 提供了关于当前包的信息, 比如,包的名称, 包的依赖信息, 许可协议和bug信息。以上都是nodejs创建包的信息(易于包的管理,上传,下载等), 对于Dojo 编译系统来说, 唯一有用的是dojoBuild键。 在构造package.json 时,至少提供name, version和 description. dojoBuild键用来指定profile 文件的入口。 以下是app包的一个例子, 一个基本的package.json如下所示
{
"name": "app",
"description": "My Application.",
"version": "1.0",
"keywords": ["JavaScript", "Dojo", "Toolkit", "DojoX"],
"maintainers": [{
"name": "Kitson Kelly"
}],
"contributors": [{
"name": "Kitson Kelly"
},{
"name": "Colin Snover"
}],
"licenses": [{
"type": "AFLv2.1",
"url": "http://bugs.dojotoolkit.org/browser/dojox/trunk/LICENSE#L43"
},{
"type": "BSD",
"url": "http://bugs.dojotoolkit.org/browser/dojox/trunk/LICENSE#L13"
}],
"bugs": "https://github.com/example/issues",
"repositories": [{
"type": "git",
"url": "http://github.com/example.git",
"path": "packages/app"
}],
"dependencies": {
"dojo": "~1.9.2",
"dijit": "~1.9.2",
"dojox": "~1.9.2"
},
"main": "src",
"homepage": "http://example.com/",
"dojoBuild": "app.profile.js"
}
!* CommonJS Package/1.0 规定必须向包描述器提供完整的选项清单。 如果只计划内部使用代码,可以不需要按照规定来做。 但你至少要包括一个dojoBuild键,才能让编译器找到你的profile 文件。
包配置文件
编译简介文件是编译系统主要的配置文件。 它是 Javascript 文件,文件里定义了一个对像,对像包含 了编译时所有必要的指令。 最基本的包配置文件如下所示:
var profile = (function(){
return {
resourceTags: {
amd: function(filename, mid) {
return /\.js$/.test(filename);
}
}
};
})();
注意我们使用了自动执行了一个匿名函数,是为了确保环境中的其它代码不会干扰到你的 profile. 这也给你提供了自已写更复杂的计算profile 对像的机会。
!* 如果不是应用程序最主要的配置文件的话,它链接到dojoBuild 只需要包含 一个 resourceTags 指令。
这里提供的 "minimum" 信息是为了编译器编译你的包。 当builder读取这个包时会做什么, builder后把包里每个文件传递给resourceTags函数,以确定文件是否需要给文件加上标记。 它里有两个参数: 第一个为文件名,第二个为MID(模块的 ID)。 如果函数返回true , 然后给文件加上标记(如果为false,则不需要应用标记)。
我们在这里给每个文件的末尾添加一个标记来作为AMD 模块。 所以编译器会把它做为AMD 模块处理,而不是老版本的Dojo模块。 这里还有其它的标记,你可能想用来标记包中的其它资源。
amd
资源为AMD模块
declarative
资源使用了你想要描扫的依赖声明标记。
test
资源是包测试代码的一部分
copyOnly
资源会被复制到目的位置,并保持不变。
miniExclude
如果profile中的属性mini为true, 资源不会被复制到目的位置
!*如果你没有为AMD资源标记 amd标签, 编译器通常会提示这个模块是AMD,并正常处理, 但最好还是准确的标记你的代码。
!* declarative标签已经超出了本教程的范围。 关于更多这个主题,你可以查看
depsDeclarative 参考指南,获得更多的信息。
确保资源标记是编译器正常处理资源的前题保证。 让我们假设你的测试在 src/app/tests( 所有的码农都会为包创建单元测试,不是吗?) 加上 profile.jso 和一些其它只想复制的文件。 所以一个更复杂的包配置文件如下所示:
var profile = (function(){
var testResourceRe = /^app\/tests\//,
// checks if mid is in app/tests directory
copyOnly = function(filename, mid){
var list = {
"app/app.profile": true,
// we shouldn't touch our profile
"app/package.json": true
// we shouldn't touch our package.json
};
return (mid in list) ||
(/^app\/resources\//.test(mid)
&& !/\.css$/.test(filename)) ||
/(png|jpg|jpeg|gif|tiff)$/.test(filename);
// Check if it is one of the special files, if it is in
// app/resource (but not CSS) or is an image
};
return {
resourceTags: {
test: function(filename, mid){
return testResourceRe.test(mid) || mid=="app/tests";
// Tag our test files
},
copyOnly: function(filename, mid){
return copyOnly(filename, mid);
// Tag our copy only files
},
amd: function(filename, mid){
return !testResourceRe.test(mid)
&& !copyOnly(filename, mid)
&& /\.js$/.test(filename);
// If it isn't a test resource, copy only,
// but is a .js file, tag it as AMD
}
}
};
})();
如你看到的, 这看起来变得复杂了。 但基本的概念是profile对像需要包含 一个 resourceTags. 它包含了不同的标记函数。 然后你可以利用Javascript的强大功能计算出哪个资源儿得什么样的标签。
应用程序配置文件
为了将一个包包含到整体的应用中,你只需简单的按照上一章节里的做, 标记你的资源。但为了在生产环节创建一个编译, 你还需要额外的选项。 这里两个思路: 如果你的应用非常简单 并且你有一个自定义包包含 所有你自定的代码(e.g. app), 你仅需要创建一个完成的包profile。 如果你的应用更复杂并且你有多个包, 或者你可能想为不同的编译创建多个不同的配置文件。然后你应该创建一个"application"配置文件。
本教程剩下的内容,我们假设你将创建一个应用程序级的profile. 称为 "myapp.rpofile.js", 文件位于应用程序的根目录。
对于一个编译配置文件结构有如下选项:
Option | Type | Description |
---|---|---|
basePath | Path | 指定编译的根目录,编译的其余的路径都是相对于这个位置, 定义的路径是相对于配置文件的位置 |
releaseDir | Path | 编译的目的(输出文件)根目录, 编译器会尝试直接创建这个目录,如果存在,直接覆盖。它相对于basePath |
releaseName | String | 当输出时提供发布版本的名称。 名称会附加到releaseDir后面。 例如,如果你想发布代码到release/prd, 你可以设置releaseDir为release, releasename为prd |
action | String | 应该设置为release |
packages | Array | 一个数组对像,数组对像的元素为包信息的散列表。这些包信息在编译器映射模块时使用。 |
layers | Object | 它允许你创建不同的“layer" 模块组作为编译的一部分。一个layer为单个文件,文件里包含多个模块。 |
假设我们将要建立一个应用程序的配置文件,在这里有两个文件是我们想要加载到一个单页面应用里。一个包含了主要的代码,这些代码会被我们作为依赖使用(dojo/dojo), 然后在第二个层(app/Dialog)是在某种情况下有条件加载。
var profile = (function(){
return {
basePath: "./src",
releaseDir: "../../app",
releaseName: "lib",
action: "release",
packages:[{
name: "dojo",
location: "dojo"
},{
name: "dijit",
location: "dijit"
},{
name: "dojox",
location: "dojox"
},{
name: "app",
location: "app"
}],
layers: {
"dojo/dojo": {
include: [ "dojo/dojo", "dojo/i18n", "dojo/domReady",
"app/main", "app/run" ],
customBase: true,
boot: true
},
"app/Dialog": {
include: [ "app/Dialog" ]
}
}
};
})();
如果我们创建profile文件, 并正常工作。 我们将编译后的文件输出到app/lib. app/lib会包含我们四个包中的所有模块。加上两个特殊的文件 app/lib /dojo/dojo.js 和 app/lib/app/Dialog.js, 这个文件里会包含所有必须的模块(build 版本), 如dojo.js 会包含dojo/dojo模块及依赖,dojo/i18n模块及依赖, dojo/domReady模块及依赖,app/main模块及依赖,app/run模块及依赖, 所有的这些模块的代码都是末被编译的。
!* 暂时可以先不管layer, 之后会对它进行深入了解
编译优化
仅完成一个编译还是不够的, 编译器默认情况下会将所有的层最小化。 但从本质上说,编译的其余部分都是”独立的“。 这里有几个其它的配置选项(knobs/options), 你应该考虑:
Option | Type | Description |
---|---|---|
layerOptimize | String/Boolean | 设置层为最小化, 默认为"shrinksafe". 如果为false,则关闭最小化。 另一个 有效的值是”shrinksafe.keeplines", "closure", "closure.keeplines", "comment" 和 "comment.keeplines". |
optimize | String/Boolean | 设置不是layer部分的模块最小化。 默认为 false. 接受跟layerOptimize 相同的值。 |
cssOptimize | String/Boolean | 对CSS文件做优化处理。 默认为false. 如果值为"comments",则删除注释,多余的行, 以及内联的@import命令。 如果值为"comments.keepLines",则删除注释,我内联的@imports. 但保留换行符。 |
mini | Boolean | 确定整个编译是否为"mini"(最小编译,不要编译), 如果为true, 不会复制标记了miniExclude的文件,如测试,DEMO , 以及不需要被编译(复制,最小化)的文件。 默认为false. |
stripConsole | String | 处理输出代码中的conole语名, 默认为"normal", 会删除所以console语句,除了console.error 和 console.warn. 最需注意的是,这个特征只在优化级别时才适用。否则它会被忽略。 另外可能的值为"none", "warn" 和"all" |
selectorEngine | String | 默认选择引擎,并编译到代码里,虽然它没有直接使代码变小,但它确保 了选择引擎不需要加载其它的引擎。 它默认为nothing, 会包含两个 dojo引擎 lite 和 acme. |
staticHasFeatures | Object | 特征散列表, 你可以为编译时强近的关闭这些特征检测。 当与Closure一起用时, 它允许你删除无效的代码。 之后会详细介绍它的值。 |
所以基于以上的特征,你可以为你的应用程序以及任何其它的必需的代码,创建一个最优化的版本。 一个配置文件可能如下:
layerOptimize: "closure",
optimize: "closure",
cssOptimize: "comments",
mini: true,
stripConsole: "warn",
selectorEngine: "lite",
它将保证经过closurer后,我们的 layers 和 所有其它的模块都被最小化。 我们改善了 CSS资源的性能。 我们不会复制额外的资源,如demo和tests. 我们删除了代码中的console.warn语句, 并且明确的指定了我们将使用到的css选择器。
你可能会问自己 “如果我们的所有的东西都编译到一个layer里。 为什么复制其它包(模块)?” 如果你仅保持layer文件,而没有其它模块。 会导致在没有在重新编译的情况下,你会无法访问到这些模块( 因为没有被复制到releaseName文件夹里)。
!* 输出将包含每个模块和layer的未压缩格式以及删除了console信息的版本(.uncompressed.js和.consoleStripped.js). 主要是为了以后潜在的调试用。 正常的你可以保留它们, 但如果你想删除它们,也可以直接删除(比如 网站服务器的空间大小限制)。
无效代码路径删除
上面提及到的staticHasFeatures 可以联合Closure 来优化代码。 Dojo 使用has API 做特征检测。 当相关特征的代码是必须的,if(has("some-feature")){...} 会被添加到代码里, 用于检测某一特征。 staticHasFeatures 它允许你在编译时可以将创建“硬编码的特征检测”(最终代码里出现if(0){....} )。 如果不需要这个特征, Closure 可以检测到(判断if(0){...}),并删除。
最需要注意是这样创建的编译版本可能不会像 末编译的版本那样正确的工作。 所以你需要小心考虑你的目标环境以及在编译后的版本里做测试,以确保跟你想像的一样。
有许多大量的特片检测,但这里列举的特征检测,适用于全部采用AMD模式的Dojo编译。 设计的浏览器客户端。
Feature | Setting | Description |
---|---|---|
config-deferredInstrumentation | 0 | 禁止自动加载dojo/promise/instrumentation模块. 该模块用于监测被拒绝的承诺,将末被处理的错误输出到控制台. 参考 deferred.js |
config-dojo-loader-catches | 0 | Disables some of the error handling when loading modules. |
config-tlmSiblingOfDojo | 0 | 禁止非标准化的模块名解析,dojo.js |
dojo-amd-factory-scan | 0 | 假定所有的模块都为AMD格式, 可以参考dojo/dojo.js |
dojo-combo-api | 0 | Disables some of the legacy loader API |
dojo-config-api | 1 | 确保 build 是可配置的 |
dojo-config-require | 0 | Disables configuration via the require() |
dojo-debug-messages | 0 | Disables some diagnostic information |
dojo-dom-ready-api | 0 | Ensures that the DOM ready API is available |
dojo-firebug | 0 | Disables Firebug Lite for browsers that don't have a developer console (e.g. IE6) |
dojo-guarantee-console | 1 | Ensures that the console is available in browsers that don't have it available (e.g. IE6) |
dojo-has-api | 1 | 保证has API 可以使用。可以查看dojo/has.js文件 |
dojo-inject-api | 1 | 保证可以使用跨域来加载模块, 更详细的可以参考 dojo/dojo.js |
dojo-loader | 1 | 确保使用的是Dojo 加载器,而不是第三方加载器。可以查看dojo/_base/loader.js |
dojo-log-api | 0 | 禁止loader提供日志记录功能,即 require.log |
dojo-modulePaths | 0 | Removes some legacy API related to loading modules |
dojo-moduleUrl | 0 | Removes some legacy API related to loading modules |
dojo-publish-privates | 0 | 禁止loader 对过 require公开内部的方法和变量, 可以查看dojo.js |
dojo-requirejs-api | 0 | Disables support for RequireJS |
dojo-sniff | 1 | 允许扫描dojo.js 角本中的 data-dojo-config 和 djConfig 标签, dojo.js |
dojo-sync-loader | 0 | 禁止使用1.7版本之前的加载器, dojo.js |
dojo-test-sniff | 0 | 禁止以测试为目的特征检测 |
dojo-timeout-api | 0 | 禁止代码处理未加载的模块 |
dojo-trace-api | 0 | Disables the tracing of module loading. |
dojo-undef-api | 0 | Removes support for module unloading |
dojo-v1x-i18n-Api | 1 | Enables support for v1.x i18n loading (required for Dijit) |
dom | 1 | Ensures the DOM code is available |
host-browser | 1 | Ensures the code is built to run on a browser platform |
extend-dojo | 1 | Ensures pre-Dojo 2.0 behavior is maintained |
!* 可以查看更多关于
Dojo/has 的信息
我们将需要添加如下的信息到配置文件里:
staticHasFeatures: {
"config-deferredInstrumentation": 0,
"config-dojo-loader-catches": 0,
"config-tlmSiblingOfDojo": 0,
"dojo-amd-factory-scan": 0,
"dojo-combo-api": 0,
"dojo-config-api": 1,
"dojo-config-require": 0,
"dojo-debug-messages": 0,
"dojo-dom-ready-api": 1,
"dojo-firebug": 0,
"dojo-guarantee-console": 1,
"dojo-has-api": 1,
"dojo-inject-api": 1,
"dojo-loader": 1,
"dojo-log-api": 0,
"dojo-modulePaths": 0,
"dojo-moduleUrl": 0,
"dojo-publish-privates": 0,
"dojo-requirejs-api": 0,
"dojo-sniff": 1,
"dojo-sync-loader": 0,
"dojo-test-sniff": 0,
"dojo-timeout-api": 0,
"dojo-trace-api": 0,
"dojo-undef-api": 0,
"dojo-v1x-i18n-Api": 1,
"dom": 1,
"host-browser": 1,
"extend-dojo": 1
},
!* 虽然对于大部分应用来说都是安全的, 在所有的目标平都不会出现问题。 但我们还是在次提醒你, 当你使用staticHasFeatures,你已经从根本上改变了执行的路径(if(has(feature)){....}.
Layers
多个layer文件在很多情况下是有益的。 最常见的情况是当你的代码分为几个不同的部分,当你在需要的时候在加载。 举例, 在一个web邮件的应用程序也会包含日历组件,这里就有 公共部分代码, 邮件部分 和日历部分, 所有的代码可以分成三个单独的layer. 这样可以确保使用者在仅使用mail服务时避免加载 日历代码。 同时也确保用户在同时使用mail 和日历时不会再次下载共享的代码(公共部分)。创建这几个layer非常简单:
var profile = {
layers: {
"app/main": {
include: [ "app/main" ],
exclude: [ "app/mail", "app/calendar" ]
},
"app/mail": {
include: [ "app/mail" ],
exclude: [ "app/main" ]
},
"app/calendar": {
include: [ "app/calendar" ],
exclude: [ "app/main" ]
}
}
};
注意,如果这里还有其它共享的组件时,却又没有出现在app/main模块里, 你应该将它们添加到layer 层的依赖里 。 例如, 如果mail 和 calendar组件都使用到了DataGrid, 但是app/main去没有引用它,这里它应该明确的指定main layer加载这个依赖,避免编译到两个早独的层里。 app/main 和 app/caledar:
它也可以用于创建自定义的dojo.js. 特别是在使用AMD模式时。 因为默认情况下(为了向后兼容)。 dojo/main模块会被编译系统自动添加到 dojo.js文件, 这会使得你加载了代码,但又不用到这段代码。 为了自定义一个dojo.js , 你可以简单定义一个单独的layer. 设置 customBase 和 boot都为true.
var profile = {
layers: {
"app/main": {
include: [ "app/main", "dojox/grid/DataGrid" ],
exclude: [ "app/mail", "app/calendar" ]
},
"app/mail": {
include: [ "app/mail" ],
exclude: [ "app/main" ]
},
"app/calendar": {
include: [ "app/calendar" ],
exclude: [ "app/main" ]
}
}
};
它也可以用于创建自定义的dojo.js. 特别是在使用AMD模式时。 因为默认情况下(为了向后兼容)。 dojo/main模块会被编译系统自动添加到 dojo.js文件, 这会使得你加载了代码,但又不用到这段代码。 为了自定义一个dojo.js , 你可以简单定义一个单独的layer. 设置 customBase 和 boot都为true.
var profile = {
layers: {
"dojo/dojo": {
include: [ "dojo/dojo", "app/main" ],
customBase: true,
boot: true
}
}
};
customBase 阻止自动添加dojo/amin . boot 用于保证文件包含了必要的 AMD loader代码。 将app/main也添加到include 列表里,会直接将app/main模块(及它的依赖)代码添加到dojo.js文件中。
!* 设计你的layer非常具有挑战, 特别当你想在编译时利用其它包。 一个很好的例子是讲处理layers和优化,请参考
SitePen blog post
默认的配置(应用程序的dojoConfig默认配置)
最后要讲的是在编译时为编译提定默认配置。 这些配置可能会在一个应用里或者代码里被重写。 但如果你想为特定的程序做特定的编译时, 最好利用 defaultConfig。 别外,你可以指定hasCache. 它类似于 static has features. 但不用会硬编码。 它是直接设置某一特征的代码,而不是计算出来。 (注:这一段大体讲的是通过编译配置文件profile中的defaultConfig项,来指定最终文件中的dojoConfig, 即指定的这些配置什么输出到layer的末层,如dojo.js的末尾会有 dojoConfig:{async:true}, 而dojoConfig也可以在实际的页面里进行设置。)
我们将添加以下信息到配置文件里。
defaultConfig: {
hasCache:{
"dojo-built": 1,
"dojo-loader": 1,
"dom": 1,
"host-browser": 1,
"config-selectorEngine": "lite"
},
async: 1
},
以上的内容适合于所有的 AMD应用程序。 如果你在你产品应用里只使用单个配置, 在编译时你很容易扩展配置。 然后当你加载应用时可以直接忽略他们。
综合使用
所以,如果我们把以上讲到的东西组合成一个应用程序的 profile文件。 它会看起来如下所示:
var profile = (function(){
return {
basePath: "./src",
releaseDir: "../../app",
releaseName: "lib",
action: "release",
layerOptimize: "closure",
optimize: "closure",
cssOptimize: "comments",
mini: true,
stripConsole: "warn",
selectorEngine: "lite",
defaultConfig: {
hasCache:{
"dojo-built": 1,
"dojo-loader": 1,
"dom": 1,
"host-browser": 1,
"config-selectorEngine": "lite"
},
async: 1
},
staticHasFeatures: {
"config-deferredInstrumentation": 0,
"config-dojo-loader-catches": 0,
"config-tlmSiblingOfDojo": 0,
"dojo-amd-factory-scan": 0,
"dojo-combo-api": 0,
"dojo-config-api": 1,
"dojo-config-require": 0,
"dojo-debug-messages": 0,
"dojo-dom-ready-api": 1,
"dojo-firebug": 0,
"dojo-guarantee-console": 1,
"dojo-has-api": 1,
"dojo-inject-api": 1,
"dojo-loader": 1,
"dojo-log-api": 0,
"dojo-modulePaths": 0,
"dojo-moduleUrl": 0,
"dojo-publish-privates": 0,
"dojo-requirejs-api": 0,
"dojo-sniff": 1,
"dojo-sync-loader": 0,
"dojo-test-sniff": 0,
"dojo-timeout-api": 0,
"dojo-trace-api": 0,
"dojo-undef-api": 0,
"dojo-v1x-i18n-Api": 1,
"dom": 1,
"host-browser": 1,
"extend-dojo": 1
},
packages:[{
name: "dojo",
location: "dojo"
},{
name: "dijit",
location: "dijit"
},{
name: "dojox",
location: "dojox"
},{
name: "app",
location: "app"
}],
layers: {
"dojo/dojo": {
include: [ "dojo/dojo", "dojo/i18n", "dojo/domReady",
"app/main", "app/run" ],
customBase: true,
boot: true
},
"app/Dialog": {
include: [ "app/Dialog" ]
}
}
};
})();
编译角本的执行步骤 :
- 指定build的根目录
- 指定输出的目录,app/lib
- 优化layers和所有模块时用Closure Compiler( goolge)
- 删除CSS中的注释和内嵌的@imports.
- 不需要复制额外资源,如测试和demos.
- 从代码里删除console.log 行
- 利用lite css选择器引擎
- 提供一个默认的dojoConfig
- 不需要的特征检测会通过 Closure Compiler删除。
- 标识的每个包会成为编译版本的一部分
- 创建两个层, 一个称为app/lib/dojo/dojo.js, 它包含我们应用程序的主体代码,加上必须的引用的dojo代码。 另一个称为app/lib/app/Dialog.js, 它包含一些额外的代码,用于支持app/Dialog模块。
我们在编译文件中提供了包的位置, 这里有多个方法来提供包的位置,如下所示:
- 使用 -- require 命令行标志来指定一个包含 require对象的角本或者调用require(config)
- 使用 -- dojoConfig 命令行标志指定一个角本,script包含 dojoCOnfig对像和包配置数据 dojoConfig={ packages:[{name:"dojo", location:"dojo"}] }
- 使用多个 -- package 命令行标志指定每个包的目录(注意每个包必须包含 package.json文件)
- 在profile文件里指定包的配置,这正是我们上面所做的。
编译
现在各种各样的配置文件已经设置完成, 现在可以实际的执行一个编译。 运行一个编译相对于之前做的准备来说,要简单多了。 在应用程序根目录你可以运行以下命令(OSX 或 linux):
$ src/util/buildScripts/build.sh --profile myapp.profile.js
在Windows系统:
> src\util\buildScripts\build.bat --profile myapp.profile.js
通过我们为应用程序创建 的profile,开始编译。 在实际应用中,命令行还可以指定不同的配置选项。 -- help ( 或在windows上 build.bat --help)会获得所有命令列表。 虽然我们推荐你使用简单的方式,并把所有的选项放 profile里。 但是也有可能需要在编译前确认执行参数 --check-args. 它会以JSON格式输出处理编译的各个配置。
!* 当使用了 Closure Compiler时,你将看到很多关于dojox代码的警告和错误, 这些都可以被忽略掉,并且在未来的版本中得到修正。 同样当你使用staticHasFeatures 你也将看到关于无法达到的代码(无效代码)的警告。实际上这正是我们想要删除的代码。
总结
在部署web应用程序时编译系统是非常重要的。 即使在Dojo1.9中使用异步加载, 编译应用要比没有编译的加载要快。 加载时间是用户体验的关键因素,所以不要发布一个末编译的应用程序。
其它资源
- Defining Modules tutorial
- Configuring Dojo with dojoConfig tutorial
- Reference Guide for dojo/has
- Reference Guide for the build system
- Dojo Boilerplate