首页 > 代码库 > 翻译 - 【Dojo Tutorials】Creating Builds

翻译 - 【Dojo Tutorials】Creating Builds

Dojo的编译系统(build,后称编译)提供了一种编译Dojo和你的其他JavaScript代码与CSS文件的方式,让你的应用在生产环境可以高效的利用它们。

“编译”Dojo或JavaScript?

如果你使用过其他编程语言,你也许会纳闷为什么我们要探讨Dojo或JavaScript的“编译”问题,因为编译通常意味着是将代码编译成机器语言。但是当我们谈论Dojo的编译时讲的是这么一个概念,将代码最小化,优化性能,代码串联与移除没用的代码。

每当你从服务器发送代码到客户端来解释执行时,如JavaScript,HTML和CSS,它都将花费带宽与时间。你发送了多少,取决与向服务器发送了多少请求。与你的代码执行快慢无关,因为在浏览器加载完之前还无法开始执行代码。

Dojo编译器是一个可以让代码在客户端运行更高效的工具,包括自定义代码,模块与CSS。如果处理得当,你可以为你的应用创建一个易管理与维护的编译系统。

基础

Dojo的编译系统可以很复杂,且是高定制化的。它被设计成可扩展的,虽然本教程没有讲到它。另外,新的编译系统在Dojo 1.7引入,且对之前的版本做了向后兼容,它几乎完全被重写,很多情况下它可以做的更好,以至于让你不用再看编译后的代码,尤其是当你在应用中使用AMD语法时。

在开始之前,有几个贯穿本教程的核心概念需要来理解一下。

模块与包

但愿你在查看本教程之前已经理解了模块的概念。如果没有,你应该先看看定义模块的教程,它是Dojo 1.10的基础。模块被组织成包,把逻辑相关的模块组在一起。在Dojo 1.10中,每个包都应该有个描述包的package.json文件。很多包还应有package.js文件,用于提供Dojo指定的编译信息。

Dojo配置

内在的,Dojo有很多配置项可以在应用中使用。配置不仅在应用运行时显得重要,它还影响应用的编译,潜在的提供这些配置作为编译处理的部分。如果你还不熟悉Dojo的配置,你应用先看看使用dojoConfig配置Dojo。

层本质上是一个单独的JavaScript文件,包含了几个模块,有时候是其他资源。一个层通常是你要查找的编译输出,创建这个文件后你的应用就变为可分发的了。一个层可以是一个启动层,它包含Dojo的启动代码,允许Dojo载入其他模块。你拥有什么层及它里面是什么内容,这取决与你的应用与设计,没有一个统一的方式。

编译配置文件

编译配置文件是一个很小的JavaScript文件,它为编译器提供了改如何编译代码的信息。在就的编译系统中,有一个配置,在util/buildscripts/profiles目录下,包含了你需要的所有东西。从Dojo 1.7开始,变得较为灵活分散了,每个包都应该有个编译配置,然后应该还有一个主配置告诉编译器那些包和层应该编译,同样也可以提供优化代码的配置。

最小化

这个概念是将JavaScript代码压缩的更小,且保持原用功能。从效率的角度来看这是很棒的,一些开发者喜欢这样混淆代码,但是那样使调试变得困难。这是一个你不想开发编译的代码的原因。Dojo编译器使用两个工具来实现最小化。首先是ShrinkSafe,它只可以压缩1.7之前的代码。1.7之后的代码,编译使用Google的Closure Compiler。

移除无用代码

Google Closure Compiler的一个最大优势就是可以检测出不被执行到的代码,在压缩版本中将它们删除。几年前,我们启动了一个叫Dojo Linker的项目以解决类似的问题,但一直没有时间来完成它,所以有个可选方案我们很开心。Dojo设计时曾构思过该功能。有些“把手”在编译的时候可以设置,使编译器在路径中“硬编码”到输出代码中,当开始优化的时候,Closure Compiler检测那些执行不到的代码并删除它们,以达到缩小文件的目的。

编译控制,传输与分析器

它们都是编译系统的基础编译块。你不需要特意的去了解他们,但是如果你对高级编译感兴趣,可以修改它们来做些有趣的事情。编译控制是编译指令的集合,读取配置来决定要使用什么传输与分析器。传输就是做些如它字面上所说的,谈论一些东西然后传输到其他东西上;分析器是编译时解决AMD插件模块。例如,如果你使用dojo/text加载一个挂件模板,编译时会将模板文件放入压缩文件中。

你需要什么

为了使用Dojo的编译系统,你必须有完整的Dojo SDK。标准版已经使用编译系统编译过的。编译工具自身依赖于Java(与可选的快速方案:Node.js),所以要确保这些依赖你已经安装好了。

