先簡單回顧下 webpack 原理
Webpack
可以看做是模塊打包機(jī)最域,把解析的所有模塊變成一個對象谴分,然后通過入口模塊去加載我們的東西,然后依次實(shí)現(xiàn)遞歸的依賴關(guān)系镀脂,通過入口來運(yùn)行所有的文件牺蹄。由于 webpack
只認(rèn)識js,所以需要通過一系列的 loader
和 plugin
轉(zhuǎn)換成合適的格式供瀏覽器運(yùn)行薄翅。
loader
主要是對資源進(jìn)行加載/轉(zhuǎn)譯的預(yù)處理工作沙兰,其本質(zhì)是一個函數(shù),在該函數(shù)中對接收到的內(nèi)容進(jìn)行轉(zhuǎn)換翘魄,返回轉(zhuǎn)換后的結(jié)果鼎天。某種類型的資源可以使用多個loader
,執(zhí)行順序是從右到左暑竟,從下到上斋射。plugin
(插件)主要是擴(kuò)展webpack
的功能,其本質(zhì)是監(jiān)聽整個打包的生命周期但荤。webpack
基于事件流框架Tapable
罗岖, 運(yùn)行的生命周期中會廣播出很多事件,plugin
可以監(jiān)聽這些事件腹躁,在合適的時機(jī)通過webpack
提供的 API 改變輸出結(jié)果桑包。
本文主要介紹 webpack5
的配置,按步驟邊配置邊打包對比會印象更深~ 附上完整的源碼纺非。
webpack 安裝
新建一個目錄哑了,進(jìn)入目錄初始化 package.json
赘方,并安裝 webpack
依賴
// 初始化包
npm init -y
// 安裝依賴
npm i webpack webpack-cli -D
基礎(chǔ)配置
webpack
默認(rèn)配置文件名字為 webpack.config.js
,于是在項(xiàng)目根目錄下新建一個名為 webpack.config.js
的文件垒手,在配置文件里寫最簡單的單頁面配置:
let path = require("path");
module.exports = {
mode: "development",
entry: "./src/js/index.js",
output: {
filename: "js/bundle.js",
path: path.resolve("dist"),
publicPath: "http://cdn.xxxxx"
}
}
配置詳解
- mode - 打包模式
-
development
為開發(fā)模式蒜焊,打包后代碼不會被壓縮 -
production
為生產(chǎn)模式倒信,打包后代碼為壓縮代碼
-
- entry - 入口文件
- output - 打包文件配置
-
filename
:打包后文件科贬,filename 的值可設(shè)置成帶hash
戳的文件:js/bundle.[hash].js
/js/bundle.[hash:8].js
(只顯示 8 位 hash 戳) -
path
:打包文件路徑,需為絕對路徑 -
publicPath
:上線的cdn地址
-
TIP: 上述代碼中
path
為內(nèi)置模塊鳖悠,無需安裝榜掌,直接引入即可。
新建后還需在項(xiàng)目根目錄下的 src/js
目錄下新建 index.js
文件乘综,然后隨便輸入一句 js 代碼憎账。
配置后可使用 webpack
命令嘗試打包,若報錯找不到命令可 npm i webpack -g
全局安裝后再打包卡辰,打包成功后會輸出到項(xiàng)目根目錄下的 dist
目錄胞皱。
項(xiàng)目目錄結(jié)構(gòu)大致如下
├─package.json
├─webpack.config.js
├─src
| ├─js
| | └index.js
├─dist
html 文件打包
由于 webpack
只認(rèn)識 js
,因此需通過 html-webpack-plugin
插件打包 html 文件
npm i html-webpack-plugin -D
安裝后在 webpack.config.js
配置文件中
let HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html"
})
]
}
production
模式下可以開啟 html 文件的壓縮配置:
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
minify: { removeAttributeQuotes: true, collapseWhitespace: true },
hash: true
})
]
配置詳解
- plugins - webpack 插件配置
- html-wepack-plugin配置
- template - html 模板文件的相對/絕對路徑
- minify - 壓縮配置
-
removeAttributeQuotes
:刪除屬性雙引號 -
collapseWhitespace
:代碼壓縮成一行
-
- hash - 引入文件帶上hash戳
- html-wepack-plugin配置
TIP: 如果不指定模板
template
配置九妈,將是插件默認(rèn)的 html文件反砌,而不是項(xiàng)目中的 html 文件
開啟服務(wù)
webpack 通過安裝 webpack-dev-server
開啟服務(wù)
npm i webpack-dev-server -D
配置 webpack.config.js
devServer: {
port: 5000,
compress: true,
open: true,
client: { progress: true }
}
配置詳解
- devServer -
webpack-dev-server
配置- port - 端口號
- compress - 開啟
gzip
壓縮 - open - 啟動后自動把頁面打開
- client
-
progress
:在瀏覽器中以百分比顯示編譯進(jìn)度
-
配置好可運(yùn)行 webpack-dev-server
命令查看效果,若找不到命令可 npm i webpack-dev-server -g
全局安裝下
跨域
開發(fā)過程中容易遇到接口跨域問題萌朱,可通過 devServer.proxy
配置解決
假設(shè)接口地址為 http://localhost:3000/api/users
宴树,對 /api/users
的請求可如下配置
devServer: {
proxy: {
'/api': 'http://localhost:3000',
},
},
但實(shí)際項(xiàng)目中接口的地址有很多種可能,一般不會有 /api
目錄晶疼,即一般接口地址為http://localhost:3000/users
酒贬,因此枚舉配置會很麻煩,可通過代理請求解決
即先請求 http://localhost:3000/api/users
接口地址翠霍,然后通過 devServer 代理到 http://localhost:3000/users
本文通過 express
開啟接口服務(wù)锭吨,接口地址為 http://localhost:3000/user
,接口代碼不再贅述寒匙,后期上傳完整的源碼零如,可通過 node "項(xiàng)目路徑\webpack5\src\js\server.js"
啟動接口服務(wù),然后配置 webpack.config.js
devServer: {
proxy: {
"/api": {
target: "http://localhost:3000/",
pathRewrite: {
"/api": ""
},
},
}
}
devServer
配置詳解
- proxy - 代理配置
- target - 接口域名
- pathRewrite - 接口路徑重寫蒋情,把請求代理到接口服務(wù)器上
mock 接口數(shù)據(jù)
當(dāng)后端接口沒有寫好埠况,又不希望被阻塞進(jìn)度,可以通過 mock 前期跟后端約定好的接口數(shù)據(jù)格式來模擬調(diào)試頁面棵癣≡玻可使用有自定義函數(shù)和應(yīng)用自定義中間件的能力的配置 devServer.setupMiddlewares
,在 middlewares.unshift
中的回調(diào)函數(shù)使用 res.send
把需要 mock 的數(shù)據(jù)傳遞進(jìn)去:
devServer: {
setupMiddlewares: (middlewares, devServer) => {
if (!devServer) {
throw new Error("webpack-dev-server is not defined");
}
middlewares.unshift({
name: "user-info",
// `path` 是可選的狈谊,接口路徑
path: "/user",
middleware: (req, res) => {
// mock 數(shù)據(jù)模擬接口數(shù)據(jù)
res.send({ name: "moon mock" });
},
});
return middlewares;
},
}
樣式處理
樣式處理需要用到的 loader 及其作用:
-
less-loader
:加載和轉(zhuǎn)譯 LESS 文件 -
postcss-loader
:使用 PostCSS 加載和轉(zhuǎn)譯 CSS/SSS 文件喜命,如可以處理autoprefixer
css 包沟沙,為css添加瀏覽器前綴 -
css-loader
:解析@import
andurl()
語法,使用 import 加載解析后的css文件壁榕,并且返回 CSS 代碼 -
mini-css-extract-plugin
的loader
:抽取出 css 文件矛紫,通過 link 標(biāo)簽引入 html 文件
安裝依賴,若使用的是 sass牌里,則把 less
less-loader
換成 node-sass
sass-loader
即可
npm i mini-css-extract-plugin css-loader postcss-loader autoprefixer less-loader less -D
配置 webpack.config.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
plugins: [
new MiniCssExtractPlugin({
filename: "css/main.css", // 抽離的css文件名
})
],
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"],
},
{
test: /\.less$/,
use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader", "less-loader"],
},
]
}
}
還需新建并配置 postcss.config.js
module.exports = {
plugins: [require("autoprefixer")]
};
上述文件配置好后颊咬,打包后會發(fā)現(xiàn) css3 樣式還是沒有添加前綴,還需配置 package.json
的 browserlist
才能生效
"browserslist": [
"last 1 version",
"> 1%",
"IE 10"
],
js 處理及語法校驗(yàn)
es6
或更高級的語法需轉(zhuǎn)化成 es5
牡辽,并使用 eslint
規(guī)范代碼:
-
babel-loader
:加載 ES2015+ 代碼喳篇,然后使用 Babel 轉(zhuǎn)譯為 ES5 -
@babel/preset-env
:基礎(chǔ)的ES語法分析包,各種轉(zhuǎn)譯規(guī)則的統(tǒng)一設(shè)定态辛,目的是告訴loader要以什么規(guī)則來轉(zhuǎn)化成對應(yīng)的js版本 -
@babel/plugin-transform-runtime
:解析 generator 等高級語法麸澜,但不包含 include 語法,include 語法需安裝@babel/polyfill
奏黑。官方文檔說上線需帶上@babel/runtime
這個補(bǔ)丁炊邦,該包還做了一些方法抽離的優(yōu)化,如 class 語法的抽離(抽離出 classCallCheck 方法) -
@babel/polyfill
:解析更高級的語法熟史,如promise
馁害,include
等,在js文件中require
引入即可 -
eslint-loader
:校驗(yàn) js 是否符合規(guī)范以故,可自行在 eslint 網(wǎng)站上配置下載
安裝依賴
npm i @babel/core babel-loader @babel/preset-env @babel/plugin-transform-runtime -@babel/polyfill -D
npm i @babel/runtime eslint-loader eslint -S
webpack.config.js
{
test: /\.js$/,
use: {
loader: "eslint-loader",
options: {
enforce: "pre", // 定義為前置loader蜗细,在normal的loader前執(zhí)行
},
},
},
{
test: /\.js$/, // enforce 默認(rèn)為 normal 普通loader
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"], // 把es6轉(zhuǎn)成es5
plugins: ["@babel/plugin-transform-runtime"], //作用?
},
},
include: path.resolve(__dirname, "src"),
exclude: /node_modules/,
},
配置 source-map
源碼映射配置 source-map
的值:
- source-map 映射源碼 會單獨(dú)生成source-map文件 出錯了會標(biāo)識當(dāng)前報錯的行和列 大而全
- eval-source-map 不會產(chǎn)生單獨(dú)的文件怒详,可顯示行和列
- cheap-module-source-map 不會標(biāo)識列炉媒,會生成單獨(dú)的映射文件
- cheap-module-eval-source-map 不會產(chǎn)生文件 集成在打包后的文件中 不會產(chǎn)生列
webpack.config.js
devtool: "eval-source-map",
引入js全局變量
有三種方式可以引入全局變量
expose-loader
可把變量暴露到 window
全局對象上,以 jquery 為例昆烁,先安裝依賴
npm i jquery expose-loader -D
然后在 webpack.config.js
中配置 loader吊骤,把 $
暴露到 window 全局對象上
module: {
rules: [{
test: require.resolve('jquery'),
use: [{
loader: 'expose-loader',
options: '$'
}]
}]
}
除了上述方法外還可以在入口 js 文件中暴露
require("expose-loader?$!jquery");
providePlugin
可使用 webapck 內(nèi)置插件 providePlugin
給每個模塊中注入變量,還是以 jquery 為例
在 webapck.config.js
中配置
const webpack = require("webpack");
module.exports = {
plugins: [
new webpack.ProvidePlugin({
$: 'jquery'
});
]
}
然后在任意js模塊中可以直接使用$調(diào)用静尼,無需引入jquery包
// in a module
$('#item'); // <= works
// $ is automatically set to the exports of module "jquery"
通過 cdn 引入
還可以通過 cdn 鏈接的方式引入全局變量白粉,但如果此時js文件中多寫了 import $ from 'jquery',就會把 jquery 也打包進(jìn)去鼠渺,可使用 external 防止將某些 import
的包(package)打包到 bundle 中
index.html
<script
src="https://code.jquery.com/jquery-3.1.0.js"
integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
crossorigin="anonymous"
></script>
webpack.config.js
module.exports = {
//...
externals: {
jquery: 'jQuery',
},
};
這樣就剝離了那些不需要改動的依賴模塊鸭巴,換句話,下面展示的代碼還可以正常運(yùn)行:
import $ from 'jquery';
$('.my-element').animate(/* ... */);
上面的例子拦盹。屬性名稱是 jquery
鹃祖,表示應(yīng)該排除 import $ from 'jquery'
中的 jquery
模塊。為了替換這個模塊普舆,jQuery
的值將被用來檢索一個全局的 jQuery
變量恬口。換句話說校读,當(dāng)設(shè)置為一個字符串時,它將被視為全局的
(定義在上面和下面)祖能。
樣式壓縮和 js 壓縮
production
模式下需壓縮 css 可使用插件 css-minimizer-webpack-plugin
歉秫,但使用了此插件壓縮 css, 會導(dǎo)致 js 不壓縮,所以需要安裝 js 壓縮插件 terser-webpack-plugin
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
optimization: {
minimize: true,
minimizer: [
new CssMinimizerPlugin(),
// 壓縮js
new TerserPlugin({ test: /\.js(\?.*)?$/i }),
],
},
}
圖片處理
需要 loader 解析圖片資源:
-
file-loader
:將文件的import/require()解析為url养铸,并將文件發(fā)送到輸出文件夾(dist文件夾)雁芙,并返回(相對)URL -
url-loader
:像file-loader
一樣工作,但如果文件小于限制揭厚,可以返回 data URL却特,即把圖片變成base64 -
html-loader
:可以解析html標(biāo)簽引入的圖片,可以通過查詢參數(shù) attrs,指定哪個標(biāo)簽屬性組合(tag-attribute combination)應(yīng)該被處理,默認(rèn)值:attrs=img:src
安裝依賴
npm i file-loader url-loader html-loader -D
配置 webpack.config.js
module: {
rules: [
{
test: /\.jpg|png|jpeg$/,
use: {
loader: "file-loader",
options: {
outputPath: "images/",
name: "[name].[ext]", // 如果不寫文件名,則會生成隨機(jī)名字
// publicPath: "http://cdn.xxx.com/images", // 可配置生產(chǎn)環(huán)境的cdn地址前綴
},
},
},
{
test: /\.(html)$/,
use: {
loader: "html-loader",
options: {
esModule: false,
},
},
},
]
}
TIP: url-loader可以使用 options.limit 限制抱究,小于多少k時使用base64轉(zhuǎn)換幌衣,大于這個體積使用file-loader打包
html-loader 配置報錯問題
html-loader
需關(guān)閉 es6 模塊化,使用commonjs解析掌桩,否則會報錯。原因主要是兩個 loader 解析圖片的方式不一樣。
項(xiàng)目目錄結(jié)構(gòu)大致如下
├─.eslintrc.json
├─package-lock.json
├─package.json
├─postcss.config.js
├─webpack.config.js
├─src
| ├─index.html
| ├─js
| | ├─index.js
| | ├─server.js
| | └test.js
| ├─image
| | └logo.png
| ├─css
| | ├─a.css
| | └index.css
├─dist
resolve 配置
resolve 常用的屬性配置:
-
modules
:告訴 webpack 解析模塊時應(yīng)該搜索的目錄提岔。絕對路徑和相對路徑都能使用,但是要知道它們之間有一點(diǎn)差異笋敞。- 使用絕對路徑碱蒙,將只在給定目錄中搜索。使用相對路徑夯巷,通過查看當(dāng)前目錄以及祖先路徑赛惩。
- 如果想要優(yōu)先于某個目標(biāo)目錄搜索,則需把該目錄放到目標(biāo)目錄前面趁餐,可詳看官網(wǎng)例子
-
alias
:設(shè)置別名喷兼,方便使用,下面的例子應(yīng)用于src
目錄下的路徑使用 -
mainFields
:當(dāng)從 npm 包中導(dǎo)入模塊時(例如后雷,import * as D3 from 'd3')季惯,此選項(xiàng)將決定在 package.json 中使用哪個字段導(dǎo)入模塊。根據(jù) webpack 配置中指定的 target 不同臀突,默認(rèn)值也會有所不同勉抓。這里 browser 屬性是最優(yōu)先選擇的,因?yàn)樗?mainFields 的第一項(xiàng) -
extensions
:嘗試按順序解析這些后綴名候学。當(dāng)引入的文件不帶后綴名藕筋,且有多個文件有相同的名字,但后綴名不同盒齿,webpack 會解析列在數(shù)組首位的后綴的文件 并跳過其余的后綴念逞。
let path = require("path");
module.exports = {
resolve: {
modules: [path.resolve("node_modules")],
alias: {
"@": path.resolve(__dirname, "src"),
},
mainFields: ["browser", "module", "main"],
extensions: [".js", ".json", ".vue"],
},
}
多頁面配置
多頁面顧名思義就是多個 html 頁面困食,因此一般也會有多個 js 入口文件。
下面的配置中 entry 的 key
值對應(yīng)的是 output 屬性的 [name]
值翎承,HtmlWebpackPlugin 中的屬性 chunks
表示引入 [name]
對應(yīng)的 js 代碼文件硕盹,不指定 chunks
值將引入所有打包出來的 js 文件。
即本例的 [name]
分別為 home
和 other
叨咖,即打包出來是 home.js 和 other.js瘩例,最終打包的效果是 home.html
引入的是 home.js
,other.html
引入的是 other.js
文件
配置 webpack.config.js
let path = require("path");
let HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
mode: "development",
entry: {
home: "./src/js/index.js",
other: "./src/js/other.js",
},
output: {
filename: "js/[name].js",
path: path.resolve("dist")
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
filename: "home.html",
chunks: ['home']
}),
new HtmlWebpackPlugin({
template: "./src/other.html",
filename: "other.html",
chunks: ['other']
}),
],
}
webpack 小插件應(yīng)用
clean-webpack-plugin
清除插件甸各,可用于清除上一次的打包文件垛贤,清除目錄為 output.path
的值
安裝依賴
npm i clean-webpack-plugin -D
配置 webpack.config.js
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
plugins: [
new CleanWebpackPlugin(),
]
}
copy-webpack-plugin
拷貝插件,把某個文件夾導(dǎo)出到打包文件夾中趣倾,如文檔文件夾(如 doc 文件夾)
安裝依賴
npm i copy-webpack-plugin -D
配置 webpack.config.js
const CopyWebpackPlugin = require("copy-webpack-plugin"); // 拷貝文件
module.exports = {
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: "./doc",
to: "./doc",
},
],
}),
]
}
插件配置屬性
- patterns
- from: 源文件聘惦,相對于當(dāng)前目錄路徑
- to:目標(biāo)文件,相對于output.path文件路徑儒恋,會生成到 dist/doc 目錄下
webpack.BannerPlugin
版權(quán)聲明插件善绎,webpack 內(nèi)置插件,無需安裝
配置 webpack.config.js
const webpack = require("webpack");
module.exports = {
plugins: [
new webpack.BannerPlugin("copyright by Moon in 2022"),
]
}
打包后的文件開頭會帶上版權(quán)聲明大概如下:
[圖片上傳失敗...(image-1584af-1651569545228)]
watch
可以監(jiān)聽文件變化诫尽,當(dāng)它們修改后會重新編譯禀酱,可以用在實(shí)時打包的場景下
配置 webpack.config.js
watch: true,
watchOptions: {
poll: 1000, //每秒檢查一次變動
aggregateTimeout: 600, // 防抖
ignored: /node_modules/,
},
配置屬性
-
watchOptions
監(jiān)聽參數(shù)-
poll
: 每n毫秒檢查一次變動 -
aggregateTimeout
:防抖,當(dāng)?shù)谝粋€文件更改牧嫉,會在重新構(gòu)建前增加延遲剂跟。這個選項(xiàng)允許 webpack 將這段時間內(nèi)進(jìn)行的任何其他更改都聚合到一次重新構(gòu)建里。以毫秒為單位 -
ignored
:對于某些系統(tǒng)酣藻,監(jiān)聽大量文件會導(dǎo)致大量的 CPU 或內(nèi)存占用曹洽。可以使用正則排除像node_modules
如此龐大的文件夾
-
配置后在命令窗口輸入 npm run build
就可以進(jìn)行監(jiān)控并實(shí)時打包了臊恋,如圖實(shí)時打包了一次
[圖片上傳失敗...(image-7a8f7a-1651569545229)]
環(huán)境變量
通過 webpack 內(nèi)置插件 DefinePlugin
定義 DEV
環(huán)境變量衣洁。
還可以把開發(fā)和生產(chǎn)模式不同的 webpack 配置抽離出來,即把 webpack.config.js
文件一分為三
- 公共配置放在
webpack.config.base.js
文件 - 開發(fā)模式配置放在
webpack.config.dev.js
文件抖仅,通過webpack-merge
合并webpack.config.base.js
文件和webpack.config.dev.js
文件的配置 - 生產(chǎn)模式配置放在
webpack.config.prod.js
文件 (和開發(fā)模式配置文件邏輯一致)
webpack.config.dev.js
文件完整代碼如下:
let { merge } = require("webpack-merge");
let base = require("./webpack.config.base.js");
let HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require("webpack");
module.exports = merge(base, {
mode: "development",
devtool: "eval-source-map",
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html",
}),
new webpack.DefinePlugin({
ENV: JSON.stringify("dev"),
}),
],
devServer: {
compress: true,
client: { progress: true },
port: 5000,
// mock數(shù)據(jù)
setupMiddlewares: (middlewares, devServer) => {
if (!devServer) {
throw new Error("webpack-dev-server is not defined");
}
middlewares.unshift({
name: "fist-in-array",
// `path` 是可選的
path: "/user",
middleware: (req, res) => {
res.send({ name: "moon mock" });
},
});
return middlewares;
},
},
});
使用環(huán)境變量后目錄結(jié)構(gòu)大致如下
├─.eslintrc.json
├─package-lock.json
├─package.json
├─postcss.config.js
├─webpack.config.base.js
├─webpack.config.dev.js
├─webpack.config.prod.js
├─src
| ├─index.html
| ├─js
| | ├─index.js
| | ├─server.js
| | └test.js
| ├─image
| | └logo.png
| ├─css
| | ├─a.css
| | └index.css
├─doc
| └notes.md
├─dist
更改配置文件后坊夫,打包命令也要做適當(dāng)調(diào)整,打包時需要指定配置文件:
// 開發(fā)模式
webpack --config webpack.config.dev.js
// 生產(chǎn)模式
webpack --config webpack.config.prod.js
生產(chǎn)模式配置文件和公共配置文件源碼后期上傳
熱更新
webpack
的熱更新又稱熱替換(Hot Module Replacement
)撤卢,縮寫為 HMR
环凿。這個機(jī)制可以做到不用刷新瀏覽器而將新變更的模塊替換掉舊的模塊。默認(rèn)啟用熱更新放吩,無需配置智听,它會自動應(yīng)用 webpack.HotModuleReplacementPlugin
,這是啟用 HMR 所必需的。
優(yōu)化
下面的配置代碼都是在 webpack 配置文件中到推,不再贅述
縮小構(gòu)建范圍
include/exclude 選其一即可
module: {
rules: [
{
test: /\.js$/,
include: path.resolve(__dirname, "src"),
// exclude: /node_modules/,
},
]
}
module.noParse
由于webpack會通過入口文件解析 import
, require
引用的包考赛,還會去分析包的依賴,但有些包是沒有依賴的莉测,因此可以通過 noParse
不解析某個引用包中的依賴關(guān)系颜骤,來提高構(gòu)建性能。適合沒有依賴項(xiàng)的包捣卤,如 jquery
module: {
noParse: /jquery/,
}
webpack.IgnorePlugin
webpack 內(nèi)置插件 IgnorePlugin 可以阻止生成用于導(dǎo)入的模塊忍抽,或要求調(diào)用與正則表達(dá)式或篩選函數(shù)匹配的模塊。如 moment 包內(nèi)引入了很多語言包董朝,這些語言包都放在 locale 文件夾下鸠项,但大部分實(shí)際場景只會引用一個的語言包,因此打包時可忽略 moment 目錄下的 locale 語言包
new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/,
}),
忽略后再重新再js文件中引入某個語言包就能正常使用了
import "moment/locale/zh-cn";
moment.locale("zh-cn");
抽離公共代碼
一般用在多頁應(yīng)用場景或者是單個 js 文件太大子姜,請求需要很長時間祟绊,需要拆成幾個js文件,優(yōu)化請求速度闲询,使用 optimization 的 splitChunks 屬性來優(yōu)化久免。
splitChunks.cacheGroups 緩存組可以繼承和/或覆蓋來自 splitChunks.*
的任何選項(xiàng)。但是 test
扭弧、priority
和 reuseExistingChunk
只能在緩存組級別上進(jìn)行配置。將它們設(shè)置為 false
以禁用任何默認(rèn)緩存組记舆。
看下面配置之前先了解splitChunks的幾個屬性:
-
priority
:抽離代碼的優(yōu)先級鸽捻,值越高越先被抽離,防止某些模塊在前面的模塊抽離完了后面沒被抽離到泽腮,在本例中是防止vendor
模塊被common
模塊抽離完了沒被抽離到 -
name
:每個模塊(chunk)的文件名御蒲,不定義將是隨機(jī)名字 -
test
:匹配目錄 -
chunks
:選擇哪些 chunk 進(jìn)行優(yōu)化-
initial
:從入口處開始提取代碼,若有異步模塊考慮后面兩個值 -
async
:異步模塊 -
all
:可以存在異步和非異步模塊
-
-
minSize
:生成 chunk 的最小體積诊赊,此處為方便測試設(shè)置為 0 -
minChunks
:拆分前必須共享模塊的最小 chunks 數(shù)厚满,當(dāng)前代碼塊引用多少次才被抽離,此處為方便設(shè)置設(shè)置為 1
本例中分割了 common 和 vendor 兩個 chunk
optimization: {
// 分割代碼塊
splitChunks: {
// 緩存組
cacheGroups: {
// 公共模塊
commons: {
name: "common",
chunks: "initial",
minSize: 0,
minChunks: 1,
},
vendor: {
name: "vendor",
priority: 1,
test: /[\\/]node_modules[\\/]/,
chunks: "all", //包括異步和非異步代碼塊
},
},
},
},
為方便大家理解碧磅,獻(xiàn)上打包后的目錄樹結(jié)構(gòu)
├─index.html
├─js
| ├─common.js
| ├─common.js.LICENSE.txt
| ├─main.js
| ├─main.js.LICENSE.txt
| ├─vendor.js
| └vendor.js.LICENSE.txt
├─images
| └logo.png
├─doc
| └notes.md
├─css
| └main.css
這一塊比較難理解碘箍,建議多試幾次打包對比差異就懂了
懶加載
通過 es6 的 import()
語法實(shí)現(xiàn)懶加載,通過 jsonp
實(shí)現(xiàn)動態(tài)加載文件鲸郊,import 函數(shù)返回的是 promise 對象丰榴。vue 懶加載,react 懶加載都是這樣實(shí)現(xiàn)的秆撮。舉個簡單的栗子四濒,某些 js 文件在按鈕點(diǎn)擊后再請求加載。
此處省略獲取 button dom元素對象的代碼
button.addEventListener('click', function(){
import('./test.js').then(data => {
console.log(data);
})
})
除了以上的優(yōu)化方法之外,還有dll預(yù)構(gòu)建盗蟆,多線程構(gòu)建/壓縮戈二,利用緩存提升二次構(gòu)建速度,動態(tài)
polyfill
等等喳资,可根據(jù)實(shí)際情況自行選擇優(yōu)化方案觉吭,這里不一一贅述
webpack自帶優(yōu)化
tree-shaking
使用 import
語法在生產(chǎn)環(huán)境下沒用到的代碼不會被打包, 即 tree shaking, require
語法不支持tree-shaking
scope hosting
scope hosting(作用域提升),舉個栗子:
let a = 1
let b = 2
let c = 3
let d = a+b+c
console.log(d)
代碼打包出來只有最后一句, webpack打包會自動省略一些可以簡化的代碼
手寫簡易less-loader
less-loader.js
在 /loaders/less-loader.js
目錄文件中引入 less
插件
const less = require("less");
function loader(source) {
let css = "";
less.render(source, function (err, res) {
css = res.css;
});
}
module.exports = loader;
webpack.config.js
寫入以下配置
resolveLoader: {
alias: {
"lessLoader": path.resolve(__dirname, "loaders", "less-loader"))
}
},
module: {
rules: [
{
test: /\.less/,
use: ["style-loader", "lessLoader"]
}
]
}
最后
最后附上完整源碼骨饿,若有錯漏之處望指出~