Gzip壓縮
前端頁面檔案快取
我們先來簡單回顧下 http 快取的知識:
- HTTP1.0 是透過Expires(檔案過期時間)和Last-Modified(最近修改時間)來告訴瀏覽器進行快取的,這兩個欄位都是 UTC 時間(絕對時間)。Expires 過期控制不穩定,因為瀏覽器端可以隨意修改本地時間,導致快取使用不精準。而且 Last-Modified 過期時間只能精確到秒。
- HTTP1.1 透過Cache-Contorl和 Etag(版本號)進行快取控制。瀏覽器先檢查 Cache-Control,如果有,則以Cache-Control 為準,忽略Expires。如果沒有 Cache-Control,則以Expires 為準。
Cache-Control 除了可以設定 max-age(相對過期時間,以秒為單位)以外,還可以設定如下幾種常用值:
- public,資源允許被中間伺服器快取。瀏覽器請求伺服器時,如果快取時間沒到,中間伺服器直接返回給瀏覽器內容,而不必請求源伺服器。
- private,資源不允許被中間代理伺服器快取。瀏覽器請求伺服器時,中間伺服器都要把瀏覽器的請求透傳給伺服器。
- no-cache,不管本地副本是否過期,每次訪問資源,瀏覽器都要向伺服器詢問,如果檔案沒變化,伺服器只告訴瀏覽器繼續使用快取(304)。
- no-store,瀏覽器和中間代理伺服器都不能快取資源。每次訪問資源,瀏覽器都必須請求伺服器,並且,伺服器不去檢查檔案是否變化,而是直接返回完整的資源。
- must-revalidate,本地副本過期前,可以使用本地副本;本地副本一旦過期,必須去源伺服器進行有效性校驗。
- proxy-revalidate,要求代理伺服器針對快取資源向源伺服器進行確認。
- s-maxage:快取伺服器對資源快取的最大時間。
現在 99%的瀏覽器都是 HTTP1.1 及以上版本,我們配置快取就使用 Cache-Contorl 和 Etag 配合就好了。
伺服器配置快取
檔名帶 hash 的(即 css、js、font 和 img 目錄下的所有檔案)設定一個月快取,瀏覽器可以直接使用快取不需要請求伺服器。其他的檔案(index.html 和 static 目錄下的檔案)設定為 no-cache,即每次都來伺服器檢查是否最新。nginx 配置如下:
server {
location = /index.html {
add_header Cache-Control no-cache;
}
location ~ /static/ {
add_header Cache-Control no-cache;
}
location ~ /(js/_|css/_|img/_|font/_) {
expires 30d;
add_header Cache-Control public;
}
}
前端檔案設定 gzip 壓縮
首先需要安裝一個 webpack 外掛,作用是將大檔案壓縮成 gzip 的格式。執行一下命令進行安裝:
npm install --save-dev compression-webpack-plugin
安裝成功後,在 vue.config.js 進行配置,配置如下:
const CompressionWebpackPlugin = require("compression-webpack-plugin");
// 可加入需要的其他檔案型別,比如json
// 圖片不要壓縮,體積會比原來還大
const productionGzipExtensions = ["js", "css"];
module.exports = {
configureWebpack: (config) => {
if (process.env.NODE_ENV === "production") {
return {
plugins: [
new CompressionWebpackPlugin({
// filename: '[path].gz[query]',
algorithm: "gzip",
test: new RegExp(
"\\.(" + productionGzipExtensions.join("|") + ")$"
),
threshold: 10240, //對超過10k的資料進行壓縮
minRatio: 0.6, // 壓縮比例,值為0 ~ 1
}),
],
};
}
},
};
這樣配置後,打包完的 js 和 css 檔案就多了字尾名為 gz 的檔案,下面是是否開啟 gzip 壓縮的檔案大小對比:
File | Size | Gzipped |
---|---|---|
dist\static\lib\echarts.4.0.6.min.js | 729.99KB | 243.57KB |
dis\static\lib\jquery.3.3.1.min.js | 84.89KB | 29.65KB |
dist\js\chuck-vendors.41428558.js | 2316.10KB | 623.74KB |
dist\js\app.aea87398.js | 1701.24KB | 447.00KB |
dist\css\app.c6e9f88a.css | 464.43KB | 137.84KB |
dist\css\chunk-vendors.aa340280.css | 263.42KB | 38.56KB |
伺服器配置 gzip 壓縮
Nginx 伺服器的配置檔案 nginx.conf 的 http 模組:
server {
# 開啟gzip on為開啟,off為關閉
gzip on;
# 檢查是否存在請求靜態檔案的gz結尾的檔案,如果有則直接返回該gz檔案內容,不存在則先壓縮再返回
gzip_static on;
# 設定允許壓縮的頁面最小位元組數,頁面位元組數從header頭中的Content-Length中進行獲取。
# 預設值是0,不管頁面多大都壓縮。
# 建議設定成大於10k的位元組數,配合compression-webpack-plugin
gzip_min_length 10k;
# 對特定的MIME型別生效,其中'text/html’被系統強制啟用
gzip_types text/javascript application/javascript text/css application/json;
# Nginx作為反向代理的時候啟用,開啟或者關閉後端伺服器返回的結果
# 匹配的前提是後端伺服器必須要返回包含"Via"的 header頭
# off(關閉所有代理結果的資料的壓縮)
# expired(啟用壓縮,如果header頭中包括"Expires"頭資訊)
# no-cache(啟用壓縮,header頭中包含"Cache-Control:no-cache")
# no-store(啟用壓縮,header頭中包含"Cache-Control:no-store")
# private(啟用壓縮,header頭中包含"Cache-Control:private")
# no_last_modefied(啟用壓縮,header頭中不包含"Last-Modified")
# no_etag(啟用壓縮,如果header頭中不包含"Etag"頭資訊)
# auth(啟用壓縮,如果header頭中包含"Authorization"頭資訊)
# any - 無條件啟用壓縮
gzip_proxied any;
# 請求加個 vary頭,給代理伺服器用的,有的瀏覽器支援壓縮,有的不支援,所以避免浪費不支援的也壓縮
gzip_vary on;
# 同 compression-webpack-plugin 外掛一樣,gzip壓縮比(1~9),
# 越小壓縮效果越差,但是越大處理越慢,一般取中間值
gzip_comp_level 6;
# 獲取多少記憶體用於快取壓縮結果,‘16 8k’表示以8k*16 為單位獲得。
# PS: 如果沒有.gz檔案,是需要Nginx實時壓縮的
gzip_buffers 16 8k;
# 注:99.99%的瀏覽器基本上都支援gzip解壓了,所以可以不用設這個值,保持系統預設即可。
gzip_http_version 1.1;
}
配置完 nginx 然後進行重啟,如果此時發現報錯資訊是 unknown directive "gzip_static" ,意味著 nginx 沒有安裝該模組,解決辦法如下: 進入到 nginx 安裝目錄,執行以下命令:
進入到 nginx 安裝目錄,執行以下命令:
./configure --with-http_gzip_static_module
然後執行:
make && make install
關閉 nginx:
systemctl stop nginx
啟動 nginx:
systemctl start nginx
檢查 gzip 是否生效
瀏覽器檔案請求的請求頭包含欄位 Accept-Encoding: gzip 代表瀏覽器支援 gzip 壓縮檔案,檔案響應頭包含欄位 Content-Encoding: gzip 代表返回的是壓縮檔案。
上面 nginx 配置 gzip_static on; 當我們不在 nginx 開啟 gzip_static 的時候,發現生產的 gz 檔案並沒有被執行。gzip_static 是會自動執行 gz 檔案的,這樣的就避免了透過 gzip 自動壓縮。換句話說,開啟之後 nginx 會優先使用我們的 gz 檔案。
如果 nginx 使用了已有的 gz 檔案,那麼這個請求的 etag 值不帶有 W/,反之,如果檔案是 nginx 壓縮的,etag 值則會帶有 W/。我們以剛才羅列的 app.aea87398.js 為例,下面是 Response Headers :
Cache-Control: max-age=2592000
Cache-Control: public
Content-Encoding: gzip
Content-Length: 455941
Content-Type: application/javascript
Date: Thu, 06 Aug 2020 03:17:24 GMT
Etag: "5f2b6d5e-6f505"
Expires: Sat, 05 Sep 2020 03:17:24 GMT
Last-Modified: Thu, 06 Aug 2020 02:39:26 GMT
Server: nginx/1.9.9
Vary: Accept-Encoding
會發現 Etag 的值為"5f2b6d5e-6f505" ,該值不以 W/ 開頭,則意味著使用了我們自己打包生成的 gz 檔案。
webpack效能最佳化
體積分析
經過webpack 打包後的體積最佳化是一個很重要的點,比如引入的第三方庫是否過大,能否對體積過大的庫進行最佳化。此時需要用到一款外掛,叫做webpack-bundle-analyzer 。它可以用互動式可縮放樹形圖顯示webpack輸出檔案的大小,用起來非常的方便。
首先安裝外掛:
npm install --save-dev webpack-bundle-analyzer
然後在vue.config.js 中引入:
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
module.exports = {
plugins: [new BundleAnalyzerPlugin()],
};
然後npm run serve 啟動專案,此時會預設啟動 http://127.0.0.1:8888,頁面裡可以清晰的檢視包占比的大小。建議只在開發環境啟用,生產環境採用預設配置會打包失敗。以tieshangongzhu 專案為例,打包結果中佔比比較大的第三方庫包括:iview.js 、moment.js 、lodash.js 等。下面介紹如何最佳化這些大的資源。
體積最佳化
這裡介紹支援按需引入的babel 外掛babel-plugin-import ,用來最佳化lodash 。
首先安裝外掛:
npm install babel-plugin-import --save-dev
然後在babel.config.js 中的plugins 陣列中新增一下配置:
["import", { libraryName: "lodash", libraryDirectory: "" }];
透過上述配置就完成了lodash 的按需載入。
接著我們來最佳化moment ,透過分析頁面檢視可知,moment 很大部分佔比是語言包,但我們基本用不到,於是我們可以藉助webpack自帶的外掛來忽略語言包。配置如下:
plugins: [new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)];
透過上述配置即可去除語言包,減少大概 70%的大小。
多程序構建
大家都知道 webpack 是執行在 node 環境中,而 node 是單執行緒的。webpack 的打包過程是 io 密集和計算密集型的操作,如果能同時 fork 多個程序並行處理各個任務,將會有效的縮短構建時間。
這裡採用thread-loader 進行多程序構建。
首先安裝loader :
npm install --save-dev thread-loader
然後在vue.config.js 新增如下配置:
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: ["thread-loader", "babel-loader"],
},
],
},
};
然後執行npm run build ,統計打包時長。透過對比會發現,引入前打包耗時 37s,引入後打包耗時 18s。速度有了一倍的提升。透過檢視可選項配置:
options: {
// the number of spawned workers, defaults to (number of cpus - 1) or
// fallback to 1 when require('os').cpus() is undefined
workers: 2,
// number of jobs a worker processes in parallel
// defaults to 20
workerParallelJobs: 50,
// additional node.js arguments
workerNodeArgs: ['--max-old-space-size=1024'],
// Allow to respawn a dead worker pool
// respawning slows down the entire compilation
// and should be set to false for development
poolRespawn: false,
// timeout for killing the worker processes when idle
// defaults to 500 (ms)
// can be set to Infinity for watching builds to keep workers alive
poolTimeout: 2000,
// number of jobs the poll distributes to the workers
// defaults to 200
// decrease of less efficient but more fair distribution
poolParallelJobs: 50,
// name of the pool
// can be used to create different pools with elsewise identical options
name: "my-pool"
}
可以看到,預設是啟用cpus - 1 個worker 來實現多程序打包。
多程序並行壓縮程式碼
上面我們提到了多程序打包,接下來應用一下可以並行壓縮JavaScript 程式碼的外掛。
webpack預設提供了UglifyJS外掛來壓縮JS程式碼,但是它使用的是單執行緒壓縮程式碼,也就是說多個js檔案需要被壓縮,它需要一個個檔案進行壓縮。所以說在正式環境打包壓縮程式碼速度非常慢(因為壓縮JS程式碼需要先把程式碼解析成用Object抽象表示的AST語法樹,再應用各種規則分析和處理AST,導致這個過程耗時非常大)。
這裡介紹一款可以並行壓縮程式碼的外掛:terser-webpack-plugin 。
首先安裝外掛:
npm install terser-webpack-plugin --save-dev
然後在vue.config.js 中新增配置,注意該配置位於configureWebpack 下,並且建議在生產環境開啟。
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
parallel: true,
terserOptions: {
output: {
comments: false, // remove comments
},
compress: {
warnings: false,
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log'], // remove console.log
},
},
extractComments: false,
}),
],
},
按照上述配置後,即可開啟並行壓縮 js 程式碼的功能。需要注意的是,V8 在系統上有記憶體的限制,預設情況下,32 位系統限制為 512M,64 位系統限制為 1024M。因為如果不加以限制,大型專案構建的時候可能會出現記憶體溢位的情況。也就是說,可能無法開啟並行壓縮的功能。但是壓縮程式碼的功能還是可以正常使用的。
利用快取提升二次構建速度
這裡討論下在webpack中如何利用快取來提升二次構建速度。
在webpack中利用快取一般有以下幾種思路:
- babel-loader開啟快取
- 使用cache-loader
- 使用hard-source-webpack-plugin
這裡重點介紹下第三種。HardSourceWebpackPlugin 為模組提供了中間快取,快取預設的存放路是: node_modules/.cache/hard-source。
配置 hard-source-webpack-plugin後,首次構建時間並不會有太大的變化,但是從第二次開始,構建時間大約可以減少 80%左右。
首先安裝外掛:
npm install --save-dev hard-source-webpack-plugin
然後在vue.config.js 中新增配置,建議配置在開發環境,生產環境可能沒有效果。
module.exports = {
plugins: [new HardSourceWebpackPlugin()],
};
在第二次執行npm run serve 後,可以看到終端會有以下字樣:
[hardsource:5e5f2c56] Using 144 MB of disk space.
[hardsource:5e5f2c56] Tracking node dependencies with: package-lock.json.
[hardsource:5e5f2c56] Reading from cache 5e5f2c56...
也就意味著構建從硬碟中讀取快取,加快了構建速度。
縮小構建目標
主要是exclude 與 include的使用:
- exclude: 不需要被解析的模組
- include: 需要被解析的模組
用的比較多的是排除/node_modules/ 模組。需要注意的是,exclude 權重更高,exclude 會覆蓋 include 裡的配置。
減少檔案搜尋範圍
這個主要是resolve相關的配置,用來設定模組如何被解析。透過resolve的配置,可以幫助Webpack快速查詢依賴,也可以替換對應的依賴。
- resolve.modules:告訴 webpack 解析模組時應該搜尋的目錄
- resolve.mainFields:當從 npm 包中匯入模組時(例如,import * as React from 'react'),此選項將決定在 package.json 中使用哪個欄位匯入模組。根據 webpack 配置中指定的 target 不同,預設值也會有所不同
- resolve.mainFiles:解析目錄時要使用的檔名,預設是index
- resolve.extensions:副檔名
resolve: {
alias: {
react: path.resolve(__dirname, './node_modules/react/umd/react.production.min.js')
}, //直接指定react搜尋模組,不設定預設會一層層的搜尋
modules: [path.resolve(__dirname, 'node_modules')], //限定模組路徑
extensions: ['.js'], //限定副檔名
mainFields: ['main'] //限定模組入口檔名
}
預編譯資源模組
在使用webpack進行打包時候,對於依賴的第三方庫,比如vue,vuex等這些不會修改的依賴,我們可以讓它和我們自己編寫的程式碼分開打包,這樣做的好處是每次更改原生代碼的檔案的時候,webpack只需要打包專案本身的檔案程式碼,而不會再去編譯第三方庫。
那麼第三方庫在第一次打包的時候只打包一次,以後只要我們不升級第三方包的時候,那麼webpack就不會對這些庫去打包,這樣的可以快速的提高打包的速度。其實也就是預編譯資源模組。
webpack中,我們可以結合DllPlugin 和 DllReferencePlugin外掛來實現。
DllPlugin 是什麼
DLLPlugin 外掛是在一個額外獨立的webpack設定中建立一個只有dll的bundle,也就是說我們在專案根目錄下除了有vue.config.js,還會新建一個webpack.dll.config.js檔案。
webpack.dll.config.js的作用是把所有的第三方庫依賴打包到一個bundle的dll檔案裡面,還會生成一個名為 manifest.json檔案。該manifest.json的作用是用來讓 DllReferencePlugin 對映到相關的依賴上去的。
DllReferencePlugin 又是什麼
這個外掛是在vue.config.js中使用的,該外掛的作用是把剛剛在webpack.dll.config.js中打包生成的dll檔案引用到需要的預編譯的依賴上來。
什麼意思呢?就是說在webpack.dll.config.js中打包後比如會生成 vendor.dll.js檔案和vendor-manifest.json檔案,vendor.dll.js檔案包含了所有的第三方庫檔案,vendor-manifest.json檔案會包含所有庫程式碼的一個索引,當在使用vue.config.js檔案打包DllReferencePlugin外掛的時候,會使用該DllReferencePlugin外掛讀取vendor-manifest.json檔案,看看是否有該第三方庫。
vendor-manifest.json檔案就是一個第三方庫的對映而已。
話不多說,接下來看看怎麼應用到專案中。
首先我們來編寫webpack.dll.config.js 檔案,內容如下:
const webpack = require("webpack");
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
mode: "production",
entry: {
vendor: [
"vue/dist/vue.runtime.esm.js",
"vuex",
"vue-router",
"vue-resource",
"iview",
], // 這裡是vue專案依賴的庫
util: ["lodash", "jquery", "moment"], // 這裡是與框架無關的第三方庫
},
output: {
filename: "[name].dll.js",
path: path.resolve(__dirname, "dll"),
library: "dll_[name]",
},
plugins: [
new CleanWebpackPlugin(), // clean-webpack-plugin目前已經更新到2.0.0,不需要傳引數path
new webpack.DllPlugin({
name: "dll_[name]",
path: path.join(__dirname, "dll", "[name].manifest.json"),
context: __dirname,
}),
],
};
在專案根目錄上新建webpack.dll.config.js ,填寫以上內容,同時還需要安裝CleanWebpackPlugin ,步驟省略。
然後我們需要執行命令將第三方庫打包到dll 資料夾下,該資料夾位於專案根目錄。
執行命令如下:
webpack --config ./webpack.dll.config.js
執行上述命令時如果提示Do you want to install 'webpack-cli' (yes/no) ,輸入yes 進行安裝webpack-cli 。成功後會發現專案根目錄生成dll 資料夾。資料夾下包含:
-util.dll.js - util.manifest.json - vendor.dll.js - vendor.manifest.json;
為了生成dll 資料夾方便,在package.json裡面再新增一條指令碼:
"scripts": {
"build:dll": "webpack --config ./webpack.dll.config.js",
},
以後就可以執行npm run build:dll來生成 了。
接下來需要在vue.config.js 中新增以下程式碼:
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin'); // 如果未安裝請先安裝
const dllReference = (config) => {
config.plugin('vendorDll')
.use(webpack.DllReferencePlugin, [{
context: __dirname,
manifest: require('./dll/vendor.manifest.json'),
}]);
config.plugin('utilDll')
.use(webpack.DllReferencePlugin, [{
context: __dirname,
manifest: require('./dll/util.manifest.json'),
}]);
config.plugin('addAssetHtml')
.use(AddAssetHtmlPlugin, [ // add-asset-html-webpack-plugin外掛必須在html-webpack-plugin之後使用,因此這裡要用webpack-chain來進行配置
[
{
filepath: require.resolve(path.resolve(__dirname, 'dll/vendor.dll.js')),
outputPath: 'dll',
publicPath: '/dll', // 這裡的公共路徑與專案配置有關,如果頂層publicPath下有值,請新增到dll字首
},
{
filepath: require.resolve(path.resolve(__dirname, 'dll/util.dll.js')),
outputPath: 'dll',
publicPath: '/dll', // 這裡的公共路徑與專案配置有關,如果頂層publicPath下有值,請新增到dll字首
},
],
])
.after('html'); // 'html'代表html-webpack-plugin,是因為@vue/cli-servide/lib/config/app.js裡是用plugin('html')來對映的
};
module.exports = {
publicPath: '/', // 頂層publiePath在這裡
chainWebpack: (config) => {
if (process.env.NODE_ENV === 'production') { // 在開發環境中不使用dllPlugin是因為chrome的vue devtool是不能檢測壓縮後的vue原始碼,不方便程式碼除錯
dllReference(config);
}
}
Vue專案引入tailwind.css
安裝
npm install -D tailwindcss@npm:@tailwindcss/postcss7-compat @tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9
由於專案中依賴的元件庫的postCSS版本是7.X,使用npm install tailwindcss安裝的版本依賴postCSS的8.X,因此需要安裝指定版本。
配置
npx tailwindcss init -p
透過執行以上命令來生成tailwind.config.js和postcss.config.js,如果專案中存在postcss.config.js,則只生成前者,不會覆蓋已有配置檔案。
tailwind.config.js位於專案的根目錄,預設配置如下:
// tailwind.config.js
module.exports = {
purge: [],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}
修改postcss.config.js配置:
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
引入
在專案的公共CSS檔案中新增如下配置,Tailwind將在構建時將這些指令替換為基於配置的設計系統生成的所有樣式:
/*! @import */
@tailwind base;
@tailwind components;
@tailwind utilities;
請務必新增詭異的註釋/*! @import */,避免在開發過程中Chrome DevTools中的效能問題。 最後在./src/main.js中引入css檔案即可。
自定義
一般來說,開發過程中使用最多的無外乎padding 、margin、width、height等屬性,這些屬性有一個共性就是需要指定具體的值。tailwind.config.js可以在theme下指定特殊的值來覆蓋預設值。
// tailwind.config.js
module.exports = {
purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {
borderWidth: {
default: '1px',
'0': '0',
'2': '2px',
'4': '4px',
},
spacing: {
'5': '5px',
'10': '10px',
'15': '15px',
'20': '20px',
'25': '25px',
'30': '30px',
},
colors: {
'gray-border': "#e8e8e8",
'gray-disabled': "#c5c8ce",
'gray-background': "#f8f8f9",
'blue-text': "rgba(25,118,253,.85)",
'blue-primary': "#3B91FF",
'blue-info': "#2db7f5",
'green-success': "#19be6b",
'yellow-warning': "#ff9900",
'red-error': "#ed4014",
'black-title': '#17233d',
'black-content': '#515a6e'
},
},
},
plugins: [],
prefix: 'tw-',
important: false, // Tailwind with existing CSS that has high specificity selectors
}
以上配置是目前專案中暫時用到的自定義值。
優勢
- 不用想破腦袋的去給class命名,不用給諸如只需要宣告flex樣式來增加一個class。
- css檔案不再增長。使用tailwind,所有內容都是可重用的,因此幾乎不需要編寫新的CSS。
- 更安全的變更,css是全域性性的,容易引發衝突;而class是區域性的,不需要擔心會產生衝突。
與內聯樣式相比,有以下優點:
- 有約束的設計。使用內聯樣式,每個值都是一個魔術數字。使用Tailwind,可以從預定義的設計系統中選擇樣式,這將使構建外觀一致的UI變得更加容易。
- 響應式設計。不能以內聯樣式使用媒體查詢,但是可以使用Tailwind的響應實用程式輕鬆構建完全響應的介面。
- 偽類。內聯樣式無法定位諸如懸停或焦點之類的狀態,但是Tailwind的偽類變體使使用實用程式類對這些狀態進行樣式設定變得容易。
vue.config.js程式碼
/**
* 配置參考: https://cli.vuejs.org/zh/config/
*/
const Timestamp = new Date().getTime()
var path = require("path");
// var webpack = require("webpack");
// 壓縮外掛
const CompressionWebpackPlugin = require('compression-webpack-plugin')
// 自定義配置外掛
const TerserPlugin = require('terser-webpack-plugin')
//獲取絕對地址
function resolve(dir) {
return path.join(__dirname, dir);
}
const { defineConfig } = require("@vue/cli-service");
module.exports = defineConfig({
transpileDependencies: true,
runtimeCompiler: true,
lintOnSave: false,
productionSourceMap: false,
publicPath: process.env.NODE_ENV === "production" ? "./" : "/",
chainWebpack: (config) => {
// // ============壓縮圖片 start============
// if (process.env.NODE_ENV === 'production') {
// config.module
// .rule('images')
// .use('image-webpack-loader')
// .loader('image-webpack-loader')
// .options({ bypassOnDebug: true })
// .end()
// // ============壓縮圖片 end============
// config.plugins.delete('prefetch') // vue-cli3.0 加上這行才能按需載入 移除 prefetch 外掛
// // 移除 preload 外掛
// config.plugins.delete('preload')
// // 壓縮程式碼
// config.optimization.minimize(true)
// // 分割程式碼
// config.optimization.splitChunks({
// chunks: 'initial', // async非同步程式碼分割 initial同步程式碼分割 all同步非同步分割都開啟
// minSize: 30000, // 位元組 引入的檔案大於30kb才進行分割
// // maxSize: 50000, //50kb,嘗試將大於50kb的檔案拆分成n個50kb的檔案
// minChunks: 1, // 模組至少使用次數
// maxAsyncRequests: 5, // 同時載入的模組數量最多是5個,只分割出同時引入的前5個檔案
// maxInitialRequests: 3, // 首頁載入的時候引入的檔案最多3個
// automaticNameDelimiter: '~', // 快取組和生成檔名稱之間的連線符
// name: true, // 快取組裡面的filename生效,覆蓋預設命名
// cacheGroups: { // 快取組,將所有載入模組放在快取裡面一起分割打包
// vendors: { // 自定義打包模組
// test: /[\\/]node_modules[\\/]/,
// priority: -10, // 優先順序,先打包到哪個組裡面,值越大,優先順序越高
// filename: 'vendors.js'
// },
// default: { // 預設打包模組
// priority: -20,
// reuseExistingChunk: true, // 模組巢狀引入時,判斷是否複用已經被打包的模組
// filename: 'common.js'
// }
// }
// })
// }
config.module.rule("svg").exclude.add(resolve("src/icons")).end();
config.module.rule("thread")
.test(/\.js$/)
.exclude.add(resolve("/node_modules/"))
.end()
.use("thread-loader")
.loader("thread-loader")
.end()
.use("babel-loader")
.loader("babel-loader")
.end();
config.module
.rule("icons")
.test(/\.svg$/)
.include.add(resolve("src/icons"))
.end()
.use("svg-sprite-loader")
.loader("svg-sprite-loader")
.options({
symbolId: "[name]",
})
.end();
config.module
.rule("js")
.include.add(/src/)
.add(/test/)
.add(/node_modules\/npm-lifecycle\/node-gyp-bin/)
.add(/node_modules\/@wangeditor/)
.end();
},
configureWebpack: (config) => {
if (process.env.NODE_ENV === 'production') {
// webpack 配置
config.plugins.push(
new CompressionWebpackPlugin({
test: /\.js$|\.html$|\.map$|\.css$/,
// 超過4kb壓縮
threshold: 4096,
deleteOriginalAssets: true, // 刪除原始檔案
})
)
config.plugins.push(
new TerserPlugin({
terserOptions: {
// 自動刪除console
compress: {
// warnings: false, // 若打包錯誤,則註釋這行
drop_debugger: true,
drop_console: true,
pure_funcs: ['console.log']
}
},
cache: true,
sourceMap: false,
parallel: true
})
)
config.output.filename = `[name].${Timestamp}.js`
config.output.chunkFilename = `[name].${Timestamp}.js`
} else {
config.devtool = 'source-map'
}
},
css: {
loaderOptions: {
// 沒有分號會報錯
sass: {
sourceMap: true,
additionalData: `
@import "@/assets/scss/variables.scss";
`,
},
},
},
productionSourceMap: false,
devServer: {
port: 8002,
proxy: {
"/api": {
target: "http://192.168.5.23:8408/api",
ws: true,
changeOrigin: true,
pathRewrite: {
"^/api": "",
},
},
},
},
});