布置你的应用

这是组大的一个挑战,尤其是当你要超越基础应用时。我们注意那些从Dojo 1.6甚至更早版本迁移过来的人有一个与我们预期相差深远的应用结构。这意味着有相当多的人要面对迁移他们应用到1.7或者更新版本的挑战。所以在你还没有深入完成你的应用的时候,你需要考虑一些你的文件结构。

在根目录有个类似src的文件夹装着所有的包。大概看起这样:

技术分享

Dojo的主要(dojo,dijit和dojox)包与其他包在同一级目录,包括你的自定义包,加上Dojo的工具包(util)。

如果你不像一点点来布置,Dojo Boilerplate有你需要运行的一切,不仅仅是基础的应用框架,而是已经配置的可以直接编译。

未来使用编译系统来编译,应用的包目录中必须有两个文件。首先是一个CommonJS Packages/1.0包描述,通常命名为package.json,放置在包的根目录。第二个就是编译配置文件,包含有描述让编译工具如何处理包的内容。这个两个主要约定的命名文件。在Dojo工具集中,它被命名为<packagename>.profile.js且放置在包的根目录。这有个替代方案,就是包配置文件命名为package.js放置在包的根目录下。在Dojo功能包中将会看到这种做法。

有一点需要说明,你必须把你的整个应用代码放置在一个包中,如果有需要把你的代码分开(如为了代码重用,给拆分成了shared/common模块),你需要为它们每个包创建这些文件。

包描述

包描述文件(package.json)提供了当前包的一些信息,如包名,依赖包,认证许可和bug追踪等等。对于编译系统而言,比较关键的是键dojoBuild,最好提供一个名字,版本和描述。dojoBuild用于指向包的编译配置文件。例如一个名为app的包,它的描述文件package.js如下:

 1 { 2     "name": "app", 3     "description": "My Application.", 4     "version": "1.0", 5     "keywords": ["JavaScript", "Dojo", "Toolkit", "DojoX"], 6     "maintainers": [{ 7         "name": "Kitson Kelly" 8     }], 9     "contributors": [{10         "name": "Kitson Kelly"11     },{12         "name": "Colin Snover"13     }],14     "licenses": [{15         "type": "AFLv2.1",16         "url": "http://bugs.dojotoolkit.org/browser/dojox/trunk/LICENSE#L43"17     },{18         "type": "BSD",19         "url": "http://bugs.dojotoolkit.org/browser/dojox/trunk/LICENSE#L13"20     }],21     "bugs": "https://github.com/example/issues",22     "repositories": [{23         "type": "git",24         "url": "http://github.com/example.git",25         "path": "packages/app"26     }],27     "dependencies": {28         "dojo": "~1.10.3",29         "dijit": "~1.10.3",30         "dojox": "~1.10.3"31     },32     "main": "src",33     "homepage": "http://example.com/",34     "dojoBuild": "app.profile.js"35 }

 

CommonJS Packages/1.0提供了包描述可能选项的全部列表。如果你的代码只是内部使用,你可以将它精简,但最起码要有dojoBuild选项。

包编译配置

包编译配置文件是编译系统的主要配置。它是一个JavaScript文件,用于构建一个配置对象,包含创建功能齐全的应用的指令。一个基本的编译配置看起来像这个样子:

1 var profile = (function(){2     return {3         resourceTags: {4             amd: function(filename, mid) {5                 return /\.js$/.test(filename);6             }7         }8     };9 })();

 

注意我们执行了一个匿名函数。这可以确保环境中的其他代码不会影响你的配置文件。这也给了你机会来更灵活的生成包含计算的配置对象。

编译配置由dojoBuild连接,如果它不是应用的主编译配置,只需要包含resourceTags指令即可。

这提供了编译器编译你的包的最小化信息。至于编译器读取这个包的时候发生了什么,将每个文件都交由resourceTags的函数来决策它是否接收处理。这些函数有两个参数,文件名与模块ID。如果函数返回true这个这个标签被应用(当然返回false的时候就不应用)。

标记这些js结尾的文件为AMD模块,编译器同样会处理它们,而不会假定他们是遗留的Dojo模块。这里还有些其他的标记,需要你想用它们来标记你的资源:

amd

AMD模块资源

declarative

这个资源用于声明标记,你想要为依赖而扫描的包。

test

这个资源是包测试代码的一部分。

copyOnly

这个资源只需要复制到目标目录即可,其他的都保持不变。

miniExclude

如果已经是最小化的资源了,则不需要复制到目标目录。

如果你没有标记你的模块为amd,但它确实是的,编译器会提示这个模块是AMD并继续处理,但最好能明确的标记你的代码。

