webpack構建流程
從啟動webpack構建到輸出結果經歷了一系列過程:
- 解析webpack.config.js配置引數,呼叫shell並追加命令列引數,通過 optimist將前兩者引數整合成 options 物件傳到了下一個流程的控制物件中
- 註冊所有配置的外掛,讓外掛監聽webpack構建生命週期的事件節點,以做出對應的反應。
- 從配置的entry入口檔案開始解析檔案構建AST語法樹,找出每個檔案所依賴的檔案,遞迴下去。
- 在解析檔案遞迴的過程中根據檔案型別和loader配置找出合適的loader用來對檔案進行轉換。
- 遞迴完後得到每個檔案的最終結果,根據entry配置生成程式碼塊chunk。
- 輸出所有chunk到檔案系統。
入口和上下文(entry and context)
entry
指示 webpack 應該使用哪個模組,來作為構建其內部依賴圖的開始
-
單入口
entry: './src/app.js'
等同於下面寫法:
entry: {
main: './src/app.js'
}
複製程式碼
-
物件語法
分離 應用程式(app) 和 第三方庫(vendor) 入口
entry: {
app: './src/main.js',
vendor: './src/jquery.js'
}
複製程式碼
這告訴我們 webpack 從 main.js 和 jquery.js 開始建立依賴圖(dependency graph)。這些依賴圖是彼此完全分離、互相獨立的。
context
webpack 編譯時的基礎目錄,entry和loader 會相對於此目錄查詢
預設值為當前目錄,不建議修改
出口(output)
告訴 webpack 在哪裡輸出它所建立的 bundles
output: {
path: path.join(__dirname, 'dist'),
publicPath: '/',
filename: 'js/[name].js'
}
複製程式碼
publicPath
用於指定打包後的檔案需要載入的外部資源(如圖片、js、css等)的跟路徑
預設值是一個空字串 "",通常設定成"/"
靜態資源最終訪問路徑 = output.publicPath + 資源loader或外掛等配置路徑
- loader 輸出圖片檔案配置
{ name: 'imgs/[name].[ext]' }
// 那麼圖片最終的訪問路徑為
output.publicPath('/') + 'imgs/[name].[ext]' = '/imgs/[name].[ext]'
複製程式碼
- plugin 提取css檔案配置:
new ExtractTextPlugin('css/[name].[contenthash:10].css')
// html中載入css打包後程式碼
<link href="/css/app.9502b0c565.css" rel="stylesheet">
複製程式碼
- html中載入js打包後程式碼
<script type="text/javascript" src="/js/runtime.4ece365fd5.js"></script>
複製程式碼
path
打包檔案輸出的目錄
建議絕對路徑;預設值為當前路徑。
path 中用使用 [hash] 模板可用於版本回歸
output: {
path: path.resolve('./dist/[hash:8]/')
}
複製程式碼
loader
loader 讓 webpack 能夠去處理那些非 JavaScript 檔案(webpack 自身只理解 JavaScript)
注意:module.loaders 改為 module.rules;鏈式loader
- webpack1語法
module: {
loaders: [{
test: /\.less$/,
loader: "style!css!less"
})
}
複製程式碼
- webpack2、3語法
module: {
rules: [{
test: /\.less$/,
use: [
"style-loader",
"css-loader",
"less-loader"
]
}]
}
複製程式碼
檔案類
raw-loader 載入檔案原始內容,比如.txt檔案
file-loader 將檔案傳送到打包後資料夾中
{
test: /\.(gif|png|jpe?g|svg)$/,
use: [{
loader: 'file-loader',
options:{}
}]
}
複製程式碼
預設輸出到output的根目錄下,name為32為hash值
名稱 | 型別 | 預設值 | 描述 |
---|---|---|---|
name | {String | Funciton} | [hash].[ext] | 自定義檔名 |
placeholders
名稱 | 型別 | 預設值 | 描述 |
---|---|---|---|
[ext] | {String} | file.extname | 資源副檔名 |
[name] | {String} | file.name | 資源名 |
[path] | {String} | file.dirname | 資源路徑 |
[hash] | {String} | md5 | 內容的雜湊值 |
url-loader 功能類似file-loader
對file-loader的擴充套件,可以設定小圖片轉換base64圖片
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit:5000,
name: 'img/[name].[ext]?[hash]'
}
}
]
}
複製程式碼
轉換編譯類
script-loader
在全域性上下文(global context)執行一次 JS指令碼,就像你在網頁上通過<script>把它們引進來一樣。
babel-loader
由於瀏覽器只能讀懂ES5語法,需要babel將ES2015+語法編譯為ES5語法
- 安裝
npm install babel-loader babel-core babel-preset-env webpack
- 用法
{
test: /\.js$/,
use: {
loader: 'babel-loader',
exclude: /node_modules/,
options: {}
}
}
複製程式碼
options
- cacheDirectory:預設值為 false。當有設定時,指定的目錄將用來快取 loader 的執行結果
- babelrc 預設true,設定false,.babelrc將不會啟用
ts-loader 像 JavaScript 一樣載入 TypeScript 2.0+
模版類
html-loader 解決html里載入圖片問題
minimize:true 壓縮html檔案
handlebars-loader 載入handlebars檔案並編譯為html檔案
handlebars-template-loader 解決handlebars圖片路徑問題
markup-inline-loader
將內聯的 SVG/MathML 檔案轉換為 HTML。在應用於圖示字型,或將 CSS 動畫應用於 SVG 時非常有用
{
test: /\.html$/,
use: [
'html-loader',
'markup-inline-loader'
]
}
複製程式碼
html中使用:
<img markup-inline src="./_images/camera.svg" />
<img data-markup-inline src="./_images/camera.svg" />
樣式類
style-loader 將模組的匯出作為樣式新增到 DOM 中
css程式碼放入js程式碼裡再加入到html裡
css-loader 解析 CSS 檔案後,使用 import 載入,並且返回 CSS 程式碼
得到css程式碼
less-loader 載入和轉譯 LESS 檔案
sass-loader 載入和轉譯 SASS/SCSS 檔案
得到css檔案
postcss 使用 PostCSS 載入和轉譯 CSS 檔案
後處理css檔案,對css檔案做語法分析,真正的核心操作,依賴於postcss龐大的外掛群體
postcss外掛
比如css的語法驗證,壓縮,支援變數和混入語法
- autoprefixer 補全瀏覽器字首
- postcss-import css檔案中使用@import引入其他樣式檔案,但是使用autoprefixer發現,import進來的樣式沒有處理
postcss: function(webpack) {
return [
postcssImport({
addDependencyTo: webpack
}),
autoprefixer
]
}
複製程式碼
清理和測試類
mocha-loader 使用 mocha 測試(瀏覽器/NodeJS)
eslint-loader 使用 ESLint 清理程式碼
jshint-loader 使用 JSHint 清理程式碼
框架類
vue-loader 載入和轉譯 Vue 元件
angular2-template-loader 載入和轉譯 Angular 元件
外掛
loader 被用於轉換某些型別的模組,而外掛則可以用於執行範圍更廣的任務。外掛的範圍包括,從打包優化和壓縮,一直到重新定義環境中的變數
html-webpack-plugin 生成html檔案
- 為html檔案中引入的外部資源如script、link動態新增每次compile後的hash,防止引用快取的外部檔案問題
- 可以生成建立html入口檔案,比如單頁面可以生成一個html檔案入口,配置N個html-webpack-plugin可以生成N個頁面入口
new htmlWebpackPlugin(options)
複製程式碼
名稱 | 型別 | 預設值 | 描述 |
---|---|---|---|
title | {String} | `` | 用於生成的HTML文件的標題 |
filename | {String} | 'index.html' | 生成html的檔名 |
template | {String} | `` | 模版路徑及檔名(路徑相對與output.context) |
inject | {Boolean|String} | true | 當傳遞true或'body'所有JavaScript資源將被放置在正文元素的底部。'head'將指令碼放置在head元素中,false不會將指令碼放進html中 |
favicon | {String} | `` | 將給定的圖示路徑新增到輸出HTML |
minify | {Boolean|Object} | true | 縮小輸出html html-minifier |
hash | {Boolean} | false | true:將webpack所有包含的指令碼和CSS檔案附加一個獨特的編譯雜湊。這對快取清除非常有用 |
cache | {Boolean} | true | 僅在檔案被更改時才發出檔案 |
showErrors | {Boolean} | true | 錯誤細節將寫入HTML頁面 |
chunks | {?} | ? | 允許新增的chunk名 |
chunksSortMode | {String|Function} | auto | 允許控制chunk在被包含到HTML之前應該如何排序。允許的值是'none'不分類 | 'auto'按塊ID排序 | 'dependency'通過其“父項”屬性對塊之間的依賴關係進行排序 | 'manual' | {Function} |
excludeChunks | {String} | `` | 允許您跳過一些chunk |
xhtml | {Boolean} | false | 如果true將link標籤呈現為自動關閉(符合XHTML) |
- chunks按照順序載入
chunksSortMode: function (chunk1, chunk2) {
var order = ['common', 'public', 'index'];
var order1 = order.indexOf(chunk1.names[0]);
var order2 = order.indexOf(chunk2.names[0]);
return order1 - order2;
}
複製程式碼
extract-text-webpack-plugin
webpack 把所有的資源都當成了一個模組, CSS,Image, JS 字型檔案資源, 都打包到一個 bundle.js 檔案中
它將*.css輸入塊中的所有必需模組移動到單獨的CSS檔案中
new ExtractTextPlugin([id: string], filename: string, [options])
- id 該外掛例項的唯一標誌,一般是不會傳的,其自己會生成。
- filename 檔名。可以是[name]、[id]、[contenthash] [name]:將會和entry中的chunk的名字一致 [id]:chunk 的數量 [contenthash]:根據內容生成hash值
- options allchunk:是否將所有額外的chunk都壓縮成一個檔案 disable:禁止使用外掛 ignoreOrder: 禁用順序檢查 (這對 CSS 模組很有用!),預設 false
- extract 配置:
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",//編譯後用什麼loader來提取css檔案
publicPath:, //重寫此 loader 的 publicPath 配置
use: "css-loader" //需要什麼樣的loader去編譯檔案
})
}
]
}
複製程式碼
CopyWebpackPlugin 拷貝資源外掛
CopyWebpackPlugin([
{
context: 'global/img',
from: '**/*',
to:'img/common'
},
{
from: 'img',
to:'img'
},
{
from :'global/lib/es5-shim-sham.js'
}
])
複製程式碼
from 定義要拷貝的源目錄.
to 定義要拷盤的目標目錄.
context 上下文.
flatten 只拷貝檔案不管資料夾 預設是false.
ignore 忽略拷貝指定的檔案 可以用模糊匹配.
force 強制覆蓋先前的外掛 可選 預設false.
UglifyJsPlugin 壓縮js檔案
new webpack.optimize.UglifyJsPlugin(options)
複製程式碼
名稱 | 型別 | 預設值 | 描述 |
---|---|---|---|
test | {RegExp|Array<RegExp>} | /.js$/i | 匹配檔案 |
include | {RegExp|Array<RegExp>} | undefined | 包含目錄 |
exclude | {RegExp|Array<RegExp>} | undefined | 非匹配目錄 |
cache | {Boolean|String} | false | 啟用檔案快取;cache: 'path/to/cache' |
parallel | {Boolean|Number} | false | 使用多程式並行執行來提高構建速度 |
sourceMap | {Boolean} | false | 使用源對映將錯誤訊息位置對映到模組(這會減慢編譯速度) ⚠️ cheap-source-map 選項不適用於這個外掛 |
uglifyOptions | {Object} | {...defaults} | uglify 選項 |
webpack.optimize.UglifyJsPlugin()遇到的
Unexpected token: operator (>) from UglifyJs
問題可以使用 uglifyjs-webpack-plugin外掛
DefinePlugin 允許在編譯時(compile time)配置的全域性常量
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
})
複製程式碼
CleanWebpackPlugin 打包前清理dist目錄
new CleanWebpackPlugin(['./dist'])
webpack自帶外掛
CommonsChunkPlugin 提取 chunks 之間共享的通用模組
webpack將多個模組打包之後的程式碼集合稱為chunk
將公共js程式碼提取到單獨檔案裡
- 是一個可選的用於建立一個獨立檔案(又稱作 chunk)的功能
- 通過將公共模組拆出來,最終合成的檔案能夠在最開始的時候載入一次,便存到快取中供後續使用
- 語法:
new webpack.optimize.CommonsChunkPlugin(options)
- 配置
- 決定生成chunk的引數: name, names, async
- name: string: 公共chunk的名字
- names: string[]: 和name一樣,不過傳入的是一個陣列
- async: boolean|string: 把公共程式碼提取到一個懶載入的chunk,在被使用到時才進行下載
- 決定被提取的chunk: chunks, children, deepChildren
- 決定提取條件: minChunks
- minChunks: number|infinity|function(module,count)->boolean: 如果傳入數字(預設值為3),就是告訴webpack,只有當模組重複的次數大於等於該數字時,這個模組才會被提取出來;Infinity表示不會打包多餘的第三方庫;當傳入為函式時,所有符合條件的chunk中的模組都會被傳入該函式做計算,返回true的模組會被提取到目標chunk
module.exports = {
entry: {
main1: '/src/main1.js',
main2: '/src/main2.js',
jquery:["jquery"],
vue:["vue"]
},
plugins: [
new CommonsChunkPlugin({
name: ["common",'jquery','vue'],//對應於上面的entry的key
minChunks:2
})
]
};
複製程式碼
打包後jquery和vue會生成獨立chunk,main1和main2中的公共業務模組會打包到common.js中;minChunks為infinity時,公共業務模組會分別打包到main1.js和main2.js中
webpack用外掛CommonsChunkPlugin進行打包的時候 將符合引用次數(minChunks)的模組打包到name引數的陣列的第一個塊裡(chunk),然後陣列後面的塊依次打包,最後一個塊包含webpack生成的在瀏覽器上使用各個塊的載入程式碼
HotModuleReplacementPlugin 熱模組替換
new webpack.HotModuleReplacementPlugin()
複製程式碼
devServer: {
hot: true // 啟用伺服器的HMR
}
複製程式碼
其他
cross-env
在不同系統環境下設定變數
webpack-dev-server
- 開發環境用於除錯報錯資訊等,生成一個開發用的伺服器,在檔案有變化的時候自動給我們打包,然後重新整理頁面
- 它還有個模組熱替換的功能 .. 就是它可以只替換有變化的地方 .. 不需要重新整理整個頁面 ...
優化
優化輸出
壓縮css
css-loader?minimize
去除css檔案裡有很多空格和tab
tree-shaking
藉助es6 import export
語法靜態性的特點來刪掉export但是沒有import過的東西
- 配置babel讓它在編譯轉化es6程式碼時不把import export轉換為cmd的module.export,配置如下:
"presets": [
[
"es2015",
{
"modules": false
}
]
]
複製程式碼
優化 UglifyJsPlugin
new UglifyJsPlugin({
// 最緊湊的輸出
beautify: false,
// 刪除所有的註釋
comments: false,
compress: {
// 在UglifyJs刪除沒有用到的程式碼時不輸出警告
warnings: false,
// 刪除所有的 `console` 語句
// 還可以相容ie瀏覽器
drop_console: true,
// 內嵌定義了但是隻用到一次的變數
collapse_vars: true,
// 提取出出現多次但是沒有定義成變數去引用的靜態值
reduce_vars: true,
}
})
複製程式碼
用imagemin-webpack-plugin
壓縮圖片
更快的構建
縮小檔案搜尋範圍
- 配置模組庫.
在js裡出現import 'redux'這樣不是相對也不是絕對路徑的寫法時會去node_modules目錄下找。但是預設的配置會採用向上遞迴搜尋的方式去尋找node_modules,但通常專案目錄裡只有一個node_modules在專案根目錄
module.exports = {
resolve: {
modules: [path.resolve(__dirname, 'node_modules')]
}
};
複製程式碼
- 配置loader
{
test: /\.js$/,
loader: 'babel-loader',
include: path.resolve(__dirname, 'src')
}
複製程式碼
開啟 babel-loader 快取
babel編譯過程很耗時,好在babel-loader提供快取編譯結果選項,在重啟webpack時不需要創新編譯而是複用快取結果減少編譯流程。babel-loader快取機制預設是關閉的,開啟的配置如下:
{
test: /\.js$/,
loader: 'babel-loader?cacheDirectory',
}
複製程式碼
使用 alias
resolve.alias 配置路徑對映。
釋出到npm的庫大多數都包含兩個目錄,一個是放著cmd模組化的lib目錄,一個是把所有檔案合成一個檔案的dist目錄,多數的入口檔案是指向lib裡面下的。
預設情況下webpack會去讀lib目錄下的入口檔案再去遞迴載入其它依賴的檔案這個過程很耗時,alias配置可以讓webpack直接使用dist目錄的整體檔案減少檔案遞迴解析。配置如下:
module.exports = {
resolve: {
alias: {
'moment': 'moment/min/moment.min.js',
'react': 'react/dist/react.js',
'react-dom': 'react-dom/dist/react-dom.js'
}
}
};
複製程式碼
使用 noParse
module.noParse
配置哪些檔案可以脫離webpack的解析。
有些庫是自成一體不依賴其他庫的沒有使用模組化的,比如jquey、momentjs、chart.js,要使用它們必須整體全部引入。
webpack是模組化打包工具完全沒有必要去解析這些檔案的依賴,因為它們都不依賴其它檔案體積也很龐大,要忽略它們配置如下:
module.exports = {
module: {
noParse: /node_modules\/(jquey|moment|chart\.js)/
}
};
複製程式碼