RequireJS進階-模塊的優化及配置的詳解
概述
關於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: ‘./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"
- },
參見: RequireJS進階:配置文件的學習
map
map: {},
參見: RequireJS進階:配置文件的學習
packages
packages: [],
參見: RequireJS進階:配置文件的學習
dir
dir: "../some/path"
文件輸出的頂級目錄。如果沒有指定的話,默認會創建一個“build”目錄在build文件的路徑中。所有相對路徑是相對於構建文件。
keepBuildDir
keepBuildDir: true
在 RequireJS 2.0.2 中,輸出目錄的所有資源會在 build 前被刪除。值為 true 時 rebuild 更快,但某些特殊情景下可能會出現無法預料的異常
shim
shim: {},
參見: RequireJS進階:配置文件的學習
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
參見: RequireJS進階:配置文件的學習
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
RequireJS進階-模塊的優化及配置的詳解