優化配置
1、HMR
HMR:hot module replacement 熱模塊替換
作用:一個模塊發生變化,只會重新打包這一個模塊(而不是打包所有模塊),極大提升構建速度。
樣式文件:可以使用HMR功能,因爲style-loader內部實現了。
JS文件:默認不能使用,需要修改JS代碼,添加支持HMR功能的代碼。
注意:HMR功能對js的處理,只能處理非入口js文件的其他文件。
html文件: 默認不能使用HMR功能.同時會導致問題:html文件不能熱更新了~ (不用做HMR功能)
解決:修改entry入口,將html文件引入
配置hot:true
devServer: {
// 項目構建後的路徑
contentBase: resolve(__dirname, 'build'),
// 啓動gzip壓縮
compress: true,
port: 3000, // 端口號
open: true, // 自動打開瀏覽器
hot:true
}
npx webpack-dev-server啓動。
2、source-map
source-map: 一種 提供源代碼到構建後代碼映射 技術 (如果構建後代碼出錯了,通過映射可以追蹤源代碼錯誤)
[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map
source-map:外部
錯誤代碼準確信息 和 源代碼的錯誤位置
inline-source-map:內聯
只生成一個內聯source-map
錯誤代碼準確信息 和 源代碼的錯誤位置
hidden-source-map:外部
錯誤代碼錯誤原因,但是沒有錯誤位置
不能追蹤源代碼錯誤,只能提示到構建後代碼的錯誤位置
eval-source-map:內聯
每一個文件都生成對應的source-map,都在eval
錯誤代碼準確信息 和 源代碼的錯誤位置
nosources-source-map:外部
錯誤代碼準確信息, 但是沒有任何源代碼信息
cheap-source-map:外部
錯誤代碼準確信息 和 源代碼的錯誤位置
只能精確的行
cheap-module-source-map:外部
錯誤代碼準確信息 和 源代碼的錯誤位置
module會將loader的source map加入
內聯 和 外部的區別:1. 外部生成了文件,內聯沒有 2. 內聯構建速度更快
開發環境:速度快,調試更友好
速度快(eval>inline>cheap>...)
eval-cheap-souce-map
eval-source-map
調試更友好
souce-map
cheap-module-souce-map
cheap-souce-map
--> eval-source-map / eval-cheap-module-souce-map
生產環境:源代碼要不要隱藏? 調試要不要更友好
內聯會讓代碼體積變大,所以在生產環境不用內聯
nosources-source-map 全部隱藏
hidden-source-map 只隱藏源代碼,會提示構建後代碼錯誤信息
--> source-map / cheap-module-souce-map
devServer: {
// 項目構建後的路徑
contentBase: resolve(__dirname, 'build'),
// 啓動gzip壓縮
compress: true,
port: 3000, // 端口號
open: true, // 自動打開瀏覽器
hot:true
},
devtool:'eval-source-map'
3.oneOf
{
// 以下loader只會匹配1個
oneOf: [
{
test: /\.css$/,
use: [...commonCssLoader],
},
{
test: /\.less$/,
use: [...commonCssLoader, 'less-loader'],
}
]
},
4、緩存
babel緩存
cacheDirectory: true
--> 讓第二次打包構建速度更快
文件資源緩存
1、hash: 每次wepack構建時會生成一個唯一的hash值。
問題: 因爲js和css同時使用一個hash值。
如果重新打包,會導致所有緩存失效。(可能我卻只改動一個文件)
2、chunkhash:根據chunk生成的hash值。如果打包來源於同一個chunk,那麼hash值就一樣
問題: js和css的hash值還是一樣的
因爲css是在js中被引入的,所以同屬於一個chunk
3、 contenthash: 根據文件的內容生成hash值。不同文件hash值一定不一樣
--> 讓代碼上線運行緩存更好使用
// 服務端設置緩存
const express = require('express');
const app = express();
// express.static向外暴露靜態資源
// maxAge 資源緩存的最大時間,單位ms
app.use(express.static('build', { maxAge: 1000 * 3600 }));
app.listen(3000);
5、tree shaking
tree shaking:去除無用代碼
前提:1. 必須使用ES6模塊化 2. 開啓production環境
作用: 減少代碼體積
在package.json中配置
"sideEffects": false 所有代碼都沒有副作用(都可以進行tree shaking)
問題:可能會把css / @babel/polyfill (副作用)文件幹掉
"sideEffects": ["*.css", "*.less"]
6、code split
多入口,多個文件輸出
entry:{
index:'./src/index.js',
test:'./src/test.js',
},
output: {
filename: './[name].[contenthash:8].js', // 輸出文件名
path: resolve(__dirname, 'build'), // 輸出文件路徑
},
可以將node_modules中代碼單獨打包一個chunk最終輸出。
自動分析多入口chunk中,有沒有公共的文件,如果有會打包成一個單獨的chunk
optimization:{
splitChunks:{
chunks:'all'
}
},
7、lazy loading
8、pwa
下載插件
npm i workbox-webpack-plugin -D
PWA: 漸進式網絡開發應用程序(離線可訪問)
workbox --> workbox-webpack-plugin
new WorkboxWebpackPlugin.GenerateSW({
/*
1. 幫助serviceworker快速啓動
2. 刪除舊的 serviceworker
生成一個 serviceworker 配置文件~
*/
clientsClaim:true,
skipWaiting:true
})
9、多進程打包
下載插件
npm i thread-loader -D
{
test:/\.js$/,
exclude:/node_modules/,
use:[
/*
開啓多進程打包。
進程啓動大概爲600ms,進程通信也有開銷。
只有工作消耗時間比較長,才需要多進程打包
*/
{
loader:'thread-loader',
options:{
workers:2
}
}
]
},
10、externals
externals:{
// 拒絕jQuery被打包進來
jquery:'jQuery'
},
11、dll
// 告訴webpack哪些庫不參與打包,同時使用時的名稱也得變~
new webpack.DllReferencePlugin({
manifest:resolve(__dirname,'dll/manifest.json')
}),
webpack配置
1、entry
entry: 入口起點
1. string --> ‘./src/index.js’
單入口
打包形成一個chunk。 輸出一個bundle文件。
此時chunk的名稱默認是 main
2. array --> [’./src/index.js’, ‘./src/add.js’]
多入口
所有入口文件最終只會形成一個chunk, 輸出出去只有一個bundle文件。
–> 只有在HMR功能中讓html熱更新生效~
3. object
多入口
有幾個入口文件就形成幾個chunk,輸出幾個bundle文件
此時chunk的名稱是 key
--> 特殊用法
{
// 所有入口文件最終只會形成一個chunk, 輸出出去只有一個bundle文件。
index: ['./src/index.js', './src/count.js'],
// 形成一個chunk,輸出一個bundle文件。
add: './src/add.js'
}
2、output
output: {
// 文件名稱(指定名稱+目錄)
filename: 'js/[name].js',
// 輸出文件目錄(將來所有資源輸出的公共目錄)
path: resolve(__dirname, 'build'),
// 所有資源引入公共路徑前綴 --> 'imgs/a.jpg' --> '/imgs/a.jpg'
publicPath: '/',
chunkFilename: 'js/[name]_chunk.js', // 非入口chunk的名稱
// library: '[name]', // 整個庫向外暴露的變量名
// libraryTarget: 'window' // 變量名添加到哪個上 browser
// libraryTarget: 'global' // 變量名添加到哪個上 node
// libraryTarget: 'commonjs'
},
3、module
module: {
rules: [
// loader的配置
{
test: /\.css$/,
// 多個loader用use
use: ['style-loader', 'css-loader']
},
{
test: /\.js$/,
// 排除node_modules下的js文件
exclude: /node_modules/,
// 只檢查 src 下的js文件
include: resolve(__dirname, 'src'),
// 優先執行
enforce: 'pre',
// 延後執行
// enforce: 'post',
// 單個loader用loader
loader: 'eslint-loader',
options: {}
},
{
// 以下配置只會生效一個
oneOf: []
}
]
},
4、resolve
// 解析模塊的規則
resolve: {
// 配置解析模塊路徑別名: 優點簡寫路徑 缺點路徑沒有提示
alias: {
$css: resolve(__dirname, 'src/css')
},
// 配置省略文件路徑的後綴名
extensions: ['.js', '.json', '.jsx', '.css'],
// 告訴 webpack 解析模塊是去找哪個目錄
modules: [resolve(__dirname, '../../node_modules'), 'node_modules']
}
5、dev server
devServer: {
// 運行代碼的目錄
contentBase: resolve(__dirname, 'build'),
// 監視 contentBase 目錄下的所有文件,一旦文件變化就會 reload
watchContentBase: true,
watchOptions: {
// 忽略文件
ignored: /node_modules/
},
// 啓動gzip壓縮
compress: true,
// 端口號
port: 5000,
// 域名
host: 'localhost',
// 自動打開瀏覽器
open: true,
// 開啓HMR功能
hot: true,
// 不要顯示啓動服務器日誌信息
clientLogLevel: 'none',
// 除了一些基本啓動信息以外,其他內容都不要顯示
quiet: true,
// 如果出錯了,不要全屏提示~
overlay: false,
// 服務器代理 --> 解決開發環境跨域問題
proxy: {
// 一旦devServer(5000)服務器接受到 /api/xxx 的請求,就會把請求轉發到另外一個服務器(3000)
'/api': {
target: 'http://localhost:3000',
// 發送請求時,請求路徑重寫:將 /api/xxx --> /xxx (去掉/api)
pathRewrite: {
'^/api': ''
}
}
}
6、optimization
optimization: {
splitChunks: {
chunks: 'all'
// 默認值,可以不寫~
/* minSize: 30 * 1024, // 分割的chunk最小爲30kb
maxSiza: 0, // 最大沒有限制
minChunks: 1, // 要提取的chunk最少被引用1次
maxAsyncRequests: 5, // 按需加載時並行加載的文件的最大數量
maxInitialRequests: 3, // 入口js文件最大並行請求數量
automaticNameDelimiter: '~', // 名稱連接符
name: true, // 可以使用命名規則
cacheGroups: {
// 分割chunk的組
// node_modules文件會被打包到 vendors 組的chunk中。--> vendors~xxx.js
// 滿足上面的公共規則,如:大小超過30kb,至少被引用一次。
vendors: {
test: /[\\/]node_modules[\\/]/,
// 優先級
priority: -10
},
default: {
// 要提取的chunk最少被引用2次
minChunks: 2,
// 優先級
priority: -20,
// 如果當前要打包的模塊,和之前已經被提取的模塊是同一個,就會複用,而不是重新打包模塊
reuseExistingChunk: true
}
}*/
},
// 將當前模塊的記錄其他模塊的hash單獨打包爲一個文件 runtime
// 解決:修改a文件導致b文件的contenthash變化
runtimeChunk: {
name: entrypoint => `runtime-${entrypoint.name}`
},
minimizer: [
// 配置生產環境的壓縮方案:js和css
new TerserWebpackPlugin({
// 開啓緩存
cache: true,
// 開啓多進程打包
parallel: true,
// 啓動source-map
sourceMap: true
})
]
}
webpack5
此版本重點關注以下內容:
- 通過持久緩存提高構建性能.
- 使用更好的算法和默認值來改善長期緩存.
- 通過更好的樹搖和代碼生成來改善捆綁包大小.
- 清除處於怪異狀態的內部結構,同時在 v4 中實現功能而不引入任何重大更改.
- 通過引入重大更改來爲將來的功能做準備,以使我們能夠儘可能長時間地使用 v5.
下載
- npm i webpack@next webpack-cli -D
自動刪除 Node.js Polyfills
早期,webpack 的目標是允許在瀏覽器中運行大多數 node.js 模塊,但是模塊格局發生了變化,許多模塊用途現在主要是爲前端目的而編寫的。webpack <= 4 附帶了許多 node.js 核心模塊的 polyfill,一旦模塊使用任何核心模塊(即 crypto 模塊),這些模塊就會自動應用。
儘管這使使用爲 node.js 編寫的模塊變得容易,但它會將這些巨大的 polyfill 添加到包中。在許多情況下,這些 polyfill 是不必要的。
webpack 5 會自動停止填充這些核心模塊,並專注於與前端兼容的模塊。
遷移:
- 儘可能嘗試使用與前端兼容的模塊。
- 可以爲 node.js 核心模塊手動添加一個 polyfill。錯誤消息將提示如何實現該目標。
Chunk 和模塊 ID
添加了用於長期緩存的新算法。在生產模式下默認情況下啓用這些功能。
chunkIds: "deterministic", moduleIds: "deterministic"
Chunk ID
你可以不用使用 import(/* webpackChunkName: "name" */ "module")
在開發環境來爲 chunk 命名,生產環境還是有必要的
webpack 內部有 chunk 命名規則,不再是以 id(0, 1, 2)命名了
Tree Shaking
- webpack 現在能夠處理對嵌套模塊的 tree shaking
// inner.js
export const a = 1;
export const b = 2;
// module.js
import * as inner from './inner';
export { inner };
// user.js
import * as module from './module';
console.log(module.inner.a);
在生產環境中, inner 模塊暴露的 b
會被刪除
- webpack 現在能夠多個模塊之前的關係
import { something } from './something';
function usingSomething() {
return something;
}
export function test() {
return usingSomething();
}
當設置了"sideEffects": false
時,一旦發現test
方法沒有使用,不但刪除test
,還會刪除"./something"
- webpack 現在能處理對 Commonjs 的 tree shaking
Output
webpack 4 默認只能輸出 ES5 代碼
webpack 5 開始新增一個屬性 output.ecmaVersion, 可以生成 ES5 和 ES6 / ES2015 代碼.
如:output.ecmaVersion: 2015
SplitChunk
// webpack4
minSize: 30000;
// webpack5
minSize: {
javascript: 30000,
style: 50000,
}
Caching
// 配置緩存
cache: {
// 磁盤存儲
type: "filesystem",
buildDependencies: {
// 當配置修改時,緩存失效
config: [__filename]
}
}
緩存將存儲到 node_modules/.cache/webpack
監視輸出文件
之前 webpack 總是在第一次構建時輸出全部文件,但是監視重新構建時會只更新修改的文件。
此次更新在第一次構建時會找到輸出文件看是否有變化,從而決定要不要輸出全部文件。
默認值
entry: "./src/index.js
output.path: path.resolve(__dirname, "dist")
output.filename: "[name].js"