打包优化
webpack 优化
1、依赖转化,兼容低版本浏览器
// 对依赖进行转换
transpileDependencies: true,
2、生产环境关闭sourceMap
// 生产关闭sourceMap
productionSourceMap: false,
3、打包输出目录名称修改和静态资源的存放
outputDir: 'bundle', // 打包后文件的目录 (默认为dist)
assetsDir: 'static', // outputDir的静态资源(js、css、img、fonts)目录 默认为‘’没有单独目录js/css/img在根目录中。
/*
下面这两个资源是配合使用的(要是 publicPath 是 "./" , 那么indexPath 是 'index.html' 就可以了)
要是 publicPath 是 '../' ,那么 indexPath 是 'templates/index.html'
*/
// 例如
publicPath: "./", // 打包之后的html中引入js、 css的相对路径 例如 <link href='./static/css/xxx.css' />
indexPath: 'index.html', // 生成的 index.html 文件
// 或者
publicPath: "../", // 打包之后的html中引入js、 css的相对路径 例如 <link href='../static/css/xxx.css' />
indexPath: 'templates/index.html', // 生成的 templates/index.html 文件
4、修改图标
// 修改浏览器的icon图标,不加下面的,修改浏览器图标不生效
pwa: {
iconPaths: {
favicon32: 'favicon.ico',
favicon16: 'favicon.ico',
appleTouchIcon: 'favicon.ico',
maskIcon: 'favicon.ico',
msTileImage: 'favicon.ico',
}
}
5、修改webpack配置
5-1、写在此处的配置可以覆盖掉脚手架本来就预设上有的配置 (chainWebpack 本身有的配置)
pnpm i compression-webpack-plugin
chainWebpack: config => {
config.optimization.minimizer('terser').tap(args => {
// 删除代码中的注释和打印,减少一点代码体积
args.forEach(item => {
if (item.hasOwnProperty('terserOptions')) {
Object.assign(item['terserOptions'].compress, {
drop_debugger: true,
drop_console: true,
pure_funcs: ['console.log']
})
}
item['terserOptions']['format'] = {
comments: false
}
})
return args
})
// 开启 gzip 压缩
if (process.env.NODE_ENV === "production") {
const CompressionWebpackPlugin = require("compression-webpack-plugin")
config.plugin('CompressionPlugin').use(
new CompressionWebpackPlugin({
test: /\.(js|css|less|scss|html)$/, // 将 css、scss、less、html 进行压缩
threshold: 10240, // 超过10kb的文件就压缩
deleteOriginalAssets: false, // 不删除源文件
minRatio: 0.8, // 最小压缩率 0.8
algorithm: 'gzip'
})
)
}
}
5-2、代码分割以外还可以利用 webpack 的 externals 将第三方的比较大的包拆出来使用 cdn 的方式引入
5-3、写在此处的都是预设没有配置的,脚手架本来就有的配置是不会覆盖的
// configureWebpack表示脚手架 vue-cli 本来没有的 webpack 的一些配置
configureWebpack: {
// 代码分割
optimization: {
splitChunks: {
chunks: "all",
// 定义一个cache组,将第三方的包抽离出来
cacheGroups: {
elementUI: {
// 抽离出来的名字
name: "element-chunk-vendors",
// 在node_modules包里面找
test: /[\\/]node_modules[\\/]_?element-ui(.*)/,
// 权重,越大优先打包
priority: 30,
},
vue: {
name: "vue-chunk-vendors",
test: /[\\/]node_modules[\\/]vue(.*)[\\/]/,
chunks: "initial",
priority: 20,
reuseExistingChunk: true,
},
vueRouter: {
name: "vueRouter-chunk-vendors",
test: /[\\/]node_modules[\\/]vue-router(.*)[\\/]/,
chunks: "initial",
priority: 19,
},
vuex: {
name: "vuex-chunk-vendors",
test: /[\\/]node_modules[\\/]vuex(.*)[\\/]/,
chunks: "initial",
priority: 18,
},
echarts: {
name: "echarts-chunk-vendors",
test: /[\\/]node_modules[\\/]echarts(.*)[\\/]/,
chunks: "initial",
priority: 17,
},
// 剩下的别忘记单独抽离
libs: {
name: "chunk-libs-vendors",
test: /[\\/]node_modules[\\/]/,
priority: 1, // 权重最低,优先考虑前面内容
chunks: "initial",
},
// 针对自己写的代码,重复使用的满足下面的配置就会抽离出来单独打包,比如 utils 下面的包
default: {
// 其他没有写的配置会使用上面的默认值
test: /[\\/]src(.*)[\\/]/,
name: "common-chunk",
minSize: 20000, // 超过 20kb,就会拆包
minChunks: 2, // 引用两次就会拆包
priority: -10,
reuseExistingChunk: true
}
}
}
},
// 配置别名
resolve: {
alias: {
"#": path.resolve(__dirname, "src")
}
},
// 分析插件
plugins: [
new BundleAnalyzer({
analyzerMode: 'server',
analyzerHost: '127.0.0.1',
analyzerPort: 8088,
reportFilename: 'report.html',
defaultSizes: 'parsed',
openAnalyzer: true,
generateStatsFile: false,
statsFilename: 'state.json',
statsOptions: null,
logLevel: 'info'
})
]
}
6、完整配置
const path = require("path")
const { defineConfig } = require('@vue/cli-service')
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const BundleAnalyzer = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = defineConfig({
// 对依赖进行转换
transpileDependencies: true,
// 生产关闭sourceMap
productionSourceMap: false,
outputDir: 'bundle', // 打包后文件的目录 (默认为dist)
assetsDir: 'static', // outputDir的静态资源(js、css、img、fonts)目录 默认为‘’没有单独目录js/css/img在根目录中。
// 修改浏览器的icon图标
pwa: {
iconPaths: {
favicon32: 'favicon.ico',
favicon16: 'favicon.ico',
appleTouchIcon: 'favicon.ico',
maskIcon: 'favicon.ico',
msTileImage: 'favicon.ico',
}
},
// webpack 配置(写在此处的配置可以覆盖掉脚手架本来就预设上有的配置)
chainWebpack: config => {
config.optimization.minimizer('terser').tap(args => {
// 删除代码中的注释和打印,减少一点代码体积
args.forEach(item => {
if (item.hasOwnProperty('terserOptions')) {
Object.assign(item['terserOptions'].compress, {
drop_debugger: true,
drop_console: true,
pure_funcs: ['console.log']
})
}
item['terserOptions']['format'] = {
comments: false
}
})
return args
})
// 开启 gzip 压缩,对应的 nginx 也需要配置
if (process.env.NODE_ENV === "production") {
config.plugin('CompressionPlugin').use(
new CompressionWebpackPlugin({
test: /\.(js|css|less|scss|html)$/, // 将 css、scss、less、html 进行压缩
threshold: 10240, // 超过10kb的文件就压缩
deleteOriginalAssets: false, // 不删除源文件
minRatio: 0.8, // 最小压缩率 0.8
algorithm: 'gzip'
})
)
}
},
// webpack 配置(写在此处的都是预设没有配置的,脚手架本来就有的配置是不会覆盖的)
configureWebpack: {
// 代码分割
optimization: {
splitChunks: {
chunks: "all",
// 定义一个cache组,将第三方的包抽离出来
cacheGroups: {
elementUI: {
// 抽离出来的名字
name: "element-chunk-vendors",
// 在node_modules包里面找
test: /[\\/]node_modules[\\/]_?element-ui(.*)/,
// 权重,越大优先打包
priority: 30,
},
vue: {
name: "vue-chunk-vendors",
test: /[\\/]node_modules[\\/]vue(.*)[\\/]/,
chunks: "initial",
priority: 20,
reuseExistingChunk: true,
},
vueRouter: {
name: "vueRouter-chunk-vendors",
test: /[\\/]node_modules[\\/]vue-router(.*)[\\/]/,
chunks: "initial",
priority: 19,
},
vuex: {
name: "vuex-chunk-vendors",
test: /[\\/]node_modules[\\/]vuex(.*)[\\/]/,
chunks: "initial",
priority: 18,
},
echarts: {
name: "echarts-chunk-vendors",
test: /[\\/]node_modules[\\/]echarts(.*)[\\/]/,
chunks: "initial",
priority: 17,
},
// 剩下的别忘记单独抽离
libs: {
name: "chunk-libs-vendors",
test: /[\\/]node_modules[\\/]/,
priority: 1, // 权重最低,优先考虑前面内容
chunks: "initial",
},
// 针对自己写的代码,重复使用的满足下面的配置就会抽离出来单独打包,比如 utils 下面的包
default: {
// 其他没有写的配置会使用上面的默认值
test: /[\\/]src(.*)[\\/]/,
name: "common-chunk",
minSize: 20000, // 超过 20kb,就会拆包
minChunks: 2, // 引用两次就会拆包
priority: -10,
reuseExistingChunk: true
}
}
}
},
// 配置别名
resolve: {
alias: {
"#": path.resolve(__dirname, "src")
}
},
plugins: [
new BundleAnalyzer({
analyzerMode: 'server',
analyzerHost: '127.0.0.1',
analyzerPort: 8088,
reportFilename: 'report.html',
defaultSizes: 'parsed',
openAnalyzer: true,
generateStatsFile: false,
statsFilename: 'state.json',
statsOptions: null,
logLevel: 'info'
})
]
}
})
// 打包分析工具加了之后 启动需要加上:
"build": "vue-cli-service build",
"build:analyze": "cross-env NODE_ENV=production npm_config_report=true vue-cli-service build"
https://juejin.cn/post/6951297954770583565
vite 优化
pnpm i unplugin-auto-import unplugin-vue-components vite-plugin-compression -D
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import viteCompression from "vite-plugin-compression";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
Components({//自定义的模块
dirs: ['src/components'],
extensions: ['vue', 'ts'],
resolvers: [ElementPlusResolver()]
}),
AutoImport({ // 插件进行自动导入相关的依赖库
//安装两行后你会发现在组件中不用再导入ref,reactive等
imports: ['vue', 'vue-router'],
// 可选,用于自动导入组件类型
dts: 'src/components.d.ts'
}),
// 开启gzip压缩
viteCompression({
verbose: true,
disable: false,
threshold: 10240,
algorithm: 'gzip',
ext: '.gz',
})
],
// 打包
build: {
// 打包删除 console 和 debugger
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
},
rollupOptions: {
output: { //静态资源分类打包
chunkFileNames: 'static/js/[name]-[hash].js',
entryFileNames: 'static/js/[name]-[hash].js',
assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
manualChunks: { // 相当于webpack中的 codeSplit
common: ["vue"],
elementPluse: ["element-plus"],
vueRouter: ["vue-router"],
pinia: ["pinia"],
echarts: ["echarts"],
}
}
}
}
});