Vue配置Gzip压缩,添加CDN优化加载,采坑日记
config.js
const webpack = require('webpack')
const BundlePlugin = require('webpack-bundle-analyzer')
const CompressionPlugin = require('compression-webpack-plugin')
const envTime = new Date().getTime()
const envNode = process.env.VUE_APP_MODE
const product = Boolean(envNode === 'pro')
// CDN引入文件配置列表,需要与externals顺序对应
const scriptlist = {
css: ['https://cdn.jsdelivr.net/npm/vant@2.12.37/lib/index.min.css'],
js: [
'https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js',
'https://cdn.jsdelivr.net/npm/vue-router@3.2.0/dist/vue-router.min.js',
'https://cdn.jsdelivr.net/npm/vuex@3.4.0/dist/vuex.min.js',
'https://cdn.jsdelivr.net/npm/axios@0.24.0/dist/axios.min.js',
'https://cdn.jsdelivr.net/npm/vant@2.12.3/lib/vant.min.js',
'https://cdn.jsdelivr.net/npm/moment@2.29.1/moment.min.js',
'https://cdn.jsdelivr.net/npm/echarts@5.2.2/dist/echarts.min.js',
'https://cdn.jsdelivr.net/npm/html2canvas@1.3.3/dist/html2canvas.min.js',
'https://cdn.jsdelivr.net/npm/signature_pad@3.0.0-beta.4/dist/signature_pad.umd.min.js',
'https://cdn.jsdelivr.net/npm/tinymce@5.10.2/tinymce.min.js',
'https://cdn.jsdelivr.net/npm/@packy-tang/vue-tinymce@1.1.2/dist/vue-tinymce.umd.js'
],
sign: [
'sha384-OZmxTjkv7EQo5XDMPAmIkkvywVeXw59YyYh6zq8UKfkbor13jS+5p8qMTBSA1q+F',
'sha384-q2k+qGH0mwIAi2a3vbwlfY5KFOAQu6gKv1U2qcefUUVumCvayxYUx2NI125eTHkA',
'sha384-mE6GdOegC4x8XBAer/qsG6HyKLvw9mOi3Kjh3Jy6VX9RcKnrRuS/fG2OkVYeh3Zu',
'sha384-I2unte9Y12QMna3v071F1Nka0u7+32War1ddTq53DRJ7CSNfyRctw9b7oHce7hey',
'sha384-KjscdaP49SEKsCc9BDAeJETME8lF6l5b9Ia4Qa9W7FyHDgAgKJjknVRS4Hfjawbk',
'sha384-EgMpPgA8t6YITzfRZLLpZrVASrd7xd2dHvJCUYTH6YeRSNdorHBmyA5GRmJ328xc',
'sha384-zEboaczr+mRTdFbfynqn3FUVKNSHEcnpUUudSYLptsxK3zgb1UvCGfgEA2g33Dxe',
'sha384-SDHfRgULmZeD9p9uDA58EAdSvla8KNTW+tqSkvy6S0pYp2dmLqUJBAx3SZl8cq7f',
'sha384-Ls3uq+Y1QgaClb9daHoPrarDb+ZlWOGUnH1svrxe5OdEYzhNTdqEoLGRpSCYGQXm',
'sha384-FeTeQyBGB7Ipedniv1qfR8RtmJ1Yq6guxLPnRNucAA47AwrKMIuDPGjMQ0lLltrM',
'sha384-zwJhfl6tJG492ETsDCCkzV0skjhExHFLznFSKCPJ/G02HOsIjd/2p3KAcRQsCl/a'
]
}
module.exports = {
publicPath: 'xxx',
assetsDir: 'static',
filenameHashing: false,
productionSourceMap: product,
css: {
sourceMap: envNode === 'sit',
loaderOptions: {
sass: {
additionalData: `
@import "@/assets/scss/variable.scss";
@import "@/assets/scss/mixin.scss";
`
}
},
extract: product ? { ignoreOrder: true } : false
},
chainWebpack: (config) => {
config.plugin('html').tap(args => {
args[0].scriptlist = scriptlist
return args
})
if (product) {
config.plugins.delete('preload-index');
config.plugins.delete('prefetch-index');
}
},
/* pages: {
index: {
entry: './src/main.js',
template: './public/index.html',
filename: 'index.html',
title: 'gdyb-h5',
chunks: ['chunk-vendors', 'chunk-common', 'index'],
cdn: scriptlist
}
}, */
configureWebpack: (config) => {
// 配置CDN,打包忽略文件
config.externals = {
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
axios: 'axios',
vant: 'vant',
moment: 'moment',
echarts: 'echarts',
html2canvas: 'html2canvas',
signature_pad: 'SignaturePad',
tinymce: 'tinymce',
'vue-tinymce': 'VueTinymce'
}
// 文件添加时间戳
config.output = Object.assign(config.output, {
filename: `static/js/[name]-${envTime}.js`,
chunkFilename: `static/js/[name]-${envTime}.js`
})
// 配置打包GZIP
const compression = new CompressionPlugin({
algorithm: 'gzip',
filename: '[path].gz[query]',
test: /\.js$|\.html$|\.css$/,
minRatio: 1,
threshold: 10240,
deleteOriginalAssets: false
})
const limitchuck = new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 10,
minChunkSize: 1000
})
// 打包分析
const bundleAnalyzer = new (BundlePlugin.BundleAnalyzerPlugin)()
config.plugins.push(compression, limitchuck, bundleAnalyzer)
config.entry.app = ['babel-polyfill', './src/main.js']
// 打包关闭console
config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true
},
devServer: {
host: 'localhost',
port: 8080,
proxy: process.env.VUE_APP_BASE_API
}
}
index.html(src/public/index)
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="renderer" content="webkit">
<meta name="referrer" content="never">
<meta name="x5-orientation" content="portrait">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0,minimum-scale=1.0,maximum-scale=1.0">
<title>xxxxxxx</title>
<% for (const i in htmlWebpackPlugin.options.cdn.css) { %>
<link rel="stylesheet" href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" />
<% } %>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<% for (const i in htmlWebpackPlugin.options.cdn.js) { %>
<script
type="text/javascript"
crossorigin="anonymous"
src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"
integrity="<%= htmlWebpackPlugin.options.cdn.sign[i] %>"
></script>
<% } %>
</body>
</html>
报错信息汇总:
// compression-webpack-plugin版本太高,建议使用@5.0.1
ERROR TypeError:Cannot read property ‘tapPromise‘ of undefined
// 代码分析报告必须使用new BundleAnalyzerPlugin()
TypeError: Class constructor BundleAnalyzerPlugin cannot be invoked without 'new';
// 端口占用错误提示,杀掉占用端口即可
throw er; // Unhandled 'error' event
Error: listen EADDRINUSE: address already in use 127.0.0.1:8888
// 计算文件性能入口文件大小限制
The following entrypoint(s) combined asset size exceeds the recommended limit
// mini-css-extract-plugin,css引入顺序不一致
chunk chunk-common [mini-css-extract-plugin] Conflicting order.
推荐阅读相关[mini-css-extract-plugin]问题解析:
https://laysent.com/til/2019-11-28_conflicting-order-in-mini-css-extract-plugin
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。