概述
关于RequireJS已经有很多文章介绍过了。这个工具可以将你的JavaScript代码轻易的分割成苦干个模块(module)并且保持你的代码模块化与易维护性。这样,你将获得一些具有互相依赖关系的JavaScript文件。仅仅需要在你的HTML文档中引用一个基于RequireJS的脚本文件,所有必须的文件都将会被自动引用到这个页面上.
但是,在生产环境中将所有的JavaScript文件分离,这是一个不好的做法。这会导致很多次请求(requests),即使这个些文件都很小,也会浪费很多时间。 可以通过合并这些脚本文件,以减少请求的次数达到节省加载时间的目的。
另一种节省加载时间的技巧是缩小这些被加载文件的大小,相对小一些的文件会传输的更快一些。这个过程叫作最小化 (minification)
,它是通过小心的改变脚本文件的代码结构并且不改变代码的形为(behavior)和功能(functionality)来实现的。例如这些:去除不必要的空格,缩短(mangling,或都压缩)变量(variables)名与函数(methods,或者叫方法)名,等等。这种合并并压缩文件的过程叫做代码优化(
optimization)。这种方法除了用于优化(optimization)JavaScript文件,同样适用于CSS文件的优化。
RequireJS有两个主要方法(method):
define()和require()。这两个方法基本上拥有相同的定义(declaration)
并且它们都知道如何加载的依赖关系,然后执行一个回调函数(callback function)。与require()不同的是,
define()用来存储代码作为一个已命名的模块。
因此define()的回调函数需要有一个返回值作为这个模块定义。这些类似被定义的模块叫作AMD (Asynchronous Module
Definition,异步模块定义)。
下面通过一个示例讲解如何优化RequireJS项目。(这段话来自下面的参考连接)
所需环境
Node.js、r.js、RequireJS、以及示例程序,示例程序的下载地址为:http://www.webdeveasy.com/code/optimize-requirejs-projects/todo-mvc.zip
配置参数介绍
涉及的操作请可以参考上面的示例,本文的主要目的主要是通过示例来讲解配置参数的使用。
appDir
appDir: './'
应用程序的*目录,如果这个选项被开启的话,那么你的脚本文件是这个目录路径下的一个子目录。这个是个可选项。如果没有设置的话,则通过baseUrl这个参数下的锚点来查找文件(?)。如果设置了该选项的话,那么这个路径下的所有文件都会被复制到dir指定的输出目录,并且baseUrl这个目录的路径是相对于该路径。
baseUrl
baseUrl: './js'
默认情况下,所以的模块都相对于这个路径存在(有人称为脚本的跟路径),如果baseUrl没有明确指定的话,那么所有的模块路径都相对与build构建文件的路径。如果appDir已设置,那么baseUrl 的路径是相对与appDir。
mainConfigFile
mainConfigFile: '../some/path/to/main.js'
RequireJS的主配置文件。这里要区分配置文件和入口文件的区别,示例中的main.js即包含配置文件同时又是文件的入口文件。
paths
- paths: {
- "foo.bar": "../scripts/foo/bar",
- "baz": "../another/path/baz"
- },
map
map: {},
packages
packages: [],
dir
dir: "../some/path"
文件输出的*目录。如果没有指定的话,默认会创建一个“build”目录在build文件的路径中。所有相对路径是相对于构建文件。
keepBuildDir
keepBuildDir: true
在 RequireJS 2.0.2 中,输出目录的所有资源会在 build 前被删除。值为 true 时 rebuild 更快,但某些特殊情景下可能会出现无法预料的异常
shim
shim: {},
wrapShim
wrapShim: false,
禁止非模块包裹define函数。
locale
locale: "en-us",
国际化配置设置。
optimize
optimize: "uglify",
优化脚本文件的方式。值只能取下面的任何值中的一个。
"uglify": (default) uses UglifyJS to minify the code.
"uglify2": in version 2.1.2+. Uses UglifyJS2.
"closure": uses Google's Closure Compiler in simple optimizationmode to minify the code. Only available if running the optimizer using Java.
"closure.keepLines": Same as closure option, but keeps line returns in the minified files.
"none": no minification will be done.
skipDirOptimize
skipDirOptimize: false,
当设置为true时,优化器将会跳过非构建中被约束的JS文件。
generateSourceMaps
generateSourceMaps: false,
是否生成SourceMaps文件,什么是SourceMaps,参考SourceMaps
normalizeDirDefines
normalizeDirDefines: "skip",
2.1.11中:如果dir被声明且不为”none”,并且skipDirOptimize 为false,通常所有的JS文件都会被压缩,这个值自动设置为”all”。为了让JS文件能够在压缩过正确执行,优化器会加一层define()调用并且会插入一个依赖数组。当然,这样会有一点点慢如果有很多文件或者有大文件的时候。所以,设置该参数为”skip”这个过程就不会执行,如果optimize设置为”none”也一样。如果你想手动设置值的话:
优化后:如果你打算压缩没在modules声明的JS文件,在优化器执行过后,你应该设置这个值为”all”
优化中:但在动态加载过后,你想做一个会文件优化,但不打算在动态加载这些文件可以设置成”skip”
最后:所有生成的文件(无论在不在modules里声明过)自动标准化
uglify
- uglify: {
- toplevel: true,
- ascii_only: true,
- beautify: true,
- max_line_length: 1000,
- //How to pass uglifyjs defined symbols for AST symbol replacement,
- //see "defines" options for ast_mangle in the uglifys docs.
- defines: {
- DEBUG: ['name', 'false']
- },
- //Custom value supported by r.js but done differently
- //in uglifyjs directly:
- //Skip the processor.ast_mangle() part of the uglify call (r.js 2.0.5+)
- no_mangle: true
- },
使用UglifyJS进行代码压缩,具体参数配置可见UglifyJS
uglify2
- uglify2: {
- //Example of a specialized config. If you are fine
- //with the default options, no need to specify
- //any of these properties.
- output: {
- beautify: true
- },
- compress: {
- sequences: false,
- global_defs: {
- DEBUG: false
- }
- },
- warnings: true,
- mangle: false
- },
使用UglifyJS2进行代码压缩, 具体参数配置可见UglifyJS2
closure
- closure: {
- CompilerOptions: {},
- CompilationLevel: 'SIMPLE_OPTIMIZATIONS',
- loggingLevel: 'WARNING'
- },
如果用Closure Compiler优化,这个参数可以用来配置Closure Compiler,详细请看Closure Compiler的文档
optimizeCss
optimizeCss: "standard.keepLines.keepWhitespace",
是否优化CSS文件,以那种方式优化。
- "standard": @import inlining and removal of comments, unnecessary whitespace and line returns.Removing line returns may have problems in IE, depending on the type of CSS.
- "standard.keepLines": like "standard" but keeps line returns.
- "none": skip CSS optimizations.
- "standard.keepComments": keeps the file comments, but removes linereturns. (r.js 1.0.8+)
- "standard.keepComments.keepLines": keeps the file comments and linereturns. (r.js 1.0.8+)
- "standard.keepWhitespace": like "standard" but keeps unnecessary whitespace.
cssImportIgnore
cssImportIgnore: null,
是否忽略 CSS 资源文件中的 @import 指令
cssIn
cssIn: "path/to/main.css",
一般用于命令行,可将多个 CSS 资源文件打包成单个 CSS 文件
out
out: "path/to/css-optimized.css",
一般用于命令行,可将多个 CSS 资源文件打包成单个 CSS 文件
cssPrefix
cssPrefix: "",
如果”out”和”cssIn”不是同一目录,并且在cssIn文件里面有url()相对目录的,用这个去设置URL前置。仅仅在优化后URL不正确的情况下使用。
inlineText
inlineText: true,
处理所有的文本资源依赖项,从而避免为加载资源而产生的大量单独xhr请求
useStrict
useStrict: false,
是否开启严格模式, 由于很多浏览器不支持 ES5 的严格模式,故此配置默认值为 false
pragmas
- pragmas: {
- fooExclude: true
- },
指定生成编译指示。如果源文件包含类似如下注释:>>excludeStart(“fooExlude”,pragmas.fooExclude); >>excludeEnd(“fooExclude”);那么以//>>开头的注释就是编译指示。excludeStart/excludeEnd和includeStart/includeEnd起作用,在includeStart或excludeStart中的编译指示值将参与计算来判断Start和End之前的编译指示是include还是exclude。如果你可以选择用”has”或编译指示,建议用”has”代替。 编译指示比较难于阅读,但是它在对代码移除上比较灵活。基于”has”的代码必须遵守JavaScript规则。编译指示还可以在未压缩的代码中删除代码,而”has”只能通过UglifyJS或者Closure Compiler来做。
pragmasOnSave
- pragmasOnSave: {
- //Just an example
- excludeCoffeeScript: true
- },
和”pragmas”一样,但只能在文件保存的优化阶段应用一次。”pragmas”可以同时在依赖映射和文件保存优化阶段应用。有些”pragmas”可能不会在依赖映射时被执行,例如在CoffeeScript的loader插件中,只想CoffeeScript做依赖映射,但是一旦这个文件被保存为一个javascript文件,CoffeeScript compiler就没用了。那样的话,pragmasOnSave就会用于在保存期排除编译代码。
has
- has: {
- 'function-bind': true,
- 'string-trim': false
- },
使用”has”允许trimming代码树。基于js的特征检测:https://github.com/phiggins42/has.js。代码树修饰仅仅在使用UglifyJS或Closure Compiler压缩时发生。更多请见:http://requirejs.org/docs/optimization.html
hasOnSave
- hasOnSave: {
- 'function-bind': true,
- 'string-trim': false
- },
和pragmasOnSave类似。
namespace
namespace: 'foo',
命名空间,完整实例可以参考 http://requirejs.org/docs/faq-advanced.html
skipPragmas
skipPragmas: false,
跳过执行pragmas
skipModuleInsertion
skipModuleInsertion: false,
如果是false,文件就不会用define()来定义模块而是用一个define()占位符插入其中。另外,require.pause/resume调用也会被插入。设置为”true”来避免。这个参数用在你不是用require()来创建项目或者写js文件,但是又想使用RquireJS的优化工具来合并模块是非常有用的。
stubModules
stubModules: ['text', 'bar'],
将模块排除在优化文件之外。
optimizeAllPluginResources
optimizeAllPluginResources: false,
如果不是一个文件的优化,描述输出目录的所有.js文件的插件依赖,如果这个插件支持优化成为一个单独的文件,就优化它。可能是一个比较慢的优化过程。仅仅在有些插件用了像XMLHttpRequest不支持跨域,并且生成的代码会被放在另一个域名。
findNestedDependencies
findNestedDependencies: false,
寻找require()里面的require或define调用的依赖。默认为false是因为这些资源应该被认为是动态加载或者实时调用的。当然,有些优化场景也需要将它们合并在一起。
removeCombined
removeCombined: false
如果设置为true,在输出目录将会删除掉已经合并了的文件
modules
- modules: [
- //Just specifying a module name means that module will be converted into
- //a built file that contains all of its dependencies. If that module or any
- //of its dependencies includes i18n bundles, they may not be included in the
- //built file unless the locale: section is set above.
- {
- name: "foo/bar/bop",
- //create: true can be used to create the module layer at the given
- //name, if it does not already exist in the source location. If
- //there is a module at the source location with this name, then
- //create: true is superfluous.
- create: true,
- //For build profiles that contain more than one modules entry,
- //allow overrides for the properties that set for the whole build,
- //for example a different set of pragmas for this module.
- //The override's value is an object that can
- //contain any of the other build options in this file.
- override: {
- pragmas: {
- fooExclude: true
- }
- }
- },
- //This module entry combines all the dependencies of foo/bar/bop and foo/bar/bee
- //and any of their dependencies into one file.
- {
- name: "foo/bar/bop",
- include: ["foo/bar/bee"]
- },
- //This module entry combines all the dependencies of foo/bar/bip into one file,
- //but excludes foo/bar/bop and its dependencies from the built file. If you want
- //to exclude a module that is also another module being optimized, it is more
- //efficient if you define that module optimization entry before using it
- //in an exclude array.
- {
- name: "foo/bar/bip",
- exclude: [
- "foo/bar/bop"
- ]
- },
- //This module entry shows how to specify a specific module be excluded
- //from the built module file. excludeShallow means just exclude that
- //specific module, but if that module has nested dependencies that are
- //part of the built file, keep them in there. This is useful during
- //development when you want to have a fast bundled set of modules, but
- //just develop/debug one or two modules at a time.
- {
- name: "foo/bar/bin",
- excludeShallow: [
- "foo/bar/bot"
- ]
- },
- //This module entry shows the use insertRequire (first available in 2.0):
- {
- name: "foo/baz",
- insertRequire: ["foo/baz"]
- }
- ],
列出要优化的模块。如果有依赖i18n的,只有root层会被包含进来除非locale:块在上面被声明过。
①仅定义模块会被转换成一个生成目标文件的名字,包含所有依赖项。i18n依赖同上,
create:true可用来生成在源文件目录不存在的给定模块名。如果源文件目录已经存在一个相同名称的模块,create参数就没用了;
override:可以重写全局的pragmas
②这个模块声明编译foo/bar/bop的所有依赖和foo/bar/bee及其所有依赖
③编译所有foo/bar/bip的依赖到一个文件,但是排除foo/bar/bop和它的所有依赖文件,如果想把另一个模块单独优化,这是一个很好用的方法
④excludeShallow只排除掉这个模块,但是如果输出模块的依赖和它有相同就保留不排除。
⑤这个模块声明表示用insertRequire(在2.0中新加入)
insertRequire
insertRequire: ['foo/bar/bop'],
如果目标模块在顶层级只调用了define没有调用require(),并且输出文件在data-main中使用,如果顶层没有require,就不会有任何模块被加载。定义insertRequire在文件尾部来执行其它模块,更多参见:https://github.com/jrburke/almond
name
name: "foo/bar/bop",
include
include: ["foo/bar/bee"],
insertRequire
insertRequire: ['foo/bar/bop'],
out
out: "path/to/optimized-file.js",
如果只优化一个模块(和它的依赖项),而且是生成一个单文件,你可以在行内定义模块的选项,以代替modules参数的定义方式,”exclude”, “excludeShallow”, “include”和”insertRquire”都可以以兄弟属性的方式定义。
deps
deps: ["foo/bar/bee"],
“include”的替换方案。一般用requirejs.config()来定义并用mainConfigFile引入。
out
- out: function (text, sourceMapText) {
- //Do what you want with the optimized text here.
- //Starting in 2.1.10, if generateSourceMaps was set to true
- //and optimize: 'uglify2' was used, then the second argument
- //to this function, sourceMapText, will be the text of the source map.
- },
在2.0,”out”可以是一个函数, 对单个JS文件优化可以调用requirejs.optimize(), 用out函数表示优化过后的内容不会被写到磁盘,而是传递给out函数
out
out: “stdout”
在2.0.12+, 设置”out”为”stdout”, 优化输出会写到STDOUT,这对于r.js整合其它命令行工具很有用。为了避免额外的输出”logLevel: 4”应该被使用。
wrap
- wrap: {
- start: "(function() {",
- end: "}());"
- },
wrap任何东西在start和end之间,用于define/require不是全局的情况下,在end里可以暴露全局对象在文件中。
wrap
wrap: true,
wrap的另一种方式,默认是(function() { + content + }())
wrap
- wrap: {
- startFile: "parts/start.frag",
- endFile: "parts/end.frag"
- },
用文件来wrap
wrap
- wrap: {
- startFile: ["parts/startOne.frag", "parts/startTwo.frag"],
- endFile: ["parts/endOne.frag", "parts/endTwo.frag"]
- },
多个文件的wrap
fileExclusionRegExp
fileExclusionRegExp: /^./,
跳过任何以.开头的目录和文件,比如.files, .htaccess等
preserveLicenseComments
preserveLicenseComments: true,
默认注释有授权在里面。当然,在大项目生成时,文件比较多,注释也比较多,这样可以把所有注释写在文件的顶部。
logLevel
logLevel: 0
设置logLevel。
TRACE: 0,
INFO: 1
WARN: 2
ERROR: 3
SILENT: 4
throwWhen
- throwWhen: {
- optimize: true
- }
在2.1.3,有些情况下当错误发生时不会抛出异常并停止优化,你可能想让优化器在某些错误发生时停止,就可以使用这个参数
onBuildRead
- onBuildRead: function (moduleName, path, contents) {
- //Always return a value.
- //This is just a contrived example.
- return contents.replace(/foo/g, 'bar');
- },
当每个文件被读取的时候调用这个方法来改变文件内容
onBuildWrite
- onBuildWrite: function (moduleName, path, contents) {
- //Always return a value.
- //This is just a contrived example.
- return contents.replace(/bar/g, 'foo');
- },
允许在写入目标文件前执行方法改变内容
onModuleBundleComplete
- onModuleBundleComplete: function (data) {
- /*
- data.name: the bundle name.
- data.path: the bundle path relative to the output directory.
- data.included: an array of items included in the build bundle.
- If a file path, it is relative to the output directory. Loader
- plugin IDs are also included in this array, but depending
- on the plugin, may or may not have something inlined in the
- module bundle.
- */
- },
每个JS模块集完成后执行。 模块集是指一个modules数组项。
rawText
- rawText: {
- 'some/id': 'define(["another/id"], function () {});'
- },
在2.1.3,种子raw text是模块ID的列表。这些文本内容用于代替模块的文件IO调用。用于模块ID是基于用户动态输入的情况,在网页生成工具中常用。
cjsTranslate
cjsTranslate: true,
在2.0.2中。如果为true, 优化器会添加define(require, exports, module) {});包裹每一个没有调用define()的文件。
useSourceUrl
useSourceUrl: true,
在2.0.2,有点实验性质。每一个模块集最后都会添加一段//# sourceUrl的注释。
waitSeconds
waitSeconds: 7
skipSemiColonInsertion
skipSemiColonInsertion: false
在2.1.9,通常r.js插入一个分号在文件末尾,如果没有的话。
keepAmdefine
keepAmdefine: false
在2.1.10, 如果是true,就不会删除amdefine,详情见:https://github.com/jrburke/amdefine
allowSourceOverwrites
allowSourceOverwrites: false
在2.1.11中, 作为修复BUG的一部分https://github.com/jrburke/r.js/issues/444。设置为true就允许源代码进行重写覆盖。当然,为了安全起见,请正确配置,比如你可能想设置”keepBuildDir”为true。
帮助文档
参考链接:http://jiongks.sinaapp.com/blog/build-any-web-project-with-requirejs-optimizer/
英文文档(r.js):https://github.com/jrburke/r.js/blob/master/build/example.build.js
参考链接:http://www.oschina.net/translate/optimize-requirejs-projects
参考链接:http://www.cnblogs.com/haoliang/p/3656475.html
参考链接:http://www.yfznw.com/node/22