路由懒加载
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就会更加高效。
// 将 import UserDetails from './views/UserDetails.vue'
// 替换成 const UserDetails = () => import('./views/UserDetails.vue')
把组件按组分块
- 使用 webpack
有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用命名 chunk,一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4):
const UserDetails = () =>
import(/* webpackChunkName: "group-user" */ './UserDetails.vue')
const UserDashboard = () =>
import(/* webpackChunkName: "group-user" */ './UserDashboard.vue')
const UserProfileEdit = () =>
import(/* webpackChunkName: "group-user" */ './UserProfileEdit.vue')
webpack 会将任何一个异步模块与相同的块名称组合到相同的异步块中。
- 使用 vite
在Vite中,你可以在rollupOptions
下定义分块:
// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
// https://rollupjs.org/guide/en/#outputmanualchunks
output: {
manualChunks: {
'group-user': [
'./src/UserDetails',
'./src/UserDashboard',
'./src/UserProfileEdit',
],
},
},
},
},
})
资源打包目录
将打包的资源划分到不同的文件夹下
build: {
rollupOptions: {
output: {
chunkFileNames: 'js/[name]-[hash].js', // 引入文件名的名称
entryFileNames: 'js/[name]-[hash].js', // 包的入口文件名称
assetFileNames: '[ext]/[name]-[hash].[ext]' // 资源文件像 字体,图片等
}
}
}
产物分析报告
-
rollup-plugin-visualizer
插件- 可以生成可视化构建产物关系图页面
- 打包完成后会在根目录下生成一个
stats.html
文件,打开就是文件产物分析关系图
-
安装
npm i -D rollup-plugin-visualizer
import { defineConfig } from "vite";
import { visualizer } from "rollup-plugin-visualizer";
export default defineConfig({
plugins: [
visualizer({
// 打包完成后自动打开浏览器,显示产物体积报告
open: true,
}),
],
});
打包后的 stats.html
文件产物分析关系图
从上面可以很直观地观察到产物体积的分布情况,定位到某些体积过大的包,然后针对性地进行优化。
Gzip压缩
Gzip
压缩使用 vite-plugin-compression
插件, 压缩后减小代码体积,提升加载性能
安装: npm i -D vite-plugin-compression
vite-plugin-compression
常用配置项
可配置项名称 | 默认值 | 释义 |
---|---|---|
verbose | true | 是否在控制台中输出压缩结果 |
filter | RegExp or (file) => boolean | 指定未压缩的资源 |
disable | false | 是否禁用 |
threshold | 1024 | 如果体积大于阈值,则进行压缩,单位为b |
algorithm | gzip | 压缩算法,可选[‘gzip’,‘brotliCompress’,‘deflate’,‘deflateRaw’] |
ext | .gz | 生成的压缩包的后缀 |
compressionOptions | - | 对应压缩算法的参数 |
deleteOriginFile | - | 压缩后是否删除源文件 |
// build.rollupOptions.plugins[]
viteCompression({
algorithm: 'gzip',
threshold: 10240,
verbose: true, // 是否在控制台中输出压缩结果
ext: '.gz',
deleteOriginFile: true // 源文件压缩后是否删除
})
文件大小在阈值超过 10k
后使用 gzip
算法进行压缩
压缩 gz
后缀文件,浏览器正常解析,需要 配置 nginx http 请求,告诉浏览器支持的类型,设置响应头 content-encoding: gzip 。
//在nginx添加
http {
# 开启或者关闭gzip模块(on|off)
gzip_static on;
# gzip压缩比,1 压缩比最小处理速度最快,9 压缩比最大但处理最慢(传输快但比较消耗cpu)。
gzip_comp_level 2;
}
图片压缩
使用 unplugin-imagemin
进行图片压缩
// 图片压缩
import imagemin from 'unplugin-imagemin/vite';
plugins: [
imagemin(),
]
Treeshaking
Treeshaking:保证代码运行结果不变的前提下,去除无用的代码(修建无用的枝叶,也就是去掉没有引用的代码)
Vite 会默认使用 Rollup
进行 treeshaking ,不需要额外进行配置。但是有一些条件要注意
- 使用 ES Modules 语法(即 ES6 的 import 和 export 关键字)
- CommonJS 语法无法 tree-shaking(即 require 和 exports 语法)
- 引入的时候只引用需要的模块
- 要写 import {cloneDeep} from 'lodash-es' 因为方便 tree-shaking
- 不要写 import _ from 'lodash' 因为会导致无法 tree-shaking 无用模块
去除debugger 和 console
第一种方法,使用 esbuild(官方推荐)
项目上,使用的是这个方案
build: {
esbuild: {
drop: ['console', 'debugger']
}
}
第二种方法,使用 terser
插件
npm i -D terser
build: {
//移除生产环境log
minify: 'terser',
terserOptions: {
compress: {
//生产环境时移除console
drop_console: true,
drop_debugger: true,
},
}
}
CDN加速
通过配置 CDN
让用户从最近的服务器请求资源,提升网络请求的响应速度。
使用 vite-plugin-cdn-import
配置,以 lodash
为例
// vite.config.js
import { defineConfig } from 'vite'
import viteCDNPlugin from 'vite-plugin-cdn-import'
export default defineConfig({
plugins: [
viteCDNPlugin({
// 需要 CDN 加速的模块
modules: [
{
name: 'lodash',
var: '_',
path: `https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js`
}
]
})
]
})
lodash 在代码使用方式
import _ from 'lodash'
const obj = _.cloneDeep({})
构建成功后,Vite 会自动帮我们将 cdn 资源通过 script
标签插入到 html
中
开启HTTP2
浏览器有请求并发限制,一般是 6 个,超过限制请求需要排队,之前可以通过域名分发、资源合并来解决,而使用 HTTP2
协议后,其可以在一个TCP连接分帧处理多个请求(多路复用),不受此限制。(头部压缩也带来了一定性能提升)
在 Nginx 中开启 HTTP2
按需加载
这里以 ElementPlus
为例
如果我们项目中完整引入 ElementPlus
, 那打包的大小就会偏大
如果我们手动导入, 那么每次用的时候都要导入一下,也是很麻烦
官网 推荐我们使用按需导入
安装依赖:
npm install -D unplugin-vue-components unplugin-auto-import
// 组件库按需加载
import Components from 'unplugin-vue-components/vite';
// 组件库解析器
import {
ElementPlusResolver,
} from 'unplugin-vue-components/resolvers'
// 自动导入
import AutoImport from 'unplugin-auto-import/vite';
const pathSrc = path.resolve(__dirname, 'src')
export default defineConfig({
plugins: [
// 按需加载组件库 element-plus
Components({
dts: false, // 不生成 components.d
// ui库解析器,也可以自定义
resolvers: [
ElementPlusResolver(),
]
}),
AutoImport({
// 自动导入 Vue 相关函数,如:ref, reactive, toRef 等
imports: ['vue'],
// 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式)
resolvers: [
// 自动导入 Element Plus 组件
ElementPlusResolver(),
],
dts: path.resolve(pathSrc, 'components.d.ts'),
imports: [
'vue'
],
include: [
/\.[j]sx?$/, // .ts, .tsx, .js, .jsx
/\.vue$/, /\.vue\?vue/ // .vue
],
eslintrc: {
enabled: true, // 默认false, true启用。生成一次就可以,避免每次工程启动都生成
filepath: './.eslintrc-auto-import.json', // 生成json文件
globalsPropValue: true
},
}),
]
})
注: 如果运行报错: Cannot find module 'consola'
, 找不到consola
模块,那就下载一下, npm i -D consola
就好了
当你使用unplugin-vue-components
来引入ui库的时候, message
, notification
等引入样式不生效
安装vite-plugin-style-import
即可
import {
createStyleImportPlugin,
ElementPlusResolve,
} from 'vite-plugin-style-import'
export default defineConfig({
plugins: [
createStyleImportPlugin({
resolves: [
ElementPlusResolve(),
]
})
]
})
ElementPlus
的图标也支持按需引入
可参考这里
// 自动导入图标组件
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
export default defineConfig({
plugins: [
// 按需加载组件库 element-plus
Components({
// ui库解析器,也可以自定义
resolvers: [
// 自动注册图标组件
IconsResolver({
enabledCollections: ['ep'],
}),
]
}),
AutoImport({
// 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式)
resolvers: [
// 自动导入图标组件
IconsResolver({
prefix: 'Icon',
}),
],
}),
Icons({
autoInstall: true,
}),
]
})
按需导入图标后写法就跟原来不一样了
// 原来的写法
<el-icon><Lock /></el-icon>
// 按需导入后
<i-ep-lock></i-ep-lock>