前端工程化之vite
vite常用的插件有哪些?
-
@vitejs/plugin-vue:用于支持 Vue.js 单文件组件(.vue 文件)
-
@vitejs/plugin-react:用于支持 React 和 JSX 语法
-
rollup-plugin-visualizer: 用于打包分析
-
vite-plugin-restart: 文件修改时自动重启vite
-
vite-plugin-components: 组件按需自动导入
-
vite-plugin-svg-icons: 将 SVG 图标批量导入并转化为 Vue 组件
- vite-plugin-ssr:用于构建服务器端渲染(SSR)应用程序
vite配置项
{
root: process.cwd(), // 项目根目录(index.html 文件所在的位置),
base: '/', // 开发或生产环境服务的公共基础路径 配置引入相对路径
mode: 'development', // 模式
plugins: [vue()], // 需要用到的插件数组
publicDir: 'public', // 静态资源服务的文件夹
cacheDir: 'node_modules/.vite', // 存储缓存文件的目录
resolve: {
alias: [ // 文件系统路径别名
{
find: //@//,
replacement: pathResolve('src') + '/'
}
],
dedupe: [], // 强制 Vite 始终将列出的依赖项解析为同一副本
conditions: [], // 解决程序包中 情景导出 时的其他允许条件
mainFields: [], // 解析包入口点尝试的字段列表
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json'], // 导入时想要忽略的扩展名列表
preserveSymlinks: false, // 启用此选项会使 Vite 通过原始文件路径确定文件身份
},
css: {
modules: {
scopeBehaviour: 'global' | 'local',
// ...
},
postcss: '', // 内联的 PostCSS 配置 如果提供了该内联配置,Vite 将不会搜索其他 PostCSS 配置源
preprocessorOptions: { // css的预处理器选项
scss: {
additionalData: `$injectedColor: orange;`
}
}
},
json: {
namedExports: true, // 是否支持从.json文件中进行按名导入
stringify: false, // 开启此项,导入的 JSON 会被转换为 export default JSON.parse("...") 会禁用按名导入
},
esbuild: { // 最常见的用例是自定义 JSX
jsxFactory: 'h',
jsxFragment: 'Fragment'
},
assetsInclude: ['**/*.gltf'], // 指定额外的 picomatch 模式 作为静态资源处理
logLevel: 'info', // 调整控制台输出的级别 'info' | 'warn' | 'error' | 'silent'
clearScreen: true, // 设为 false 可以避免 Vite 清屏而错过在终端中打印某些关键信息
envDir: '/', // 用于加载 .env 文件的目录
envPrefix: [], // 以 envPrefix 开头的环境变量会通过 import.meta.env 暴露在你的客户端源码中
server: {
host: '127.0.0.1', // 指定服务器应该监听哪个 IP 地址
port: 5000, // 指定开发服务器端口
strictPort: true, // 若端口已被占用则会直接退出
https: false, // 启用 TLS + HTTP/2
open: true, // 启动时自动在浏览器中打开应用程序
proxy: { // 配置自定义代理规则
'/api': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^/api/, '')
}
},
cors: true, // 配置 CORS
force: true, // 强制使依赖预构建
hmr: { // 禁用或配置 HMR 连接
// ...
},
watch: { // 传递给 chokidar 的文件系统监听器选项
// ...
},
middlewareMode: '', // 以中间件模式创建 Vite 服务器
fs: {
strict: true, // 限制为工作区 root 路径以外的文件的访问
allow: [], // 限制哪些文件可以通过 /@fs/ 路径提供服务
deny: ['.env', '.env.*', '*.{pem,crt}'], // 用于限制 Vite 开发服务器提供敏感文件的黑名单
},
origin: 'http://127.0.0.1:8080/', // 用于定义开发调试阶段生成资产的 origin
},
build: {
target: ['modules'], // 设置最终构建的浏览器兼容目标
polyfillModulePreload: true, // 是否自动注入 module preload 的 polyfill
outDir: 'dist', // 指定输出路径
assetsDir: 'assets', // 指定生成静态文件目录
assetsInlineLimit: '4096', // 小于此阈值的导入或引用资源将内联为 base64 编码
cssCodeSplit: true, // 启用 CSS 代码拆分
cssTarget: '', // 允许用户为 CSS 的压缩设置一个不同的浏览器 target 与 build.target 一致
sourcemap: false, // 构建后是否生成 source map 文件
lib: {}, // 构建为库
manifest: false, // 当设置为 true,构建后将会生成 manifest.json 文件
ssrManifest: false, // 构建不生成 SSR 的 manifest 文件
ssr: undefined, // 生成面向 SSR 的构建
write: true, // 启用将构建后的文件写入磁盘
emptyOutDir: true, // 构建时清空该目录
brotliSize: true, // 启用 brotli 压缩大小报告
chunkSizeWarningLimit: 500, // chunk 大小警告的限制
watch: null, // 设置为 {} 则会启用 rollup 的监听器
// minify:false, // 表示打包后的文件内容不进行压缩,方便阅读
minify: "terser", // esbuild | terser 指定使用哪种混淆器
terserOptions: {
compress: {
// 打包的时候可以移除console和debugger
drop_console: true,
drop_debugger: true,
},
}, // 传递给 minify: "terser" 的更多 minify 选项
preview: {
port: 5000, // 指定开发服务器端口
strictPort: true, // 若端口已被占用则会直接退出
https: false, // 启用 TLS + HTTP/2
open: true, // 启动时自动在浏览器中打开应用程序
proxy: { // 配置自定义代理规则
'/api': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^/api/, '')
}
},
cors: true, // 配置 CORS
},
optimizeDeps: {
entries: [], // 指定自定义条目——该值需要遵循 fast-glob 模式
exclude: [], // 在预构建中强制排除的依赖项
include: [], // 可强制预构建链接的包
keepNames: false, // true 可以在函数和类上保留 name 属性
},
ssr: {
external: [], // 列出的是要为 SSR 强制外部化的依赖,
noExternal: '', // 列出的是防止被 SSR 外部化依赖项
target: 'node', // SSR 服务器的构建目标
},
rollupOptions: { // 自定义底层的 Rollup 打包配置
output: {
chunkFileNames: 'js/[name]-[hash].js', // 打包后的文件名称
entryFileNames: 'js/[name]-[hash].js', // 打包后的入口文件名称
// assetFileNames: '[ext]/[name]-[hash].[ext]', // 资源文件像 字体,图片等 指定静态资源文件名(不含导出的代码)
// assetFileNames:"[name][extname]",
// 对打包出来的资源文件进行分类,分别放到不同的文件夹内
// assetFileNames(chunk) {
// console.log(chunk.name);
// // css
// if (chunk.name?.endsWith('.css')) {
// return 'css/[name]-[hash].[ext]';
// }
// // image
// if (/.(png|jpg|gif|jpeg|webp|svg)$/.test(chunk.name || '')) {
// return 'images/[name]-[hash].[ext]';
// }
// return `other/[name]-[hash].[ext]`;
// },
// 对打包出来的资源文件进行分类,分别放到不同的文件夹内
assetFileNames(assetsInfo) {
// css样式文件
if (assetsInfo.name?.endsWith(".css")) {
return "css/[name]-[hash].css";
}
// 字体文件
const fontExts = [".ttf", ".otf", ".woff", ".woff2", ".eot"];
if (fontExts.some((ext) => assetsInfo.name?.endsWith(ext))) {
return "font/[name]-[hash].[ext]";
}
// 图片文件
const imgExts = [".png", ".jpg", ".jpeg", ".webp", ".gif", ".icon"];
if (imgExts.some((ext) => assetsInfo.name?.endsWith(ext))) {
return "img/[name]-[hash].[ext]";
}
// SVG类型的图片文件
const imgSvg = [".svg",];
if (imgSvg.some((ext) => assetsInfo.name?.endsWith(ext))) {
return "assest/icons/[name].[ext]";
}
// 视频文件
const videoExts = [".mp4", ".avi", ".wmv", ".ram", ".mpg", "mpeg"];
if (videoExts.some((ext) => assetsInfo.name?.endsWith(ext))) {
return "video/[name]-[hash].[ext]";
}
// 其它文件: 保存在 assets/图片名-哈希值.扩展名
return "assets/[name]-[hash].[ext]";
},
// 打包的文件进行拆包处理/静态资源分拆打包
manualChunks:(id)=>{
// 这个ID,就是所有文件的绝对路径
if(id.includes("node_modules")){
// 因为 node_modules 中的依赖通常是不会改变的
// 所以直接单独打包出去
// 这个return 的值就是打包的名称
return "vendor";
}
},
// 或者
manualChunks(id) { //静态资源分拆打包
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString();
}
}
},
}
为什么vite比webpack快
开发构建模式不同:Vite 使用原生 ES 模块(ESM),在开发过程中仅处理应用所需的代码,而不是对整个应用进行打包。这样,Vite 可以提供近乎即时的热模块替换(HMR),因为它只重新加载修改过的模块,而不是重建整个应用;Webpack 在开发过程中通常需要对整个应用进行打包,然后通过内存中的 Bundle 提供服务
Webpack的热更新是全量更新,即使修改一个小文件,也会重新编译整个应用,这在大型应用中可能会导致编译速度变慢。 Vite的热更新是增量更新,只更新修改的文件,所以即使在大型应用中也能保持极快的编译速度
模块解析方式不同:Vite使用浏览器原生的 ES 模块 ,浏览器会根据导入的模块需求,仅请求更新的模块;Webpack 需要在开发服务器启动时分析所有的模块并构建完整的模块图,启动速度慢
打包优化手段
js最小化拆分包
//vite.config中配置
output: {
// 最小化拆分包
manualChunks(id) {
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString();
}
}
},
代码压缩
//在vite.config中配置
import {defineConfig} from "vite";
export default defineConfig({
esbuild:{
pure: ['console.log'], // 删除 console.log
drop: ['debugger'], // 删除 debugger
}
})
文件压缩
//npm i vite-plugin-compression -D
viteCompression({
verbose: true, // 是否在控制台中输出压缩结果
disable: false,
threshold: 10240, // 如果体积大于阈值,将被压缩,单位为b,体积过小时请不要压缩,以免适得其反
algorithm: 'gzip', // 压缩算法,可选['gzip',' brotliccompress ','deflate ','deflateRaw']
ext: '.gz',
deleteOriginFile: true // 源文件压缩后是否删除
})
图片压缩
import viteImagemin from 'vite-plugin-imagemin'
plugin: [
viteImagemin({
gifsicle: {
optimizationLevel: 7,
interlaced: false
},
optipng: {
optimizationLevel: 7
},
mozjpeg: {
quality: 20
},
pngquant: {
quality: [0.8, 0.9],
speed: 4
},
svgo: {
plugins: [
{
name: 'removeViewBox'
},
{
name: 'removeEmptyAttrs',
active: false
}
]
}
})
]
按需加载第三方包
import _ from 'lodash-es'; // 将会把整个lodash的库引入到项目
import { cloneDeep } from 'lodash-es'; // 将只会把cloneDeep引入到项目
使用CDN加速
import importToCDN, { autoComplete } from "vite-plugin-cdn-import";
plugins: [
importToCDN({
modules: [
{
name: "vue",
var: "Vue",
path: "https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.31/vue.global.prod.min.js",
},
{
name: "vuex",
var: "Vuex",
path: "https://cdnjs.cloudflare.com/ajax/libs/vuex/4.0.2/vuex.global.prod.min.js",
},
{
name: "vue-router",
var: "VueRouter",
path: "https://cdnjs.cloudflare.com/ajax/libs/vue-router/4.0.12/vue-router.global.prod.min.js",
},
{
// 引入cdn element-plus
name: "element-plus",
var: "ElementPlus",
path: "https://unpkg.com/element-plus",
css: "https://unpkg.com/element-plus/dist/index.css",
},
],
}),
],