为什么要升级Vue-cli4?
因为Vue-cli基于webpack4封装的,而我想用webpack5,为什么想用webpack5?那咱们要先看看webpack4和webpack5的区别了。
1.Hash生成方式改变。
Webpack4是根据代码的结构生成chunkhash,添加了空白行或注释,会引起chunkhash的变化。 Webpack5是根据内容生成chunkhash,改了注释或者变量不会引起chunkhash的变化,浏览器可以继续使用缓存。
2.优化了对缓存的使用效率。
在Webpack4 中,chunkId与moduleId都是自增id。只要我们新增一个模块,那么代码中module的数量就会发生变化,从而导致moduleId发生变化,于是文件内容就发生了变化。chunkId也是如此,新增一个入口的时候,chunk数量的变化造成了chunkId的变化,导致了文件内容变化。所以对实际未改变的chunk文件不能有效利用。 Webpack 模块 ID 默认是按照依赖顺序递增分配的,这会使得依赖了某个出现改动 chunk 的其他 chunk 内容也跟着变化,导致浏览器缓存失效。增减模块会导致顺序错位 Webpack5采用新的算法来计算确定性的chunkId和moduleId。可以有效利用缓存。在production模式下,optimization.chunkIds和optimization.moduleIds默认会设为deterministic.deterministic 选项有益于长期缓存,生成bundles比 hashed 小,比size大。稳定性ID, 这是包大小和长期缓存之间的一种权衡 。
.env环境配置
1.环境变量和模式
.env # 在所有的环境中被载入
.env.local # 在所有的环境中被载入,但会被 git 忽略
.env.[mode] # 只在指定的模式中被载入
.env.[mode].local # 只在指定的模式中被载入,但会被 git 忽略
使用:
vue-cli-service serve --mode dev
联合命令的方式:
"build": "npm run style && vue-cli-service build --mode production"
注意: 环境里所有的变量都是string类型,所以某个变量是true,实际获取的是字符串‘true’.
编辑器配置
1.配置默认eslint规范
.editorconfig # 编辑器配置
.eslintrc.js # eslint配置
.prettierrc # Prettier - Code formatter插件配置
.eslintignore # 不需要格式化的文件以及文件夹
使用:
npm i eslint@7.32.0 eslint-config-prettier@8.5.0 eslint-plugin-prettier@4.0.0 -D
注意:
1.配合Visual Code里的Prettier - Code formatter插件。
2.不要下载Prettier ESLint插件,会有莫名奇妙的冲突。
3.注意版本对应关系。
Vue.config.js配置文件
1.Path模块
const resolve = (dir) => path.join(__dirname, dir)
path.join(__dirname,'a','b') 拼接出来就是是D:/test/1/a/b,
__dirname :当前js文件的绝对路径,D:/test/1
path.resolve(),,用到当前项目的绝对路径,
path.resolve('www', 'imgs/png/', '../abc/image.jpg')
拼接出来就是D:\test\1\www\imgs\abc\image.jpg
2.插件
2.1 css-minimizer-webpack-plugin 压缩Css的插件
optimization: {
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
// `...`,
new CssMinimizerPlugin(),
],
},
如果还想在开发环境下启用 CSS 优化,请将 optimization.minimize
设置为 true
:
// [...]
module.exports = {
optimization: {
// [...]
minimize: true,
minimizer: [
// 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`),将下一行取消注释
// `...`,
new CssMinimizerPlugin(),
],
},
};
2.2 mini-css-extract-plugin 提取单独css文件的插件
plugins: [
// 提取 CSS
new MiniCssExtractPlugin({
filename: 'css/[contenthash]-[name].css',
}),
]
tips: filename路径会忽略output或者assets路径。
2.3 terser-webpack-plugin js压缩插件
optimization: {
minimizer: [
new TerserWebpackPlugin({
parallel: 4, //并发运行的默认数量
terserOptions: {
parse: {
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
mangle: {
safari10: true,
},
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
}),
],
}
2.4 progress-bar-webpack-plugin 进度条插件
plugins: [
// 进度条
new ProgressBarPlugin({
format: ` :msg [:bar] ${chalk.green.bold(
':percent'
)} (:elapsed s)`,
}),
]
2.5 uglifyjs-webpack-plugin 去除console 的插件
if (isPro) {
plugins.push(
new UglifyJsPlugin({
uglifyOptions: {
output: {
comments: false, // 去掉注释
},
warnings: false,
compress: {
drop_console: true,
drop_debugger: false,
pure_funcs: ['console.log'], //移除console
},
},
})
)
}
2.6 style-resources-loader 配置全局less变量
pluginOptions: {
'style-resources-loader': {
preProcessor: 'less',
patterns: [resolve('./theme/default/var.less')],
},
},
整个vue.config.js
/*
* @Description:
* @LastEditTime: 2022-06-13 18:26:35
*/
const { defineConfig } = require('@vue/cli-service'),
isPro = process.env.NODE_ENV === 'production',
publicPath = isPro ? './' : '/',
path = require('path'),
chalk = require('chalk'),
webpack = require('webpack'),
resolve = (dir) => path.join(__dirname, dir)
// 插件
const CssMinimizerWebpackPlugin = require('css-minimizer-webpack-plugin'), //压缩 CSS 文件。
MiniCssExtractPlugin = require('mini-css-extract-plugin'), //插件将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载。
TerserWebpackPlugin = require('terser-webpack-plugin'), //来压缩 JavaScript。
ProgressBarPlugin = require('progress-bar-webpack-plugin'), //进度条插件
PurgeCSSPlugin = require('purgecss-webpack-plugin'), //对 CSS Tree Shaking。
UglifyJsPlugin = require('uglifyjs-webpack-plugin') //去除console
module.exports = defineConfig({
publicPath, //所以请始终使用 publicPath 而不要直接修改 webpack 的 output.publicPath。
// 输出文件目录
// outputDir: 'dist', //请始终使用 outputDir 而不要修改 webpack 的 output.path。
// assetsDir: 'assets',
// indexPath:'index.html',//指定生成的 index.html 的输出路径 (相对于 outputDir)。也可以是一个绝对路径。
// 静态资源存放的文件夹(相对于ouputDir)
runtimeCompiler: true,
filenameHashing: true,
productionSourceMap: !isPro,
lintOnSave: false, //启动关闭eslint
chainWebpack: (config) => {
const plugins = []
// 修复HMR
config.resolve.symlinks(true)
if (isPro) {
plugins.push(
new UglifyJsPlugin({
uglifyOptions: {
output: {
comments: false, // 去掉注释
},
warnings: false,
compress: {
drop_console: true,
drop_debugger: false,
pure_funcs: ['console.log'], //移除console
},
},
})
)
// 必须使用cnpm安装,否则报错
// config.module
// .rule('images')
// .use('image-webpack-loader')
// .loader('image-webpack-loader')
// .options({
// mozjpeg: { progressive: true, quality: 65 },
// optipng: { enabled: false },
// pngquant: { quality: [0.65, 0.9], speed: 4 },
// gifsicle: { interlaced: false },
// webp: { quality: 75 },
// })
}
// 如果使用多页面打包,使用vue inspect --plugins查看html是否在结果数组中
// config.plugin('html').tap((args) => {
// // html中添加cdn
// args[0].cdn = cdn
// return args
// })
},
configureWebpack: {
output: {
pathinfo: false, //默认 webpack 会在输出的 bundle 中生成路径,将路径信息删除可小幅提升构建速度。
// // 仅在生产环境添加 hash
// filename: isPro
// ? '[name].[contenthash].bundle.js'
// : '[name].bundle.js',
},
resolve: {
// 配置externals:防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖
extensions: ['.css', '.js', '.vue', '.less', '.html'],
symlinks: false, //如果项目不使用 symlinks(例如 npm link 或者 yarn link),
alias: {
'@': resolve('./src'),
'@img': resolve('./src/assets/images'),
'@modules': resolve('./src/modules'),
},
},
// cache: {
// type: 'filesystem', // 不用缓存很是影响速度
// },
optimization: {
moduleIds: 'deterministic', //让公共包 splitChunks 的 hash 不因为新的依赖而改变,减少非必要的 hash 变动
runtimeChunk: true, //为运行时代码创建一个额外的 chunk,减少 entry chunk 体积,提高性能。
minimizer: [
new TerserWebpackPlugin({
parallel: 4, //并发运行的默认数量
terserOptions: {
parse: {
ecma: 8,
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
},
mangle: {
safari10: true,
},
output: {
ecma: 5,
comments: false,
ascii_only: true,
},
},
}),
new CssMinimizerWebpackPlugin({
parallel: 4, //并发运行的默认数量
}),
],
splitChunks: {
//项目中分别有a.js, b.js, page1.js, page2.js这四个JS文件, page1.js 和
//page2.js中同时都引用了a.js, b.js, 这时候想把a.js, b.js抽离出来合并成一个公共的js,
// 分割代码块
cacheGroups: {
vendor: {
//第三方库抽离
name: 'vendor',
chunks: 'all',
test: /node_modules/,
minChunks: 1, //在分割之前,这个代码块最小应该被引用的次数
maxInitialRequests: 5,
minSize: 0, //大于0个字节
priority: 100, //权重
},
common: {
//公用模块抽离
name: 'common',
chunks: 'all',
test: /[\\/]src[\\/]js[\\/]/,
minChunks: 2, //在分割之前,这个代码块最小应该被引用的次数
maxInitialRequests: 5,
minSize: 0, //大于0个字节
priority: 60,
},
styles: {
//样式抽离
name: 'styles',
test: /\.(sa|sc|le|c)ss$/,
chunks: 'all',
enforce: true,
},
runtimeChunk: {
name: 'manifest',
},
},
},
},
plugins: [
// 提取 CSS
new MiniCssExtractPlugin({
filename: 'css/[contenthash]-[name].css',
}),
// CSS Tree Shaking
// new PurgeCSSPlugin({ //添加该插件会样式错乱
// paths: glob.sync(`./src/**/*`, { nodir: true }),
// }),
// 进度条
new ProgressBarPlugin({
format: ` :msg [:bar] ${chalk.green.bold(
':percent'
)} (:elapsed s)`,
}),
new webpack.ProvidePlugin({
Vue: isPro ? 'lvm-base' : 'lvm-dev',
}),
// 处理<link rel="icon" href="<%= BASE_URL %>favicon.ico">
new webpack.DefinePlugin({
BASE_URL: '"./"',
}),
],
module: {
rules: [
{
// 字体文件等
test: /\.(woff|woff2|eot|ttf|otf)$/i,
// 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。即:所有匹配的文件都将被发送到输出目录,并且其路径将被注入到 bundle 中。
type: 'asset/resource',
exclude: /node_modules/,
parser: {
dataUrlCondition: {
// 超过20kb以文件形式引入,反之以base64形式引入
maxSize: 1024 * 20,
},
},
generator: {
//自定义输出目录
publicPath,
// 文件路径
filename: 'fonts/[name]-[contenthash:6].[ext]',
},
},
{
// 图片的转化
test: /\.(jpe?g|png|gif|bmp|svg)$/i,
type: 'asset',
include: [resolve('src')],
parser: {
dataUrlCondition: {
//自动地在 resource 和 inline 之间进行选择:小于 8kb 的文件,将会视为 inline 模块类型,否则会被视为 resource 模块类型。
maxSize: 1024 * 15,
},
},
generator: {
publicPath,
filename: 'img/[name]-[contenthash:6].[ext]',
},
},
],
},
},
pluginOptions: {
'style-resources-loader': {
preProcessor: 'less',
patterns: [resolve('./theme/default/var.less')],
},
},
css: {
sourceMap: false,
// extract: false, //是否使用css分离插件
// loaderOptions: { //向 CSS 相关的 loader 传递选项。例如:
// css: {
// 这里的选项会传递给 css-loader
// },
// postcss: {
// 这里的选项会传递给 postcss-loader
// }
// },
},
devServer: {
open: false, // 自动启动浏览器
hot: true, // 热更新
https: false,
// host: '0.0.0.0', // localhost
// port: 6060, // 端口号
// proxy: {
// // 在 Vue 组件中使用 axios 发送请求的时候: axios.get('/api/xxx')
// // 也就是你的请求地址需要以当前规则(比如:/api)开头,那么,本次请求才会被代理
// // 如果某一个接口不需要被代理,就可以直接: axios.get('http://localhost:8080/api/xxx')
// // 这样配置后,最终的得到的接口地址为: http://localhost:8080/api/xxx
// '/api': {
// target: 'http://120.53.31.103:84/api/app', // 接口的域名
// // ws: true, // 是否启用websockets
// changOrigin: true, // 开启代理,在本地创建一个虚拟服务端
// pathRewrite: {
// '^/api': '/',
// },
// },
// },
},
})