官網:http://www.css88.com/doc/webpack2/concepts/css
webpack是js的模塊打包器(module bundler)。html
webpack將建立全部應用程序的依賴關係圖標(dependency graph)。node
入口起點(entry point):圖表的起點。根上下文(contextual root) app第一個啓動文件。webpack
入口起點告訴webpack從哪裏開始,遵循依賴圖表打包文件。git
webpack中使用webpack配置對象的entry屬性定義入口。github
用法: entry: string | Array<string>web
webpack.config.jsnpm
1 module.exports = { 2 entry: './path/to/my/entry/file.js' 3 }; 4 module.exports = config;
entry屬性的單個入口語法,是下面的簡寫:編程
1 module.exports = { 2 entry: { 3 main: './path/to/my/entry/file.js' 4 } 5 };
向entry傳入一個數組會發生什麼?將建立多個入口。json
用法:entry: {[entryChunkName: string]: string | Array<string>}
const config = { entry: { app: './src/app.js', // 應用程序(app) vendors: './src/vendors.js' // 公共庫(vendor) } };
對象語法是應用程序中定義入口的最可擴展的方式。
可擴展的webpack配置:可重用並可與其餘配置組合使用。用於將關注點從環境、構建目標、運行時中分離,而後用專門的工具(如 webpack-merge)將它們合併。
全部的資源(assets)歸攏在一塊兒後,須要告訴webpack在哪裏打包應用程序。
output屬性,描述了如何處理歸攏在一塊兒的代碼(bundled code)。
webpack.config.js
module.exports = { entry: './path/to/my/entry/file.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'my-first-webpack.bundle.js' } };
將output設置爲一個對象,包括如下兩點:
1.編譯文件的文件名(filename),推薦:main.js || bundle.js || index.js
2.output.path對應一個絕對路徑(一次性打包的目錄)。
webpack.config.js
const config = { output: { filename: 'bundle.js', path: '/home/proj/public/assets' } }; exports.module = config;
能夠向output屬性傳入的值。
output.chuckFilename
非入口的 chunk(non-entry chunk) 的文件名,路徑相對於 output.path
目錄。
[id]
被 chunk 的 id 替換。
[name]
被 chunk 的 name 替換(或者,在 chunk 沒有 name 時使用 id 替換)。
[hash]
被 compilation 生命週期的 hash 替換。
[chunkhash]
被 chunk 的 hash 替換。
output.crossOriginLoading
此選項啓用跨域加載(cross-origin loading) chunk。
可選的值有:
false
- 禁用跨域加載
"anonymous"
- 啓用跨域加載。當使用 anonymous
時,發送不帶憑據(credential)的請求。
"use-credentials"
- 啓用跨域加載。發送帶憑據(credential)的請求。
output.devtoolLineToLine
全部/指定模塊啓用行到行映射(line-to-line mapped)模式。
行到行映射模式使用一個簡單的 SourceMap,即生成資源(generated source)的每一行都映射到原始資源(original source)的同一行。這是一個可作性能優化之處。
true
在全部模塊啓用(不推薦)
{test, include, exclude}
對象,對特定文件啓用(相似於 module.loaders
)。
默認值:false
output.filename
指定硬盤每一個輸出文件的名稱。
單個入口
{ entry: './src/app.js', output: { filename: 'bundle.js' path: __dirname + 'build', } } // 寫入到硬盤: ./build/bundle.js
多個入口
如配置建立多個「chunk」(例如使用多個入口起點或使用相似CommonsChunkPlugin(提取公共代碼)的插件),爲確保每一個文件名都不重複,應使用如下替換方式:
[name]
被 chunk 的 name 替換。
[hash]
被 compilation 生命週期的 hash 替換。
[chunkhash]
被 chunk 的 hash 替換。
{ entry: { app: './src/app.js', search: './src/search.js' }, output: { filename: '[name].js', path: __dirname + '/build' } } // 寫入到硬盤:./build/app.js, ./build/search.js
output.hotUpdateChunkFilename
熱更新 chunk(Hot Update Chunk) 的文件名。在 output.path
目錄中。
[id]
被 chunk 的 id 替換。
[hash]
被 compilation 生命週期的 hash 替換。(最後一個 hash 存儲在記錄中)
默認值:"[id].[hash].hot-update.js"
output.hotUpdateFunction
webpack 中用於異步加載(async load)熱更新(hot update) chunk 的 JSONP 函數。
默認值:"webpackHotUpdate"
output.hotUpdateMainFilename
熱更新主文件(hot update main file)的文件名。
[hash]
被 compilation 生命週期的 hash 替換。(最後一個 hash 存儲在記錄中)
默認值:"[hash].hot-update.json"
output.jsonpFunction
webpack 中用於異步加載(async loading) chunk 的 JSONP 函數。
較短的函數可能會減小文件大小。當單頁有多個 webpack 實例時,請使用不一樣的標識符(identifier)。
默認值:"webpackJsonp"
output.library
若是設置此選項,會將 bundle 導出爲 library。output.library
是 library 的名稱。
若是你正在編寫 library,而且須要將其發佈爲單獨的文件,請使用此選項。
output.libraryTarget
library 的導出格式
"var"
- 導出爲一個變量:var Library = xxx
(默認)
"this"
- 導出爲 this
的一個屬性:this["Library"] = xxx
"commonjs"
- 導出爲 exports
的一個屬性:exports["Library"] = xxx
"commonjs2"
- 經過 module.exports
:module.exports = xxx
導出
"amd"
- 導出爲 AMD(可選命名 - 經過 library 選項設置名稱)
"umd"
- 導出爲 AMD,CommonJS2 或者導出爲 root 的屬性
默認值:"var"
若是 output.library
未設置,可是 output.libraryTarget
被設置爲 var
之外的值,則「所導出對象」的每一個屬性都被複制到「對應的被導出對象」上(除了 amd
,commonjs2
和 umd
)。
output.path
導出目錄爲絕對路徑(必選項)。
[hash]
被 compilation 生命週期的 hash 替換。
config.js
output: { path: "/home/proj/public/assets", publicPath: "/assets/" }
index.html
<head> <link href="/assets/spinner.gif" /> </head>
接下來是一個更復雜的例子,來講明對資源使用 CDN 和 hash。
config.js
output: { path: "/home/proj/cdn/assets/[hash]", publicPath: "http://cdn.example.com/assets/[hash]/" }
注意:在編譯時不知道最終輸出文件的 publicPath
的狀況下,publicPath
能夠留空,而且在入口起點文件運行時動態設置。若是你在編譯時不知道 publicPath
,你能夠先忽略它,而且在入口起點設置 __webpack_public_path__
。
__webpack_public_path__ = myRuntimePublicPath // 其餘的應用程序入口
output.sourceMapFilename
JavaScript 文件的 SourceMap 的文件名。它們在 output.path
目錄中。
[file]
被 JavaScript 文件的文件名替換。
[id]
被 chunk 的 id 替換。
[hash]
被 compilation 生命週期的 hash 替換。
默認值:"[file].map"
webpack的目標是讓webpack聚焦於項目中的全部資源(asset),而瀏覽器不須要關注這些(並非資源都必須打包在一塊兒)。webpack把每一個文件(.css, .html, .scss, .jpg, etc.) 都做爲模塊處理。並且webpack只理解js。
loader是對應用程序中資源文件進行轉換。它們是(運行在Node.js中的)函數,能夠將資源文件做爲參數的來源,而後返回到新的資源文件。
webpack配置的目標:
1.識別(identity)被對應loader轉換(transform)的文件
2. 進行過文件轉換,能夠將被轉換的文件添加到依賴圖表(最終添加到bundle中)(use屬性)
webpack.config.js
const config = { entry: './path/to/my/entry/file.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'my-first-webpack.bundle.js' }, module: { rules: [ // 當遇到【在require() / import語句中被解析爲'.js'或'.jsx'的路徑】時,把它們添加並打包前,要先使用babel-loader轉換 {test: /\.(js | jsx)$/, use: 'babel-loader'} ] } }; module.exports = config;
在webpack配置中定義loader時,要定義在module.rules中,而不是rules。定義錯時webpack會提出嚴重警告。
可以使用 loader 告訴 webpack 加載 CSS 文件,或者將 TypeScript 轉爲 JavaScript。
首先,安裝相對應的 loader:
npm install --save-dev css-loader
npm install --save-dev ts-loader
其次,配置 webpack.config.js
,對每一個 .css
文件使用 css-loader
,對每一個 .ts
文件使用 ts-loader
:
webpack.config.js
module.exports = { module: { rules: [ {test: /\.css$/, use: ['css-loader'](loaders/css-loader)}, {test: /\.ts$/, use: ['ts-loader']((https://github.com/TypeStrong/ts-loader))} ] } };
注意,根據配置選項,下面的規範定義了同等的 loader 用法:
{test: /\.css$/, [loader](/configuration/module#rule-loader): 'css-loader'} // or equivalently {test: /\.css$/, [use](/configuration/module#rule-use): 'css-loader'} // or equivalently {test: /\.css$/, [use](/configuration/module#rule-use): { loader: 'css-loader', options: {} }}
在你的應用程序中,有三種方式使用 loader:
require
語句中顯示使用webpack.config.js
module.rules
容許在 webpack 配置中指定幾個 loader。
這是展現 loader 的一種簡明的方式,而且有助於簡潔代碼,以及對每一個相應的 loader 有一個完整的概述。
module: { rules: [ { test: /\.css$/, use: [ {loader: 'style-loader'}, { loader: 'css-loader', options: { module: true } } ] } ] }
require
能夠在 require
語句(或 define
, require.ensure
, 等語句)中指定 loader。使用 !
將資源中的 loader 分開。分開的每一個部分都相對於當前目錄解析。
require('style-loader!css-loader?modules!./styles.css');
經過前置全部規則及使用 !
,能夠對應覆蓋到配置中的任意 loader。
選項能夠傳遞查詢參數,就像在 web 中那樣(?key=value&foo=bar
)。也可使用 JSON 對象(?{"key":"value","foo":"bar"}
)。
儘量使用 module.rules
,由於能夠在源碼中減小引用,而且更快調試和定位 loader,避免代碼愈來愈糟。
可選項,經過 CLI 使用 loader:
webpack --module-bind jade --module-bind 'css=style!css'
對 .jade
文件使用 jade-loader
,對 .css
文件使用 style-loader
和 css-loader
。
options
對象進行配置。package.json
常見的 main
屬性,還能夠將普通的 npm 模塊導出爲 loader,作法是在 package.json
裏定義一個 loader 字段。loader 經過(loader)預處理函數,爲 JavaScript 生態系統提供了更多有力功能。用戶如今能夠更加靈活的引入細粒度邏輯,例如壓縮(compression)、打包(package)、語言翻譯(language translation)和其餘更多。
loader 遵循標準的模塊解析。多數狀況下,loader 將從模塊路徑(一般是 npm install
, node_modules
)解析。
如何編寫模塊?
loader 模塊須要導出爲一個函數,而且使用 Node.js 兼容的 JavaScript 編寫。一般使用 npm 管理 loader,也能夠將 loader 模塊做爲應用程序中的文件。
按照約定,loader 一般被命名爲 XXX-loader
,其中 XXX
是上下文的名稱,例如 json-loader
。
loader 的名稱約定和優先搜索順序,由 webpack 配置 API 中的 resolveLoader.moduleTemplates
定義。
loader僅在每一個文件的基礎上執行轉換,插件目的在於解決loader沒法實現的事情。
插件(plugins)最經常使用(但不限)於在打包模塊的「compilation」和「chunk」生命週期執行操做和自定義功能。
webpack的插件系統強大且可定製化。
在一個配置中,屢次使用一個插件,用於不一樣的目的。
1.想使用一個插件,只需require()它,而後把它添加到plugins數組中。多數插件能夠經過選項(option)自定義。
2.須要使用new來建立插件的實例,而且經過實例來調用插件。
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // installed via npm(經過npm安裝) const webpack = require('webpack'); //to access built-in plugins(訪問內置插件) const path = require('path'); const config = { entry: './path/to/my/entry/file.js'; output: { path: path.resolve(__dirname, 'dist'), filename: 'my-first-webpack.bundle.js' }, module: { rules: [ {test: /\.(js | jsx)$/, use: 'babel-loader'} ] }, plugins: [ new webpack.optimize.UglifyJsPlugin(), // 優化js組件 new HtmlWebpackPlugin({template: './src/index.html'}) ] }; module.exports = config;
webpack 插件是一個具備 apply
屬性的 JavaScript 對象。 apply
屬性會被 webpack compiler 調用,而且 compiler 對象可在整個 compilation 生命週期訪問。
ConsoleLogOnBuildWebpackPlugin.js
function ConsoleLogOnBuildWebpackPlugin() { }; ConsoleLogOnBuildWebpackPlugin.prototype.apply = function(compiler) { compiler.plugin('run', function(compiler, callback) { console.log("webpack 構建過程開始!!!"); callback(); }); };
經過Function.prototype.apply方法能夠把任意函數做爲插件傳遞(this
將指向 compiler
)。在配置中使用這樣的方式來內聯自定義插件。
plugin 能夠攜帶參數/選項,在 wepback 配置中,需向 plugins
屬性傳入 new
實例。
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // installed via npm(經過npm安裝) const webpack = require('webpack'); //to access built-in plugins(訪問內置插件) const path = require('path'); const config = { entry: './path/to/my/entry/file.js'; output: { filename: 'my-first-webpack.bundle.js', path: path.resolve(__dirname, 'dist') }, module: { loaders: [ { test: /\.(js | jsx)$/, loader: 'babel-loader' } ] }, plugins: [ new webpack.optimize.UglifyJsPlugin(), // 優化js組件 new HtmlWebpackPlugin({template: './src/index.html'}) ] }; module.exports = config;
webpack 的配置文件是 JavaScript 文件導出的一個對象。此對象,由 webpack 根據對象定義的屬性進行解析。
由於 webpack 配置是標準的 Node.js CommonJS 模塊,能夠以下:
require(...)
導入其餘文件require(...)
使用 npm 的工具函數?:
操做符不該該使用如下。從技術上講能夠這麼作,可是並不推薦:
--env
)時,訪問命令行接口(CLI)參數webpack.config.js
var path = require('path'); module.exports = { entry: './foo.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'foo.bundle.js' } };
webpack.config.js
var path = require('path'); var webpack = require('webpack'); var webpackMerge = require('webpack-merge'); var baseConfig = { target: 'async-node', entry: { entry: './entry.js' }, output: { path: path.resolve(__dirname, 'dist'), filename: '[name].js' }, plugin: [ new webpack.optimize.CommonsChunkPlugin({ name: 'inline', filename: 'inline.js', minChunks: Infinity }), new webpack.optimize.AggressiveSplittingPlugin({ minSize: 5000, maxSize: 10000 }), ] }; let targets = ['web', 'webworker', 'node', 'async-node', 'node-webkit', 'electron-main'].map((target) => { let base = webpackMerge(baseConfig, { target: target, output: { path: path.resolve(__dirname, 'diat/' + target), filename: '[name].' + target + '.js' } }); return base; }); module.exports = targets;
在模塊化編程,開發者將程序分解成稱爲模塊的離散功能塊。
對比 Node.js 模塊,webpack 模塊可以以各類方式表達它們的依賴關係,幾個例子以下:
import
語句require()
語句define
和 require
語句@import
語句。url(...)
)或 HTML 文件(<img src=...>
)中的圖片連接(image url) webpack 1 須要特定的 loader 來轉換 ES 2015 import
,然而經過 webpack 2 能夠開箱即用。
解析器是一個經過絕對路徑來幫助定位模塊的庫(library)。 一個模塊能夠做爲另外一個模塊的依賴模塊,而後被後者引用,以下:
import foo from 'path/to/module' // or require('path/to/module')
依賴模塊能夠來自應用程序代碼或第三方庫。解析器幫助 webpack
找到 bundle 中須要引入的模塊代碼,這些代碼包含在 require
/import
語句中。 當打包模塊時,webpack
使用加強解析來解析文件路徑
使用 enhanced-resolve
,webpack 可以解析三種文件路徑:
import "/home/me/file";
import "C:\\Users\\me\\file";
已經有了文件的絕對路徑,不須要進一步解析。
import "../src/file1";
import "./file2";
出現 import
或 require
的資源文件的目錄被認爲是上下文目錄(context directory)(當前處理文件的目錄)。在 import/require
中給定的相對路徑被追加到此上下文路徑(context path),以生成模塊的絕對路徑(absolute path)。
import "module";
import "module/lib/file";
模塊將在 resolve.modules
中指定的全部目錄內搜索。 能夠替換初始模塊路徑,此替換路徑經過使用 resolve.alias
配置選項來建立一個別名。
一旦根據上述規則解析路徑後,解析器(resolver)將檢查路徑是否指向文件或目錄。若是路徑指向一個文件:
resolve.extensions
] 選項做爲文件擴展名來解析,此選項告訴解析器在解析中可以接受哪些擴展名(例如 .js
, .jsx
)。若是路徑指向一個文件夾,則採起如下步驟找到具備正確擴展名的正確文件。
package.json
文件,則按照順序查找 resolve.mainFields
配置選項中指定的字段。而且 package.json
中的第一個這樣的字段肯定文件路徑。webpack 根據構建目標(build target)爲這些選項提供了合理的默認配置。
Loader 解析遵循與文件解析器指定的規則相同的規則。可是 resolveLoader
配置選項能夠用來爲 Loader 提供獨立的解析規則。
每一個文件系統訪問都被緩存,以便更快觸發對同一文件的多個並行或穿行請求。在觀察模式下,只有修改過的文件會從緩存中摘出。若是關閉觀察模式,在每次編譯前清理緩存。
任什麼時候候一個文件依賴於另外一個文件,webpack 把這個文件看成依賴處理。這使得 webpack 能夠接收非代碼資源(non-code asset)(例如圖像或 web 字體),而且也能把它們做爲依賴提供給應用。
webpack 從命令行或配置文件定義的一個模塊列表,開始處理應用。 從這些入口點開始,webpack 遞歸地構建一個依賴圖表,這個依賴圖表包括應用所需的每一個模塊,而後將全部模塊打包爲少許的包(bundle) - 一般只有一個包 - 可由瀏覽器加載。
對於 HTTP/1.1 客戶端,打包應用會尤爲強大,由於當瀏覽器發起一個新請求時,它可以最大限度地減小應用的等待次數。對於 HTTP/2,能夠經過 webpack 使用代碼拆分(Code Splitting)和打包實現最佳優化。
由於服務器和瀏覽器代碼均可以用 JavaScript 編寫,因此 webpack 提供了多種構建目標(target),你能夠在webpack 配置中設置。
要設置 target
屬性,只須要在你的 webpack 配置中設置 target 的值。
webpack.config.js
module.exports = { target: 'node' };
在上面例子中,使用 node
webpack 會編譯爲用於「類 Node.js」環境(使用 Node.js 的 require
,而不是使用任意內置模塊(如 fs
或 path
)來加載 chunk)。
每一個target都有各類部署(deployment)/環境(environment)特定的附加項,以支持知足其需求。查看target 的可用值。
儘管 webpack 不支持向 target
傳入多個字符串,你能夠經過打包兩份分離的配置來建立同構的庫:
webpack.config.js
var path = require('path'); var serverConfig = { target: 'node', output: { path: path.resolve(__dirname, 'dist'), filename: 'lib.node.js' } // ... }; var clientConfig = { target: 'web', // <=== 默認是'web',可省略 output: { path: path.resolve(__dirname, 'dist'), filename: 'lib.js' } //... }; module.exports = [serverConfig, clientConfig];
上面的例子將在dist
文件夾下建立 lib.js
和 lib.node.js
文件。
模塊熱替換功能會在應用程序運行過程當中替換、添加或刪除模塊,而無需從新加載頁面。在獨立模塊變動後,無需刷新整個頁面,就能夠更新這些模塊,極大地加速了開發時間。
能夠設置 HMR,使此進程自動觸發更新,或者選擇要求在用戶交互後進行更新。
除了普通資源,編譯器(compiler)須要發出 "update",以容許更新以前的版本到新的版本。"update" 由兩部分組成:
manifest 包括新的編譯 hash 和全部的待更新 chunk 目錄。
每一個待更新 chunk 包括用於與全部被更新模塊相對應 chunk 的代碼(或一個 flag 用於代表模塊要被移除)。
編譯器確保模塊 ID 和 chunk ID 在這些構建之間保持一致。一般將這些 ID 存儲在內存中(例如,當使用 webpack-dev-server 時),可是也可能將它們存儲在一個 JSON 文件中。
HMR 是可選功能,只會影響包含 HRM 代碼的模塊。舉個例子,經過 style-loader
爲 style 樣式追加補丁。 爲了運行追加補丁,style-loader
實現了 HMR 接口;當它經過 HRM 接收到更新,它會使用新的樣式替換舊的樣式。
相似的,當在一個模塊中實現了 HMR 接口,你能夠描述出當模塊被更新後發生了什麼。然而在多數狀況下,不須要強制在每一個模塊中寫入 HMR 代碼。若是一個模塊沒有 HMR 處理函數,更新就會冒泡。這意味着一個簡單的處理函數可以對整個模塊樹(complete module tree)進行處理。若是在這個模塊樹中,一個單獨的模塊被更新,那麼整個模塊樹都會被從新加載(只會從新加載,不會遷移)。
對於模塊系統的 runtime,附加的代碼被髮送到 parents
和 children
跟蹤模塊。
在管理方面,runtime 支持兩個方法 check
和 apply
。
check
發送 HTTP 請求來更新 manifest。若是請求失敗,說明沒有可用更新。若是請求成功,待更新 chunk 會和當前加載過的 chunk 進行比較。對每一個加載過的 chunk,會下載相對應的待更新 chunk。當全部待更新 chunk 完成下載,就會準備切換到 ready
狀態。
apply
方法將全部被更新模塊標記爲無效。對於每一個無效模塊,都須要在模塊中有一個更新處理函數,或者在它的父級模塊們中有更新處理函數。不然,無效標記冒泡,並將父級也標記爲無效。每一個冒泡繼續直到到達應用程序入口起點,或者到達帶有更新處理函數的模塊(以最早到達爲準)。若是它從入口起點開始冒泡,則此過程失敗。
以後,全部無效模塊都被(經過 dispose 處理函數)處理和解除加載。而後更新當前 hash,而且調用全部 "accept" 處理函數。runtime 切換回閒置
狀態,一切照常繼續。
能夠在開發過程當中將 HMR 做爲 LiveReload 的替代。webpack-dev-server 支持熱模式,在試圖從新加載整個頁面以前,熱模式會嘗試使用 HMR 來更新。查看如何實如今 React 項目中使用 HRM 爲例。
一些 loader 已經生成可熱更新的模塊。例如,style-loader
可以置換出頁面的樣式表。對於這樣的模塊,不須要作任何特殊處理。