const { resolve } = require("path");
// 图片压缩 webpack-image-loader
const hardPlugin = require("hard-source-webpack-plugin") // 使用缓存,优化启动速度
// const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer") // 打包分析插件
const CompressionPlugin = require("compression-webpack-plugin"); // 版本是 5.0.1 开启gzip压缩, 按需引用 后端也需要配置gzip
const UglifyJsPlugin = require('uglifyjs-webpack-plugin') // js压缩,附带去除console,貌似说不支持es6,被terser替代,目前没发现毛病
// PurgecssPlugin去除无用的css
const PurgecssPlugin = require('purgecss-webpack-plugin');
const glob = require('glob-all');
const path = require('path');
const prod = process.env.NODE_ENV === 'production' // 判断是否是生产环境
let cdn = {
css: [
'https://unpkg.com/element-ui/lib/theme-chalk/index.css' // element-ui css 样式表
],
js: [
// vue must at first!
'https://unpkg.com/vue@2.6.12/dist/vue.js', // vuejs
'https://unpkg.com/vue-router@3.5.3/dist/vue-router.min.js', // vue-router js
'https://unpkg.com/vuex@3.6.2/dist/vuex.min.js', // vuex js
'https://unpkg.com/element-ui/lib/index.js', // element-ui js
]
}
function setLess(config,types) {
types.forEach(type => {
let rule = config.module.rule('less').oneOf(type)
rule.use('style-resource')
.loader('style-resources-loader')
.options({
patterns: [resolve(__dirname, './src/assets/less/common.less')]
});
});
}
module.exports = {
publicPath: './',
productionSourceMap: false, // 生产环境是否要生成 sourceMap
outputDir: 'dist', // 打包生成的包名
assetsDir: 'assets', // 放置静态文件夹目录
// configureWebpack有两种写法,一种是对象的写法,还有一种是函数的写法
// configureWebpack对象写法
// configureWebpack: {
// plugins: [
// new hardPlugin({
// cacheDirectory: resolve(__dirname, "cache")
// }),
// // new BundleAnalyzerPlugin() // 模块打包分析插件
// ]
// },
// configureWebpack函数写法
configureWebpack: {
// PurgecssPlugin去除无用的css
plugins: [
new PurgecssPlugin({
paths: glob.sync([
path.join(__dirname, './src/index.html'),
path.join(__dirname, './**/*.vue'),
path.join(__dirname, './src/**/*.js')
])
})
]
},
configureWebpack: config => {
if(prod) {
const plugins = [];
plugins.push(
new hardPlugin({
cacheDirectory: resolve(__dirname, "cache")
}),
// new BundleAnalyzerPlugin() // 模块打包分析插件
new UglifyJsPlugin({
uglifyOptions: {
compress: {
// warnings: false,
drop_debugger: true, // console
drop_console: true,
pure_funcs:['console.log'] // 移除console
},
},
sourceMap: false,
parallel: true,
})
);
config.plugins = [...config.plugins, ...plugins];
}
},
chainWebpack: config => {
const types = ['vue-modules', 'vue', 'normal-modules', 'normal'];
setLess(config,types);
config.resolve.alias // 设置别名
.set('@', resolve('src'))
if(prod) {
config.module.rule('images').use('url-loader') // 图片转为base64,不用手动下载url-loader的
.tap(options => ({
name: './assets/images/[name].[contenthash:6].[ext]',
quality: 85,
limit: 10*1024, // 小于10kb的转为base64
esModule: false,
}));
// js文件output输出配置
config.output.filename('./js/[name].[chunkhash:6].js');
config.output.chunkFilename('./js/[name].[chunkhash:6].js');
// css合并 相当于webapck中的 mini-css-extract-plugin,可写可不写,@vue/cli已经默认打包到合并到css文件
config.plugin('extract-css').tap(args => [{
filename: 'css/[name].[contenthash:6].css',
chunkFilename: 'css/[name].[contenthash:6].css'
}]);
// js压缩插件 相当于webpack中的terser-webpack-plugin
// config.optimization.minimize(true)
// .minimizer('terser')
// .tap(args => {
// let { terserOptions } = args[0];
// terserOptions.compress.drop_console = true;
// terserOptions.compress.drop_debugger = true;
// return args
// });
// js gzip压缩
config.plugin("compression").use(CompressionPlugin, [
{
test: /\.(js|css)?$/i, // 哪些文件要压缩
filename: "[path].gz[query]", // 压缩后的文件名
algorithm: "gzip", // 使用gzip压缩
minRatio: 0.8, // 压缩率小于0.8才会压缩
deleteOriginalAssets: false, // 删除未压缩的文件,谨慎设置,如果希望提供非gzip的资源,可不设置或者设置为false
},
]);
// 排除npm包
config.externals({
vue: "Vue",
"vue-router": "VueRouter",
vuex: "Vuex",
"element-ui": 'ElementUI'
});
config.plugin('preload').tap(() => [
{
rel: 'preload',
fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],
include: 'initial'
}
])
// 注入cdn变量 (打包时会执行)
config.plugin('html').tap(args => {
// html-webpack-plugin
args[0].cdn = cdn // 配置cdn给插件
return args
})
}
},
devServer: {
port: '9527', // 开发时运行的端口
host: 'localhost', // 开发运行时域名
open: true, // 是否自动打开浏览器
proxy: { // 处理跨域
"/api": {
target: 'http://localhost:5000', // target代理到后端接口: http://localhost:5000
changeOrigin: true,
pathRewrite: { // 路径重写
"^/api": ''
}
}
}
}
}
public下的html文件配置cdn
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<!-- 引入样式 -->
<% for(var css of htmlWebpackPlugin.options.cdn.css) { %>
<link rel="stylesheet" href="<%=css%>">
<% } %>
<% for(var js of htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%=js%>"></script>
<% } %>
</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>
<!-- cdn的地址: https://www.jsdelivr.com/ -->
<!-- 引入样式 -->
<!-- 引入JS -->
<!-- 引入组件库 -->
<!-- <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-router@3.5.3/dist/vue-router.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuex@3.6.2/dist/vuex.min.js"></script> -->
<!-- 引入JS -->
</body>
</html>
最终显示
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/0491e65c9774ad2197c84ef2eb2d8990.png)
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/1d6a15c301fef7688a86519cf33e23b7.png)
REFERENCE
参考:https://juejin.cn/post/7004045635620405278