修改vue.config.js
//vue.config.js
const os = require('os');
const path = require('path');
const { HashedModuleIdsPlugin } = require('webpack');
const WebpackBar = require('webpackbar');
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const HappyPack = require('happypack');
const CompressionPlugin = require('compression-webpack-plugin');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const WebpackMd5Hash = require('webpack-md5-hash')
// 开辟一个线程池,拿到系统CPU的核数,happypack 将编译工作利用所有线程
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length });
const isProduction = process.env.NODE_ENV !== 'development';
const port = process.env.port || process.env.npm_config_port || 8998; // 端口
const themePath = process.env.VUE_APP_THEME ? `-${ process.env.VUE_APP_THEME }` : '';
module.exports = {
publicPath: process.env.PATH_SUFFIX,
outputDir: 'dist',
assetsDir: 'static',
lintOnSave: process.env.NODE_ENV === 'development',
productionSourceMap: false,
parallel: require('os').cpus().length > 1, // cpu核数
devServer: {
port: port,
open: true,
proxy: {
'/api': {
target: process.env.VUE_APP_URL,
changeOrgin: true,
pathRewrite: {
'^/api': ''
}
},
'/dapi': {
target: process.env.VUE_APP_URL,
changeOrgin: true,
pathRewrite: {
'^/dapi': ''
}
},
},
},
pluginOptions: {
'style-resources-loader': {
preProcessor: 'less',
patterns: [ path.resolve(__dirname, `src/assets/less/index${ themePath }.less`) ] // 引入全局样式变量
}
},
css: {
extract: true,
sourceMap: false,
requireModuleExtension: true
},
configureWebpack: {
externals: {
'AMap': 'AMap', // 高德地图配置
'AMapUI': 'AMapUI', // 高德地图AMapUI
},
resolve: {
modules: [ path.resolve((__dirname, './src')) ],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': path.resolve((__dirname, './src')),
'@api': path.resolve((__dirname, './src/api')),
'@utils': path.resolve((__dirname, './src/utils')),
'vuex': path.resolve(__dirname, './node_modules/vuex/dist/vuex.min.js'),
'vue-router': path.resolve(__dirname, './node_modules/vue-router/dist/vue-router.min.js'),
'axios': path.resolve(__dirname, './node_modules/axios/dist/axios.min.js')
}
},
performance: {
'maxEntrypointSize': 10000000,
'maxAssetSize': 30000000,
// 只给出 js 文件的性能提示
assetFilter: function (assetFilename) {
return assetFilename.endsWith('.js')
}
},
optimization: {
minimizer: [
new HashedModuleIdsPlugin(),
new UglifyJsPlugin({ // 代码压缩
uglifyOptions: {
output: {
comments: false, // 删除注释
beautify: false, // 最紧凑的输出
},
compress: {
drop_debugger: true, //清除 debugger 语句
drop_console: true, //清除console语句
pure_funcs: [ 'console.log' ],
collapse_vars: true,
reduce_vars: true
}
},
sourceMap: false,
parallel: true,
cache: true,
include: /\/src/,
// include: path.resolve((__dirname, './src')),
chunkFilter: chunk => {
// `vendor` 块不压缩
if (chunk.name === 'vendor') {
return false;
}
return true;
},
})
],
splitChunks: {
chunks: 'all', // 可选值:all,async 和 initial。all功能最强大,所以咱们就使用all
maxAsyncRequests: 10, // 每个异步加载模块最多能被拆分的数量
maxInitialRequests: Infinity, // 每个入口和它的同步依赖最多能被拆分的数量 Infinity 10
enforceSizeThreshold: 50000, // 强制执行拆分的体积阈值并忽略其他限制
cacheGroups: {
libs: { // 第三方库
name: 'chunk-libs',
test: /[\\/]node_modules[\\/]/, // 请注意'[\\/]'的用法,是具有跨平台兼容性的路径分隔符
priority: 10, // 优先级,执行顺序就是权重从高到低
chunks: 'initial' // 只打包最初依赖的第三方
},
elementUI: { // 把 elementUI 单独分包
name: 'chunk-elementUI',
test: /[\\/]node_modules[\\/]_?element-ui(.*)/,
priority: 12, // 权重必须比 libs 大,不然会被打包进 libs 里
reuseExistingChunk: true,
enforce: true
},
xlsxJs: {
name: 'chunk-xlsxJs',
test: /[\\/]node_modules[\\/]_?xlsx(.*)/,
priority: 13,
reuseExistingChunk: true,
enforce: true
},
pdfJs: {
name: 'chunk-pdfJs',
test: /[\\/]node_modules[\\/]_?pdf+(.*)/,
priority: 14,
reuseExistingChunk: true,
enforce: true
},
bpmnJs: {
name: 'chunk-bpmnJs',
test: /[\\/]node_modules[\\/]_?bpmn(.*)/,
priority: 15,
reuseExistingChunk: true,
enforce: true
},
// commons: {
// name: 'chunk-commons',
// minChunks: 2, // 拆分前,这个模块至少被不同 chunk 引用的次数
// priority: 20,
// reuseExistingChunk: true
// },
// commons: {
// test: /[\\/]node_modules[\\/]/,
// name(module) {
// const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
// return `npm.${ packageName.replace('@', '') }`;
// },
// },
components: {
chunks: 'all',
test: /[\\/]src[\\/]components[\\/]/,
priority: 21,
name: 'chunk-components',
},
mixin: {
chunks: 'all',
test: /[\\/]src[\\/]mixin[\\/]/,
priority: 22,
name: 'chunk-mixin',
},
orderCenter: {
chunks: 'all',
test: /[\\/]src[\\/]views[\\/]orderCenter[\\/]/,
name: 'chunk-orderCenter',
priority: 23,
},
vehicleCenter: {
chunks: 'all',
test: /[\\/]src[\\/]views[\\/]vehicleCenter[\\/]/,
name: 'chunk-vehicleCenter',
priority: 24,
},
workFlow: {
chunks: 'all',
test: /[\\/]src[\\/]views[\\/]workFlow[\\/]/,
name: 'chunk-workFlow',
priority: 25,
},
systemSettings: {
chunks: 'all',
test: /[\\/]src[\\/]views[\\/]systemSettings[\\/]/,
name: 'chunk-systemSettings',
priority: 26,
},
commonsJS: {
chunks: 'all',
test: /[\\/]src[\\/]js[\\/]/,
name: 'chunk-commonsJS',
minChunks: 2,
maxInitialRequests: 5,
minSize: 0,
priority: 27,
},
styles: {
name: 'styles',
test: /[\\/]src[\\/]\.(sa|sc|c)ss$/,
chunks: 'all',
priority: 28,
enforce: true
},
svgIcon: {
name: 'chunk-svgIcon',
// 函数匹配示例,把 svg 单独拆出来
test(module) {
// `module.resource` 是文件的绝对路径
// 用`path.sep` 代替 / or \,以便跨平台兼容
// const path = require('path') // path 一般会在配置文件引入,此处只是说明 path 的来源,实际并不用加上
return (
module.resource &&
module.resource.endsWith('.svg') &&
module.resource.includes(`${ path.sep }icons${ path.sep }`)
)
},
priority: 29
},
}
}
},
plugins: [
new WebpackMd5Hash(),
new WebpackBar(),
new HardSourceWebpackPlugin({
root: process.cwd(),
directories: [],
environmentHash: {
root: process.cwd(),
directories: [],
// 配置了files 的主要原因是解决配置更新,cache 不生效了的问题
// 配置后有包的变化,plugin 会重新构建一部分 cache
files: [ 'package.json', 'yarn.lock' ]
}
}),
new HappyPack({
id: 'happybabel',
loaders: [ 'babel-loader?cacheDirectory=true' ],
threadPool: happyThreadPool
}),
new HappyPack({
id: 'vue',
loaders: [ 'vue-loader?cacheDirectory=true' ],
//共享进程池
threadPool: happyThreadPool
}),
new HappyPack({
id: 'styles',
loaders: [ 'css-loader?cacheDirectory=true', 'less-loader?cacheDirectory=true' ],
//共享进程池
threadPool: happyThreadPool
})
],
devtool: 'source-map'
},
chainWebpack: config => {
// 避免无用查找
config.resolve.extensions.clear()
.add('.js')
.add('.vue')
.add('.json')
if (isProduction) { // 生产环境相关配置
// 去掉console-log
config.optimization.minimizer('terser').tap(args => {
args[0].terserOptions.compress.drop_console = true
return args
})
const productionGzipExtensions = [ 'js', 'css', 'json', 'ico', 'html', 'svg', 'webp' ]
config.plugin('compressionPlugin')
.use(new CompressionPlugin({
test: new RegExp(
`\\.(${ productionGzipExtensions.join('|') })$`
),
algorithm: 'gzip', // 使用gzip压缩
threshold: 10240, // 对10K以上的数据进行压缩
minRatio: 0.8, // 只有压缩率小于这个值的资源才会被处理
filename: '[path].gz[query]',
// filename: '[path][base].gz', // 压缩后的文件名
deleteOriginalAssets: false // 删除未压缩的文件,谨慎设置,如果希望提供非gzip的资源,可不设置或者设置为false
}));
// 临时添加
config.plugin('CssMinimizerPlugin').use(new CssMinimizerPlugin()) // css压缩
config.plugin('speedMeasurePlugin').use(new SpeedMeasurePlugin())
// 移除 prefetch 插件
config.plugins.delete('prefetch');
// 移除 preload 插件
config.plugins.delete('preload');
}
config.module
.rule('images')
.use('url-loader')
.options({
limit: 10240, // 稍微改大了点
})
.end()
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({
progressive: true,
optimizationLevel: 7,
interlaced: false,
mozjpeg: { progressive: true, quality: 50 }, // 压缩JPEG图像
optipng: { enabled: false }, // 压缩PNG图像
// pngquant: { quality: [ 0.65, 0.9 ], speed: 4 }, // 压缩PNG图像
gifsicle: { interlaced: false }, // 压缩GIF图像
webp: { quality: 75 } // 压缩webp
// bypassOnDebug: true
})
.end();
}
}