使用declarative标记已经超过了本教程的范围。关于它的更多信息请参阅参考指南depsDeclarative。

确定你适当的标记了你的资源用于编译器适当的处理他们是很重要的。我们假设你在目录src/app/tests下放置了测试代码(因为所有好的程序猿都会为他的包做单元测试,不是吗?),加上profile.json与一些其他类型的文件我想要只复制它们。所以一个更完整的配置应该是这样的:

 1 var profile = (function(){ 2     var testResourceRe = /^app\/tests\//, 3         // checks if mid is in app/tests directory 4   5         copyOnly = function(filename, mid){ 6             var list = { 7                 "app/app.profile": true, 8                 // we shouldn‘t touch our profile 9                 "app/package.json": true10                 // we shouldn‘t touch our package.json11             };12             return (mid in list) ||13                 (/^app\/resources\//.test(mid)14                     && !/\.css$/.test(filename)) ||15                 /(png|jpg|jpeg|gif|tiff)$/.test(filename);16             // Check if it is one of the special files, if it is in17             // app/resource (but not CSS) or is an image18         };19  20     return {21         resourceTags: {22             test: function(filename, mid){23                 return testResourceRe.test(mid) || mid=="app/tests";24                 // Tag our test files25             },26  27             copyOnly: function(filename, mid){28                 return copyOnly(filename, mid);29                 // Tag our copy only files30             },31  32             amd: function(filename, mid){33                 return !testResourceRe.test(mid)34                     && !copyOnly(filename, mid)35                     && /\.js$/.test(filename);36                 // If it isn‘t a test resource, copy only,37                 // but is a .js file, tag it as AMD38             }39         }40     };41 })();

 

正如你看到的,你可以很快很复杂,但是这个概念的本质是,配置文件需要一个包含resourceTags的哈希,包含了代表不同标记的函数集。你可以利用强大的JavaScript来为不同的资源指派对应的标记。

对于单独的包配置文件,以及如何标记它的资源,你可以看看dgrid的配置文件。

应用编译配置

为了把一个包进一个编译版本,你只需要完成上面说的这些,标记你的资源。但是如果想要创建一个有利于生成环境的编译版本,我们还需要一些其他选项。有两种做法,如果你的应该只有一个简单的包,包含了所有的自定义代码,你也许想要在应用中创建一个全的包配置文件。如果你的应用比较复杂且有多个包,或者你想要为不同的编译设置不同的编译配置,你应该创建一个应用配置文件。

接下来的教程,我们假定你要创建一个应用级别的配置,叫做myapp.profile.js,在应用的根目录下放着。

一些用于为创建全的编译配置的结构的关键选项如下:

OptionTypeDescription
basePathPath这是编译的根目录,接下类的编译都从这里开始计算。关联编译配置的文件位置。
releaseDirPath编译目标目录,编译器会覆盖它发现的一切,与basePath相关联。
releaseNameString

它作为发行版本的输出名称。它跟随在releaseDir之后。例如,如果你要释放代码到release/prd中,设置releaseDir为release,

设置releaseName为prd 

actionString应该设置为release
packagesArray编译器使用到的模块。用于灵活的指向其他地方的模块,在编译的时候和自定义代码放在一起。
layersObject允许创建不同的层模块作为编译版本的一部分,把谨慎的功能编译进一个单独的文件。

 假如我们要构建一个应用配置,我们有两个文件想要载入到单页应用中。一个是我们代码的依赖,另一个是在某种情况下根据需要载入:

 1 var profile = (function(){ 2     return { 3         basePath: "./src", 4         releaseDir: "../../app", 5         releaseName: "lib", 6         action: "release", 7   8         packages:[{ 9             name: "dojo",10             location: "dojo"11         },{12             name: "dijit",13             location: "dijit"14         },{15             name: "dojox",16             location: "dojox"17         },{18             name: "app",19             location: "app"20         }],21  22         layers: {23             "dojo/dojo": {24                 include: [ "dojo/dojo", "dojo/i18n", "dojo/domReady",25                     "app/main", "app/run" ],26                 customBase: true,27                 boot: true28             },29             "app/Dialog": {30                 include: [ "app/Dialog" ]31             }32         }33     };34 })();

 

如果我们现在编译这个配置,会如何呢,会在app/lib下包含我们四个包的模块,加上两个特殊命名的文件app/lib/dojo/dojo.js和app/lib/app/Dialog.js,它们包含了它们需要的模块的编译版本。

这里我们忽略了层的复杂性,后面我们会深入了解它。

编译优化

移除不会被执行到的代码

缺省配置

把他们放到一起

编译

总结

其他资源

 

翻译 - 【Dojo Tutorials】Creating Builds