webpack4.X 實戰(四):企業SPA 24點總結(下)node
本文綱要:項目打包配置、優化jquery
webpack 配置文件輸出路徑時,經常使用三種配置webpack
配置一:出口 entry.path
,配置全部文件的輸出路徑git
// webpack 配置
const path = require('path');
const rootPath = path.resolve(__dirname, '');
module.exports = {
output: {
path: path.resolve(rootPath, './dist')
}
}
// 打包後的文件,全部資源 統一輸出到 項目根目錄下的 dist 文件夾下
複製代碼
配置二:loader
的 options.outputPath
,配置 當前 loader
匹配到文件 的輸出路徑github
這項配置的文件輸出 以
entry.path
路徑爲標準web
// webpack 配置
module.exports = {
output: {
path: path.resolve(rootPath, './dist')
},
module: {
rules: [{
test: /\.(png|jpg|gif|ico)$/,
use: [{
loader: 'url-loader',
options: {
limit: 8192,
name: '[name].[ext]',
outputPath: 'static/assets/'
}
}]
}]
}
};
// 打包後的文件,圖片資源 統一輸出到 項目根目錄下的 `dist/static/assets` 文件夾下
複製代碼
配置三:name
選項的值,可改變當前包的輸出路徑vue-router
出口
entry.chunkFilename
,改變全部chunk
包的輸出位置(runtimeChunk
除外)
// webpack 配置
module.exports = {
output: {
path: path.resolve(rootPath, './dist'),
chunkFilename: 'static/js/[name].js'
}
};
// 打包後的 chunk 包,統一輸出到 dist/static/js 文件夾下
複製代碼
optimization.splitChunks
的name
值,以output.chunkFilename
路徑爲標準
// webpack 配置
module.exports = {
output: {
path: path.resolve(rootPath, './dist'),
chunkFilename: 'static/js/[name].js'
},
optimization: {
splitChunks: {
cacheGroups: {
libs: {
test: /[\\/]node_modules[\\/]/,
priority: 20,
name: '../libs/index',
chunks: 'all'
}
}
}
}
};
// 打包後的 splitChunks 包,統一輸出到 dist/static/js 文件夾下
複製代碼
【特殊】
optimization.runtimeChunk
的name
值,以output.path
路徑爲標準
// webpack 配置
module.exports = {
output: {
path: path.resolve(rootPath, './dist'),
chunkFilename: 'static/js/[name].js'
},
optimization: {
runtimeChunk: {
name: 'static/runtime/index'
}
}
};
// 打包後的 runtime 包,統一輸出到 dist/static/runtime 文件夾下
複製代碼
做用:配置 發佈路徑,讓頁面請求 CDN 上的靜態資源,速度更快
配置 公共發佈路徑: output.publicPath
// webpack 配置
module.exports = {
output : {
publicPath : 'http://aaa.com/'
}
};
複製代碼
單獨配置靜態資源: loader options
module.exports = {
module: {
rules: [{
test: /\.(png|jpg|gif|ico)$/,
use: [{
loader: 'url-loader',
options: {
limit: 8192,
name: '[name].[hash:8].[ext]',
outputPath: 'static/assets/',
publicPath: 'http://aaa.com/assets/'
}
}]
}]
}
};
複製代碼
webpack 打包後的模塊 默認命名規則:
webpack 默認爲給各個模塊分配一個 id,做爲模塊的名稱
默認 id 是根據模塊引入的順序,賦予一個整數(0、一、二、3……)
默認 id 用來處理模塊之間的依賴關係
經過配置 不一樣的 hash
,緩存文件
webpack 內置了多種可以使用 hash,官網解釋分別以下:
hash
: the hash of the module identifier
chunkHash
: the hash of the chunk content
contentHash
: the hash of extracted content
設置 哈希 的長度
// 全局設置 hash 長度
output.hashDigestLength
複製代碼
// 局部設置 hash 長度
[hash:16] 等方式
複製代碼
配置 什麼時候生成 hash
// output.hashDigest
複製代碼
如何選擇正確的 hash
?
JS、分離後的CSS、資源文件(圖片、字體圖標)都使用 hash
每次打包 JS、分離後的CSS 文件 哈希值 都同樣
項目代碼沒有變化,再次打包;全部文件哈希值不變
只要JS、CSS 有一處變化,全部 JS、分離後的CSS 文件 哈希值 都變化
資源文件(圖片、字體圖標)沒更新,哈希值不變;使用 hash
,是個不錯的選擇
JS、分離後的CSS、資源文件(圖片、字體圖標)都使用 chunkHash
每次打包 同一個頁面的 JS、分離後的CSS 哈希值同樣;不一樣頁面 JS、CSS文件 哈希值不同
項目代碼沒有變化,再次打包;全部文件哈希值不變
項目代碼有變化,只是有更改的JS、對應的CSS文件 哈希值變化;其餘的文件 哈希值不變
資源文件(圖片、字體圖標)不能使用 chunkHash
,會報錯
最佳實踐:
JS 文件:[name].[chunkHash:8].js
分離後的CSS 文件:[name].[contentHash:8].css
不會 隨着JS的改變,而更改
hash
資源文件(圖片、字體圖標):[name].[hash:8].[ext]
使用 clean-webpack-plugin
插件,在打包時先清空上一次打包的文件
若是不清空,打包後的文件體積會愈來愈大
安裝
npm i clean-webpack-plugin@1.0.0 -D
複製代碼
配置
// webpack 中配置
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
plugins: [
new CleanWebpackPlugin(['dist']) // 值爲根目錄下 清空的文件夾
]
}
複製代碼
webpack 提供內置插件:ContextReplacementPlugin
// 以下:只打包 moment 的中文包
...
plugins: [
new webpack.ContextReplacementPlugin(
/moment[/\\]locale$/,
/zh-cn/,
),
]
...
複製代碼
新舊版本 API更新
webpack3 使用 內置插件 optimize.CommonsChunkPlugin
來分割代碼
webpack4 移除內置插件,新增 optimization.splitChunks
、optimization.runtimeChunk
來分割代碼
Webpack 4 的 Code Splitting 最大的特色就是配置簡單(0配置起步),和基於內置規則自動拆分
爲何要分割代碼?
編譯:減小編譯的總體大小,以提升構建性能
運行:減少文件體積,提升加載速度
這是相對的,在代碼分割減少文件體積的同時,也應該考慮 合理的HTTP請求次數
一般分割哪些代碼?
提取公有代碼
提取經常使用庫代碼
提取 webpack 的 runtime (運行時) 代碼
webpack4默認的分割代碼機制,知足以下條件的默認都會被分割
新 bundle 被兩個及以上模塊引用,或者來自 node_modules
新 bundle 大於 30kb (壓縮以前)
異步加載併發加載的 bundle 數不能大於 5 個
初始加載的 bundle 數不能大於 3 個
默認配置以下:
// 默認配置以下:
module.exports = {
// ...
optimization: {
splitChunks: {
chunks: 'async', //默認只做用於異步模塊,爲`all`時對全部模塊生效,`initial`對同步模塊有效
minSize: 30000, //合併前模塊文件的體積
maxSize: 0,
minChunks: 1, //最少被引用次數
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~', //自動命名鏈接符
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10 // 同時知足條件的,優先級更高的生效
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
};
複製代碼
自定義代碼分割 optimization.splitChunks.cacheGroups
optimization.splitChunks.cacheGroups
下每個chunk
的配置 選項 同optimization.splitChunks
的配置選項
若是設置了將 覆蓋
optimization.splitChunks
下的配置選項;若是沒設置 就繼承
// 將代碼庫 單獨打包
module.exports = {
optimization: {
splitChunks: {
cacheGroups: {
libs: {
test: /[\\/]node_modules[\\/]/,
priority: 20,
name: '../libs/index',
chunks: 'all'
}
}
}
}
};
複製代碼
將 webpack runtime
代碼單獨打包
// 將代碼庫 單獨打包
module.exports = {
optimization: {
runtimeChunk: {
name: 'static/runtime/index'
}
}
};
複製代碼
特殊場景下,須要咱們 手動代碼分割,可參考以下案例
默認狀況下,webpack 單線程處理任務
因爲運行在 Node.js 之上的 Webpack 是單線程模型的
因此Webpack 須要處理的事情須要一件一件的作,不能多件事一塊兒作
HappyPack 可讓 webpack 同時處理多個任務
它將任務分解給多個子進程去併發執行,子進程處理完成後再將結果發送給主進程中
從而減小總的構建時間,提高構建效率
HappyPack 處理 JS,配置以下
// 安裝
npm i happypack@5.0.1 -D
複製代碼
// webpack 配置以下
const os = require('os');
const HappyPack = require('happypack');
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
module.exports = {
module: {
rules: [{
test: /\.js$/,
// 把對.js 的文件處理交給id爲 happyBabel 的 HappyPack 的實例執行
loader: 'happypack/loader?id=happyBabel'
}]
},
plugins: [
new HappyPack({
id: 'happyBabel', // 用id來標識 happypack處理那裏類文件
// 如何處理 用法和loader 的配置同樣
loaders: [{
loader: 'babel-loader?cacheDirectory=true'
}],
threadPool: happyThreadPool, // 共享進程池
verbose: true // 容許 HappyPack 輸出日誌
})
]
};
複製代碼
說明 module.rules.loader
在 Loader 配置中,全部文件的處理都交給了 happypack/loader
去處理
使用緊跟其後的 ?id=happyBabel
去告訴 happypack/loader
去選擇哪一個 HappyPack 實例去處理文件
說明 HappyPack 參數
id
: String 用惟一的標識符 id 來表明當前的 HappyPack 是用來處理一類特定的文件
loaders
: Array 用法和 webpack Loader 配置中同樣
threads
: Number 表明開啓幾個子進程去處理這一類型的文件,默認是3個,類型必須是整數
verbose
: Boolean 是否容許 HappyPack 輸出日誌,默認是 true
threadPool
: HappyThreadPool 表明共享進程池(即多個 HappyPack 實例使用同一個共享進程池中的子進程去處理任務,以防止資源佔用過多)
verboseWhenProfiling
: Boolean 開啓 webpack --profile ,仍然但願HappyPack產生輸出
debug
: Boolean 啓用debug 用於故障排查;默認 false
注意:
HappyPack 在處理 CSS / SCSS 時,須要單首創建一個 postcss.config.js
文件;不然會報錯
HappyPack 對 url-loader 和 file-loader 的支持度有限
存疑
問題:項目中只配置了子線程編譯 JS,編譯速度卻更慢了(項目沒有分離CSS;項目代碼很少,生產環境打包後體積 1.13M)
緣由猜想:HappyPack 多線程的原理是,先開啓子線程處理,完成後再講結果傳遞給主線程,這個時間 超過了 多線程打包節省的時間(因爲須要編譯的JS很少)
認識 DLL
能夠包含給其餘模塊調用的函數和數據
用過 Windows 系統的人應該會常常看到以 .dll 爲後綴的文件
web 項目構建接入動態連接庫,須要完成如下事情:
把網頁依賴的基礎模塊抽離出來,打包到一個個單獨的動態連接庫中(一個動態連接庫中能夠包含多個模塊)
當須要導入的模塊存在於某個動態連接庫中時,這個模塊不能被再次被打包,而是去動態連接庫中獲取
頁面依賴的全部動態連接庫須要被加載
web 項目構建接入動態連接庫,好處:提高構建速度(每次構建 不用再重複打包)
webpack 已經內置了對動態連接庫的支持
DllPlugin 插件:打包出一個個單獨的動態連接庫文件
DllReferencePlugin 插件:在主要配置文件中去引入 DllPlugin 插件打包好的動態連接庫文件
實踐 DLL
// webpack/dll.config.js 配置文件
const path = require("path");
const webpack = require("webpack");
const libs = require('../configs/dll.config');
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
mode: 'production',
entry: {
// 把 vue 相關模塊的放到一個單獨的動態連接庫
vue: ['vue', 'vue-router'],
// 把項目須要全部的 lib 放到一個單獨的動態連接庫
lib: ['jquery', 'moment'],
},
output: {
path: path.resolve(__dirname, './dll'),
filename: "[name].js",
library: "_dll_[name]"
},
plugins: [
new webpack.DllPlugin({
name: "_dll_[name]",
path: path.join(__dirname, 'dll', 'manifest.json'),
}),
// 清空 dll 文件夾
new CleanWebpackPlugin(['dll']),
]
}
複製代碼
配置 npm script
{
"scripts": {
"dll": "webpack --config webpack/dll.config.js",
}
}
複製代碼
安裝 依賴包
npm i clean-webpack-plugin@1.0.0 -D
複製代碼
// webpack 配置文件
const webpack = require('webpack');
module.exports = {
plugins: [
// 啓用 dll
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./dll/manifest.json')
})
]
};
複製代碼
// 新dowload下來的項目,須要先打 DLL 包,再運行項目
複製代碼
webpack 內置插件,支持全局引入第三方庫
// webapck.config.js 文件中配置以下:
const webpack = require('webpack');
plugins:[
new webpack.ProvidePlugin({
$:"jquery"
})
],
複製代碼
對比:
全局引入:
全局引入後,項目代碼中直接使用便可
若是沒有地方用,第三方庫將不會被打包
局部引入:
每一個文件中須要的話,都單獨引入
只要引入了,即便沒用,第三方庫也會打包
插件:webpack-bundle-analyzer
生成的報告中有三種尺寸大小:
stat
: 壓縮等轉換以前的輸入文件大小(從webpack的stat對象裏獲得的
parsed
: webpack 打包壓縮後 輸出的JS文件大小(不含資源文件、分離後的CSS等)
gzip
: 通過 gzip 壓縮後的大小
使用
// 安裝
npm i webpack-bundle-analyzer@3.0.3 -D
複製代碼
// webpack 配置
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
};
// 打包完成後 自動在瀏覽器中顯示分析結果
複製代碼
插件:webpack-chart
插件:webpack-analyse
部署打包 配置
// webpack 配置文件
module.exports = {
// 精簡 終端輸出(打包部署)
stats: {
modules: false,
children: false,
chunks: false,
chunkModules: false
}
};
複製代碼
本地運行 配置 webpack-dev-server
// webpack 配置文件
module.exports = {
devServer: {
// 精簡 終端輸出(本地運行)
stats: {
modules: false,
children: false,
chunks: false,
chunkModules: false
}
}
};
複製代碼
以下插件,可顯示打包進度,具體配置可查閱 GitHub
progress-bar-webpack-plugin
nyan-progress-webpack-plugin
progress-bar-webpack-plugin
progress-webpack-plugin