vite打包性能優(yōu)化以及填坑
前言
大家好,我是 simple ,我的理想是利用科技手段來(lái)解決生活中遇到的各種問(wèn)題。
最近在使用 Vite4.0 構(gòu)建一個(gè)中型前端項(xiàng)目的過(guò)程中,遇到了一些坑,也做了一些項(xiàng)目在構(gòu)建生產(chǎn)環(huán)境時(shí)的優(yōu)化,在這里做一個(gè)記錄,以便后期查閱。(完整配置在后面)
項(xiàng)目?jī)?yōu)化前
上面是dist文件夾的截圖,里面的內(nèi)容已經(jīng)有30mb了,是時(shí)候該做點(diǎn)什么了。
分析
想要實(shí)現(xiàn)優(yōu)化,首先我得先知道,是什么占了這么大的空間。是圖片?是庫(kù)?還是其他靜態(tài)資源?
將文件分門(mén)別類,js,css這些資源目錄分別打包到對(duì)應(yīng)的文件夾下
js
復(fù)制代碼build: {
rollupOptions: {
output: {
chunkFileNames: 'js/[name]-[hash].js', // 引入文件名的名稱
entryFileNames: 'js/[name]-[hash].js', // 包的入口文件名稱
assetFileNames: '[ext]/[name]-[hash].[ext]', // 資源文件像 字體,圖片等
}
}
}
查看項(xiàng)目的依賴,找出大塊頭
rollup-plugin-visualizer是一個(gè)打包體積分析插件,對(duì)應(yīng)webpack中的webpack-bundle-analyzer。配置好后運(yùn)行構(gòu)建命令會(huì)生成一個(gè)stats.html。
bash
復(fù)制代碼npm i rollup-plugin-visualizer -D
js
復(fù)制代碼import { visualizer } from 'rollup-plugin-visualizer'
js
復(fù)制代碼plugins: [
visualizer({open: true})
]
arduino
復(fù)制代碼npm run build // 打包結(jié)束后會(huì)出現(xiàn)下圖
從體積能看到,這里已經(jīng)達(dá)到了7MB大小了,是時(shí)候該做點(diǎn)什么了。
優(yōu)化
拆分包
這里有一個(gè)自己的個(gè)人見(jiàn)解: 如果不同模塊使用的插件基本相同那就盡可能打包在同一個(gè)文件中,減少http請(qǐng)求,如果不同模塊使用不同插件明顯,那就分成不同模塊打包。這是一個(gè)矛盾體。 這里使用的是最小化拆分包。如果是前者可以直接選擇返回'vendor'。
scss
復(fù)制代碼rollupOptions: {
output: {
manualChunks(id) {
if (id.includes("node_modules")) {
// 讓每個(gè)插件都打包成獨(dú)立的文件
return id .toString() .split("node_modules/")[1] .split("/")[0] .toString();
}
}
}
}
去除debugger
bash
復(fù)制代碼npm i terser -D
js
復(fù)制代碼terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
CDN 加速
內(nèi)容分發(fā)網(wǎng)絡(luò)(Content Delivery Network,簡(jiǎn)稱 CDN)就是讓用戶從最近的服務(wù)器請(qǐng)求資源,提升網(wǎng)絡(luò)請(qǐng)求的響應(yīng)速度。同時(shí)減少應(yīng)用打包出來(lái)的包體積,利用瀏覽器緩存,不會(huì)變動(dòng)的文件長(zhǎng)期緩存。(不建議使用第三方cdn,這里做學(xué)習(xí)討論使用)
bash
復(fù)制代碼npm i rollup-plugin-external-globals -D
npm i vite-plugin-html -D
html
復(fù)制代碼<head>
<%- vuescript %>
</head>
css
復(fù)制代碼import { createHtmlPlugin } from 'vite-plugin-html'
rollupOptions: {
// 告訴打包工具 在external配置的 都是外部依賴項(xiàng) 不需要打包
external: ['vue'],
plugins: [
externalGlobals({
// "在項(xiàng)目中引入的變量名稱":"CDN包導(dǎo)出的名稱,一般在CDN包中都是可見(jiàn)的"
vue: 'Vue'
})
]
}
plugins: [
createHtmlPlugin({
minify: true,
inject: {
data: {
vuescript: '<script src="https://cdn.jsdelivr.net/npm/vue@3.2.37"></script>'
}
}
})
]
按需導(dǎo)入
仔細(xì)看上面那張圖右下部分的模塊,不知道你會(huì)不會(huì)感覺(jué)到奇怪,明明是同一個(gè)包,為什么既出現(xiàn)了lodash又出現(xiàn)了lodash-es。其實(shí)lodash-es 是 lodash 的 es modules 版本 ,是著具備 ES6 模塊化的版本,體積小,而lodash是common.js版本。lodash最大的缺陷就是無(wú)法按需導(dǎo)入。
js
復(fù)制代碼import _ from 'lodash-es'; // 你將會(huì)把整個(gè)lodash的庫(kù)引入到項(xiàng)目
import { cloneDeep } from 'lodash-es'; // 你將會(huì)把引入cloneDeep引入到項(xiàng)目
項(xiàng)目中用到lodash的地方也不多,經(jīng)過(guò)手動(dòng)修改一下,看現(xiàn)在已經(jīng)看不到lodash的庫(kù)了。
文件壓縮
復(fù)制代碼npm install vite-plugin-compression -D
js
復(fù)制代碼// build.rollupOptions.plugins[]
viteCompression({
verbose: true, // 是否在控制臺(tái)中輸出壓縮結(jié)果
disable: false,
threshold: 10240, // 如果體積大于閾值,將被壓縮,單位為b,體積過(guò)小時(shí)請(qǐng)不要壓縮,以免適得其反
algorithm: 'gzip', // 壓縮算法,可選['gzip',' brotliccompress ','deflate ','deflateRaw']
ext: '.gz',
deleteOriginFile: true // 源文件壓縮后是否刪除(我為了看壓縮后的效果,先選擇了true)
})
當(dāng)請(qǐng)求靜態(tài)資源時(shí),服務(wù)端發(fā)現(xiàn)請(qǐng)求資源為gzip的格式時(shí),應(yīng)該設(shè)置響應(yīng)頭 content-encoding: gzip 。因?yàn)闉g覽器解壓也需要時(shí)間,所以代碼體積不是很大的話不建議使用 gzip 壓縮。
圖片壓縮
bash
復(fù)制代碼yarn add vite-plugin-imagemin -D
or
bash
復(fù)制代碼npm i vite-plugin-imagemin -D
js
復(fù)制代碼import viteImagemin from 'vite-plugin-imagemin'
plugin: [
viteImagemin({
gifsicle: {
optimizationLevel: 7,
interlaced: false
},
optipng: {
optimizationLevel: 7
},
mozjpeg: {
quality: 20
},
pngquant: {
quality: [0.8, 0.9],
speed: 4
},
svgo: {
plugins: [
{
name: 'removeViewBox'
},
{
name: 'removeEmptyAttrs',
active: false
}
]
}
})
]
viteImagemin在國(guó)內(nèi)比較難安裝,容易出現(xiàn)報(bào)錯(cuò),可以嘗試一下下面幾種解決方案。
viteImagemin報(bào)錯(cuò)
使用 yarn 在 package.json 內(nèi)配置(推薦) "resolutions": { "bin-wrapper": "npm:bin-wrapper-china" }
使用 npm,在電腦 host 文件加上如下配置即可 199.232.4.133 raw.githubusercontent.com
使用 cnpm 安裝(不推薦)
填坑
坑1
在優(yōu)化過(guò)程中發(fā)現(xiàn)有什么rollupOption不生效,請(qǐng)檢查vite版本。上述配置在vite4.0版本生效,如需升級(jí),請(qǐng)前往官方遷移文檔。
坑2
Uncaught TypeError: Failed to resolve module specifier "Vue". Relative references must start with either "/", "./", or "../".
這里有可能是 vue-demi 引入了 vue,然而 rollup-plugin-external-globals 插件配置全局變量時(shí)不會(huì)處理 node_modules 下的依賴項(xiàng),導(dǎo)致 vue-demi 還是通過(guò) import 的方式與 node_modules 下的 vue 進(jìn)行關(guān)聯(lián),而沒(méi)有使用全局變量下的 vue,打包后 vue 已變成外部依賴項(xiàng),vue-demi 自然無(wú)法找到 vue,所以就報(bào)錯(cuò)了。
而vue-demi是哪里來(lái)的呢,我的項(xiàng)目是由于element-plus引用了vue-demi,所以此時(shí)解決方案就是將vue-demi也用cdn引入。
總結(jié)
到了這一步,整個(gè)文件夾已經(jīng)完全瘦身了。從一開(kāi)始的30MB到現(xiàn)在的11.8MB了。我們?cè)陧?xiàng)目里面放置了許多json數(shù)據(jù)(因?yàn)闃I(yè)務(wù)原因不能上傳到服務(wù)器),json數(shù)據(jù)已經(jīng)占了差不多5、6mb的原因,所以是一個(gè)單純的項(xiàng)目并沒(méi)有這么大。
配置
js
復(fù)制代碼// vite.config.js
import { defineConfig } from 'vite'
import { createHtmlPlugin } from 'vite-plugin-html'
import viteImagemin from 'vite-plugin-imagemin'
import externalGlobals from 'rollup-plugin-external-globals'
import { visualizer } from 'rollup-plugin-visualizer'
import viteCompression from 'vite-plugin-compression'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
visualizer({ open: true }),
// 將下面的添加到plugin下
createHtmlPlugin({
minify: true,
inject: {
data: {
vuescript: '<script src="https://cdn.jsdelivr.net/npm/vue@3.2.25"></script>',
demiScript: '<script src="http://cdn.jsdelivr.net/npm/vue-demi@0.13.7"></script>',
elementPlusScript: `
<link rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/element-plus@2.2.22/dist/index.full.min.js"></script>
`,
echartsSciprt: '<script src="https://cdn.jsdelivr.net/npm/echarts@5.0.2/dist/echarts.min.js"></script>'
}
}
}),
viteImagemin({
gifsicle: {
optimizationLevel: 7,
interlaced: false
},
optipng: {
optimizationLevel: 7
},
mozjpeg: {
quality: 20
},
pngquant: {
quality: [0.8, 0.9],
speed: 4
},
svgo: {
plugins: [
{
name: 'removeViewBox'
},
{
name: 'removeEmptyAttrs',
active: false
}
]
}
})
],
build: {
target: 'es2020',
minify: 'terser',
// rollup 配置
rollupOptions: {
output: {
chunkFileNames: 'js/[name]-[hash].js', // 引入文件名的名稱
entryFileNames: 'js/[name]-[hash].js', // 包的入口文件名稱
assetFileNames: '[ext]/[name]-[hash].[ext]', // 資源文件像 字體,圖片等
manualChunks(id) {
if (id.includes('node_modules')) {
return 'vendor'
}
}
},
// 告訴打包工具 在external配置的 都是外部依賴項(xiàng) 不需要打包
external: ['vue', 'element-plus', 'echarts'],
plugins: [
externalGlobals({
vue: 'Vue',
'element-plus': 'ElementPlus',
echarts: 'echarts',
'vue-demi': 'VueDemi'
}),
viteCompression({
verbose: true, // 是否在控制臺(tái)中輸出壓縮結(jié)果
disable: false,
threshold: 10240, // 如果體積大于閾值,將被壓縮,單位為b,體積過(guò)小時(shí)請(qǐng)不要壓縮,以免適得其反
algorithm: 'gzip', // 壓縮算法,可選['gzip',' brotliccompress ','deflate ','deflateRaw']
ext: '.gz',
deleteOriginFile: false // 源文件壓縮后是否刪除
})
]
},
terserOptions: {
compress: {
// 生產(chǎn)環(huán)境時(shí)移除console
drop_console: true,
drop_debugger: true
}
}
}
})
作者:simple_lau
歡迎關(guān)注微信公眾號(hào) :深圳灣碼農(nóng)