1.原始篇
let path = require('path');
const webpack = require('webpack');
const ThemeColorReplacer = require('webpack-theme-color-replacer');
const { getThemeColors, modifyVars } = require('./src/utils/themeUtil');
const { resolveCss } = require('./src/utils/theme-color-replacer-extend');
const CompressionWebpackPlugin = require('compression-webpack-plugin');
// const productionGzipExtensions = ['js', 'css'];
const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i;
const isProd = process.env.NODE_ENV === 'production';
const assetsCDN = {
// webpack build externals
externals: {
// vue: 'Vue',
// 'vue-router': 'VueRouter',
// vuex: 'Vuex',
// axios: 'axios',
// nprogress: 'NProgress',
// clipboard: 'ClipboardJS',
// '@antv/data-set': 'DataSet',
// 'js-cookie': 'Cookies',
// AMap: "window.AMap"
},
css: [
// '//at.alicdn.com/t/font_2338481_gghb7xktb5.css'
],
js: [
// '//cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js',
// '//cdn.jsdelivr.net/npm/vue-router@3.3.4/dist/vue-router.min.js',
// '//cdn.jsdelivr.net/npm/vuex@3.4.0/dist/vuex.min.js',
// '//cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js',
// '//cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.js',
// '//cdn.jsdelivr.net/npm/clipboard@2.0.6/dist/clipboard.min.js',
// '//cdn.jsdelivr.net/npm/@antv/data-set@0.11.4/build/data-set.min.js',
// '//cdn.jsdelivr.net/npm/js-cookie@2.2.1/src/js.cookie.min.js'
]
};
module.exports = {
lintOnSave: false,
devServer: {
// host: "localhost",
open: true,
port: 8088,
//配置几个跨域接口
proxy: {
'/v2/city/lookup': {
target: 'https://geoapi.qweather.com', //请求城市定位区域接口地址
changeOrigin: true, //是否跨域
// pathRewrite: {
// '^/api': '' //路径重写
// }
},
'/v7/weather/now': {
target: 'https://devapi.qweather.com', //请求指定区域的接口
changeOrigin: true, //是否跨域
// pathRewrite: {
// '^/api': '' //路径重写
// }
}
}
},
pluginOptions: {
'style-resources-loader': {
preProcessor: 'less',
patterns: [path.resolve(__dirname, "./src/theme/theme.less")],
}
},
configureWebpack: config => {
config.entry.app = ["babel-polyfill", "whatwg-fetch", "./src/main.js"];
config.performance = {
hints: false
};
config.plugins.push(
new ThemeColorReplacer({
fileName: 'css/theme-colors-[contenthash:8].css',
matchColors: getThemeColors(),
injectCss: true,
resolveCss
})
);
// Ignore all locale files of moment.js
config.plugins.push(new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/))
// 生产环境下将资源压缩成gzip格式
if (isProd) {
// add `CompressionWebpack` plugin to webpack plugins
config.plugins.push(new CompressionWebpackPlugin({
algorithm: 'gzip',
// test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
test: productionGzipExtensions,
threshold: 4096,
minRatio: 0.8
}))
}
// if prod, add externals
if (isProd) {
config.externals = assetsCDN.externals
}
},
chainWebpack: config => {
// 生产环境下关闭css压缩的 colormin 项,因为此项优化与主题色替换功能冲突
if (isProd) {
config.plugin('optimize-css')
.tap(args => {
args[0].cssnanoOptions.preset[1].colormin = false;
return args
})
}
// 生产环境下使用CDN
if (isProd) {
config.plugin('html')
.tap(args => {
args[0].cdn = assetsCDN;
return args
})
}
},
css: {
loaderOptions: {
less: {
lessOptions: {
modifyVars: modifyVars(),
javascriptEnabled: true
}
}
}
},
publicPath: './',
outputDir: 'distM',
assetsDir: '',
productionSourceMap: false
};
2.改进篇
let path = require('path');
const webpack = require('webpack');
const ThemeColorReplacer = require('webpack-theme-color-replacer');
const { getThemeColors, modifyVars } = require('./src/utils/themeUtil');
const { resolveCss } = require('./src/utils/theme-color-replacer-extend');
const CompressionWebpackPlugin = require('compression-webpack-plugin');
// const productionGzipExtensions = ["js", "css"];
const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i;
const isProd = process.env.NODE_ENV === 'production';
const assetsCDN = {
// webpack build externals
externals: {
//不需要打包进来的资源
// vue: 'Vue',
// vuex: 'Vuex',
// 'vue-router': 'VueRouter',
// axios: 'axios',
// nprogress: 'NProgress',
// clipboard: 'ClipboardJS',
// '@antv/data-set': 'DataSet',
// 'js-cookie': 'Cookies',
// AMap: "window.AMap"
},
css: [
// '//at.alicdn.com/t/font_2338481_gghb7xktb5.css'
],
js: [
// '//cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js',
// '//cdn.jsdelivr.net/npm/vue-router@3.3.4/dist/vue-router.min.js',
// '//cdn.jsdelivr.net/npm/vuex@3.4.0/dist/vuex.min.js',
// '//cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js',
// '//cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.js',
// '//cdn.jsdelivr.net/npm/clipboard@2.0.6/dist/clipboard.min.js',
// '//cdn.jsdelivr.net/npm/@antv/data-set@0.11.4/build/data-set.min.js',
// '//cdn.jsdelivr.net/npm/js-cookie@2.2.1/src/js.cookie.min.js'
]
};
let timeStamp = new Date().getTime();
module.exports = {
publicPath: './',
outputDir: 'distM',
assetsDir: '',
productionSourceMap: false, // 是否为生产环境构建生成 source map?
lintOnSave: false, //是否开启eslint保存检测,有效值: true || false || 'error'
devServer: {
// host: "localhost",
open: true,
port: 8087,
overlay: {
errors: false,
warnings: false
},
//配置几个跨域接口
proxy: {
'/v2/city/lookup': {
target: 'https://geoapi.qweather.com', //请求城市定位区域接口地址
changeOrigin: true, //是否跨域
// pathRewrite: {
// '^/api': '' //路径重写
// }
},
'/v7/weather/now': {
target: 'https://devapi.qweather.com', //请求指定区域的接口
changeOrigin: true, //是否跨域
// pathRewrite: {
// '^/api': '' //路径重写
// }
}
}
},
pluginOptions: {
'style-resources-loader': {
preProcessor: 'less',
patterns: [path.resolve(__dirname, "./src/theme/theme.less")]
}
},
configureWebpack: config => {
config.entry.app = ["babel-polyfill", "whatwg-fetch", "./src/main.js"];
config.performance = {
hints: false
};
config.plugins.push(
new ThemeColorReplacer({
fileName: 'css/theme-colors-[contenthash:8].css',
matchColors: getThemeColors(),
injectCss: true,
resolveCss
})
);
// 生产环境下将资源压缩成gzip格式
if (isProd) {
// add `CompressionWebpack` plugin to webpack plugins
config.plugins.push(
// Ignore all locale files of moment.js 2024-01-18
new webpack.IgnorePlugin(/^\.\/locale$/,/moment$/),
new CompressionWebpackPlugin({
algorithm: 'gzip',
// test: new RegExp("\\.(" + productionGzipExtensions.join("|") + ")$"),
test: productionGzipExtensions,
threshold: 4096, //对4K以上的数据进行压缩
minRatio: 0.8,
}),
/**
* 新增限制 2024-01-18
* maxChunks:使用大于或等于 1 的值,来限制 chunk 的最大数量。使用 1 防止添加任何其他额外的 chunk,这是因为 entry/main chunk 也会包含在计数之中。
* minChunkSize: 设置 chunk 的最小大小。
* 限制打包的个数(减少打包生成的js文件和css文件)
*/
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 10,
minChunkSize: 100
}),
)
}
// if prod, add externals
if (isProd) {
config.externals = assetsCDN.externals
}
},
chainWebpack: config => {
if (isProd) {
// 移除 prefetch 插件
config.plugins.delete('prefetch');
// 移除 preload 插件
config.plugins.delete('preload');
// 压缩代码
config.optimization.minimize(true);
// 分割代码
config.optimization.splitChunks({
chunks: 'all'
});
// 生产环境下关闭css压缩的 colormin 项,因为此项优化与主题色替换功能冲突
config.plugin('optimize-css')
.tap(args => {
args[0].cssnanoOptions.preset[1].colormin = false;
return args
})
// 生产环境下使用CDN
config.plugin('html')
.tap(args => {
args[0].cdn = assetsCDN;
return args
})
}
},
css: {
extract: { // 打包后css文件名称添加时间戳 2024-01-18
filename: `css/[name].[hash:8].${timeStamp}.css`,
chunkFilename: `css/chunk.[id].[hash:8].${timeStamp}.css`,
},
loaderOptions: {
less: {
lessOptions: {
modifyVars: modifyVars(),
javascriptEnabled: true
}
}
}
},
// chunks: ['chunk-vendors', 'chunk-common']
};
通过对chunk生成的css和js文件数量和大小做限制,对代码进行压缩和分割,线上生产环境下使用cdn方式等对webpack打包优化。