Vite 面试题

Vite 面试题

Vite 是新一代前端构建工具,基于原生 ES Module,以极快的启动和热更新速度著称。以下涵盖核心原理、配置、插件开发等高频面试题。


1. Vite 是什么?为什么比 Webpack 快?

Vite 是由尤雨溪开发的下一代前端构建工具,它利用浏览器原生 ES Module 支持和 esbuild 预构建,实现了极快的开发服务器启动和热更新。

arduino 复制代码
Webpack 的开发流程:
  入口 → 解析依赖 → 编译所有模块 → 打包 Bundle → 启动 Dev Server
  (全部处理完才能启动,项目越大越慢)

Vite 的开发流程:
  启动 Dev Server(极快)→ 浏览器请求模块 → 按需编译返回
  (不需要打包,请求什么编译什么)

Vite vs Webpack 速度对比:

指标 Webpack Vite
冷启动 30s-60s(大项目) ~300ms
热更新(HMR) 1-5s ~50ms
依赖预构建 每次全量 esbuild 预构建(Go 编写,极快)
生产构建 webpack + terser Rollup + esbuild

Vite 快的核心原因:

javascript 复制代码
// 1. 开发环境不打包,利用浏览器原生 ES Module
// 浏览器直接请求源文件,Vite 只做按需编译和转换

// 浏览器发起请求:
// GET /src/main.tsx
// GET /src/App.tsx
// GET /src/components/Header.tsx

// Vite Dev Server 拦截请求,编译后返回:
// main.tsx → 编译 TSX → 返回 JS(保留 import 语句)
// App.tsx → 编译 TSX → 返回 JS
// 浏览器继续按 import 请求其他模块...

// 2. 依赖预构建:用 esbuild 将 node_modules 的 CommonJS/UMD 转为 ESM
// esbuild 用 Go 编写,比 JavaScript 写的打包器快 10-100 倍

// 3. HMR 基于 ESM:只需要精确地更新变化的模块
// 不像 Webpack 需要重新构建整个 chunk

💡 面试加分点: Vite 开发环境用原生 ESM + esbuild,生产环境用 Rollup 打包。原因是浏览器在生产环境加载过多 ESM 文件会有性能问题(HTTP 请求瀑布流),所以仍需要打包。


2. Vite 的核心原理?

xml 复制代码
Vite 的三个核心机制:

1. 依赖预构建(Pre-bundling)
   ├── 首次启动时,用 esbuild 扫描并预构建 node_modules 依赖
   ├── 将 CommonJS/UMD 转为 ESM 格式
   ├── 将零散的模块合并(如 lodash-es 有 600+ 个文件 → 合并为 1 个)
   └── 结果缓存到 node_modules/.vite 目录

2. 基于 ESM 的开发服务器
   ├── 浏览器通过 <script type="module"> 加载入口
   ├── 遇到 import 语句,浏览器发起 HTTP 请求
   ├── Vite Dev Server 拦截请求,按需编译
   ├── 裸模块导入重写:import 'vue' → import '/node_modules/.vite/deps/vue.js'
   └── .vue/.tsx/.less 等文件实时编译转换

3. 基于 Rollup 的生产构建
   ├── 使用 Rollup 打包(更好的 Tree Shaking)
   ├── 用 esbuild 做代码压缩(替代 terser,快 20-40 倍)
   └── 自动处理代码分割、CSS 提取等
javascript 复制代码
// 依赖预构建过程示意
// 1. Vite 启动时扫描源码中的 import
import { createApp } from 'vue'        // 裸模块导入
import axios from 'axios'               // CommonJS 包
import { debounce } from 'lodash-es'    // 有很多子模块的包

// 2. esbuild 预构建
// vue → 预构建为 ESM,缓存到 node_modules/.vite/deps/vue.js
// axios → 从 CommonJS 转为 ESM
// lodash-es → 600+ 文件合并为 1 个文件(减少 HTTP 请求)

// 3. 重写导入路径
import { createApp } from '/node_modules/.vite/deps/vue.js?v=abc123'
import axios from '/node_modules/.vite/deps/axios.js?v=abc123'
import { debounce } from '/node_modules/.vite/deps/lodash-es.js?v=abc123'

// 强缓存:依赖文件使用 max-age=31536000 强缓存
// 版本号变化时自动更新缓存

3. vite.config.ts 常用配置?

typescript 复制代码
// vite.config.ts
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import react from '@vitejs/plugin-react'
import path from 'path'

export default defineConfig(({ command, mode }) => {
  // 加载环境变量
  const env = loadEnv(mode, process.cwd(), '')

  return {
    // ===== 基础配置 =====
    base: '/',                    // 公共基础路径(CDN 部署时修改)
    root: process.cwd(),          // 项目根目录
    publicDir: 'public',          // 静态资源目录

    // ===== 插件 =====
    plugins: [
      vue(),                      // Vue 支持
      // react(),                 // React 支持
    ],

    // ===== 路径别名 =====
    resolve: {
      alias: {
        '@': path.resolve(__dirname, 'src'),
        '@components': path.resolve(__dirname, 'src/components'),
        '@utils': path.resolve(__dirname, 'src/utils')
      },
      extensions: ['.ts', '.tsx', '.js', '.jsx', '.json']
    },

    // ===== CSS 配置 =====
    css: {
      // CSS Modules
      modules: {
        localsConvention: 'camelCaseOnly'
      },
      // 预处理器选项
      preprocessorOptions: {
        scss: {
          additionalData: `@import "@/styles/variables.scss";`
        },
        less: {
          javascriptEnabled: true,
          modifyVars: { '@primary-color': '#1890ff' }
        }
      },
      // PostCSS
      postcss: {
        plugins: [
          require('autoprefixer')(),
          require('tailwindcss')()
        ]
      }
    },

    // ===== 开发服务器 =====
    server: {
      host: '0.0.0.0',
      port: 3000,
      open: true,
      cors: true,
      // 代理配置
      proxy: {
        '/api': {
          target: 'http://localhost:8080',
          changeOrigin: true,
          rewrite: (path) => path.replace(/^\/api/, '')
        }
      }
    },

    // ===== 构建配置 =====
    build: {
      target: 'es2020',           // 构建目标
      outDir: 'dist',             // 输出目录
      assetsDir: 'assets',        // 静态资源目录
      assetsInlineLimit: 4096,    // 小于 4KB 的资源内联为 base64
      sourcemap: false,           // 生产环境不生成 sourcemap
      minify: 'esbuild',         // 压缩方式:'esbuild' | 'terser' | false
      cssMinify: true,

      // Rollup 配置
      rollupOptions: {
        output: {
          // 代码分割
          manualChunks: {
            'react-vendor': ['react', 'react-dom'],
            'vue-vendor': ['vue', 'vue-router', 'pinia'],
            'lodash': ['lodash-es']
          },
          // 文件命名
          chunkFileNames: 'js/[name]-[hash].js',
          entryFileNames: 'js/[name]-[hash].js',
          assetFileNames: '[ext]/[name]-[hash].[ext]'
        }
      },

      // chunk 大小警告阈值
      chunkSizeWarningLimit: 500
    },

    // ===== 依赖预构建 =====
    optimizeDeps: {
      include: ['axios', 'dayjs'],    // 强制预构建
      exclude: ['@vueuse/core'],       // 排除预构建
      esbuildOptions: {
        target: 'es2020'
      }
    },

    // ===== 环境变量 =====
    // .env 文件中 VITE_ 开头的变量会暴露给客户端
    define: {
      __APP_VERSION__: JSON.stringify('1.0.0')
    }
  }
})

4. Vite 的 HMR(热更新)原理?

markdown 复制代码
Vite HMR 流程:

1. 文件变化 → Vite 监听文件系统(chokidar)
2. 找到变化文件对应的模块及其依赖关系
3. 通过 WebSocket 通知浏览器
4. 浏览器重新请求变化的模块(带时间戳参数避免缓存)
5. 替换旧模块,触发更新回调
typescript 复制代码
// Vite HMR API
if (import.meta.hot) {
  // 1. 接受自身更新
  import.meta.hot.accept((newModule) => {
    // newModule 是更新后的模块
    console.log('模块已更新', newModule)
  })

  // 2. 接受依赖更新
  import.meta.hot.accept('./module.ts', (newModule) => {
    // 当 ./module.ts 更新时触发
  })

  // 3. 清理副作用
  import.meta.hot.dispose((data) => {
    // 模块被替换前执行
    clearInterval(timer)
    data.savedState = currentState  // 传递状态给新模块
  })

  // 4. 精确失效(强制刷新)
  import.meta.hot.invalidate()

  // 5. 自定义事件
  import.meta.hot.on('custom-event', (data) => {
    console.log('收到自定义事件', data)
  })
}

// Vue/React 框架的 HMR 是自动的:
// - Vue:@vitejs/plugin-vue 自动处理组件 HMR
// - React:@vitejs/plugin-react 使用 React Fast Refresh
// 开发者通常不需要手动写 HMR 代码

Vite HMR vs Webpack HMR:

对比 Webpack HMR Vite HMR
更新粒度 整个 chunk 重新编译 只编译变化的文件
速度 与项目大小成正比(越大越慢) 恒定快速(与项目大小无关)
实现方式 重新打包 → 推送 ESM 重新请求变化的模块
CSS 更新 style-loader 注入 直接替换 <style> 标签

5. Vite 的环境变量和模式?

bash 复制代码
# .env                  # 所有模式共享
VITE_APP_TITLE=我的应用

# .env.development      # 开发模式
VITE_API_BASE=http://localhost:8080
VITE_DEBUG=true

# .env.production       # 生产模式
VITE_API_BASE=https://api.example.com
VITE_DEBUG=false

# .env.staging          # 自定义模式
VITE_API_BASE=https://staging-api.example.com
typescript 复制代码
// 使用环境变量(只有 VITE_ 开头的才会暴露给客户端)
console.log(import.meta.env.VITE_API_BASE)    // ✅
console.log(import.meta.env.VITE_APP_TITLE)   // ✅
console.log(import.meta.env.MODE)              // 'development' | 'production'
console.log(import.meta.env.DEV)               // true(开发模式)
console.log(import.meta.env.PROD)              // true(生产模式)
console.log(import.meta.env.BASE_URL)          // base 配置值

// TypeScript 类型声明
// env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
  readonly VITE_API_BASE: string
  readonly VITE_APP_TITLE: string
  readonly VITE_DEBUG: string
}
interface ImportMeta {
  readonly env: ImportMetaEnv
}

// 命令行指定模式
// vite --mode staging        → 加载 .env.staging
// vite build --mode staging  → 生产构建使用 staging 环境变量

// 在 vite.config.ts 中使用环境变量
import { defineConfig, loadEnv } from 'vite'

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), '')
  return {
    define: {
      __API_BASE__: JSON.stringify(env.VITE_API_BASE)
    }
  }
})

6. Vite 插件机制和常用插件?

typescript 复制代码
// Vite 插件兼容 Rollup 插件接口,同时扩展了 Vite 特有的钩子

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import react from '@vitejs/plugin-react'
import legacy from '@vitejs/plugin-legacy'
import { visualizer } from 'rollup-plugin-visualizer'
import { compression } from 'vite-plugin-compression2'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import Icons from 'unplugin-icons/vite'
import Inspect from 'vite-plugin-inspect'

export default defineConfig({
  plugins: [
    // 1. 框架支持
    vue(),
    vueJsx(),
    // react(),

    // 2. 浏览器兼容(生成传统浏览器 polyfill)
    legacy({
      targets: ['> 1%', 'last 2 versions', 'not dead'],
      additionalLegacyPolyfills: ['regenerator-runtime/runtime']
    }),

    // 3. 自动导入 API(不需要手动 import ref, computed 等)
    AutoImport({
      imports: ['vue', 'vue-router', 'pinia'],
      resolvers: [ElementPlusResolver()],
      dts: 'src/auto-imports.d.ts'
    }),

    // 4. 组件自动注册
    Components({
      resolvers: [ElementPlusResolver()],
      dts: 'src/components.d.ts'
    }),

    // 5. 图标按需加载
    Icons({ autoInstall: true }),

    // 6. Gzip 压缩
    compression({ algorithm: 'gzip', threshold: 10240 }),

    // 7. 包体积分析
    visualizer({
      open: true,
      filename: 'stats.html',
      gzipSize: true
    }),

    // 8. 调试工具(查看插件转换中间结果)
    Inspect()
  ]
})

7. 如何手写一个 Vite 插件?

typescript 复制代码
// Vite 插件是一个返回对象的函数,对象包含插件名和各种钩子

import type { Plugin, ResolvedConfig } from 'vite'

// ========== 示例1:自动添加版本号 ==========
function vitePluginVersion(version: string): Plugin {
  return {
    name: 'vite-plugin-version',  // 必须:插件名称
    enforce: 'pre',               // 可选:'pre' | 'post'(执行顺序)

    // 解析配置后调用
    configResolved(config) {
      console.log(`当前模式: ${config.mode}`)
    },

    // 转换 HTML
    transformIndexHtml(html) {
      return html.replace(
        '</head>',
        `<meta name="version" content="${version}">\n</head>`
      )
    }
  }
}

// ========== 示例2:虚拟模块 ==========
function vitePluginVirtualModule(): Plugin {
  const virtualModuleId = 'virtual:my-module'
  const resolvedVirtualModuleId = '\0' + virtualModuleId

  return {
    name: 'vite-plugin-virtual-module',

    resolveId(id) {
      if (id === virtualModuleId) {
        return resolvedVirtualModuleId
      }
    },

    load(id) {
      if (id === resolvedVirtualModuleId) {
        return `
          export const buildTime = '${new Date().toISOString()}'
          export const env = '${process.env.NODE_ENV}'
        `
      }
    }
  }
}

// 使用虚拟模块
// import { buildTime, env } from 'virtual:my-module'

// ========== 示例3:自动生成路由 ==========
function vitePluginAutoRoutes(): Plugin {
  return {
    name: 'vite-plugin-auto-routes',

    // 开发服务器配置钩子
    configureServer(server) {
      // 监听文件变化
      server.watcher.on('add', (filePath) => {
        if (filePath.includes('src/pages/') && filePath.endsWith('.vue')) {
          console.log('新页面文件:', filePath)
          // 触发 HMR 更新
          const mod = server.moduleGraph.getModulesByFile(filePath)
          if (mod) {
            server.ws.send({ type: 'full-reload' })
          }
        }
      })
    },

    // 代码转换钩子
    transform(code, id) {
      if (id.endsWith('.vue')) {
        // 可以在这里修改 Vue SFC 的代码
        return code
      }
    }
  }
}

// ========== 示例4:API Mock 插件 ==========
function vitePluginMock(): Plugin {
  return {
    name: 'vite-plugin-mock',
    apply: 'serve',  // 只在开发环境生效

    configureServer(server) {
      server.middlewares.use('/api/users', (req, res) => {
        res.setHeader('Content-Type', 'application/json')
        res.end(JSON.stringify([
          { id: 1, name: '张三' },
          { id: 2, name: '李四' }
        ]))
      })
    }
  }
}

// 使用插件
export default defineConfig({
  plugins: [
    vitePluginVersion('1.0.0'),
    vitePluginVirtualModule(),
    vitePluginAutoRoutes(),
    vitePluginMock()
  ]
})

Vite 插件钩子执行顺序:

lua 复制代码
config → configResolved → configureServer → buildStart
→ resolveId → load → transform → buildEnd → closeBundle

💡 面试加分点: Vite 插件和 Rollup 插件的区别------Vite 额外提供了 configconfigResolvedconfigureServertransformIndexHtmlhandleHotUpdate 等钩子。enforce: 'pre' 的插件先执行,enforce: 'post' 的后执行。


8. Vite 的 CSS 处理方式?

typescript 复制代码
// Vite 内置了对 CSS 的处理,无需额外配置

// ========== 1. 原生 CSS 导入 ==========
import './styles.css'  // 自动注入到页面

// ========== 2. CSS Modules ==========
// 文件名以 .module.css 结尾即可
import styles from './Button.module.css'
// styles.button → 生成唯一类名如 _button_1a2b3_1

// ========== 3. CSS 预处理器(自动支持,只需安装依赖)==========
// npm install -D sass
import './styles.scss'

// npm install -D less
import './styles.less'

// npm install -D stylus
import './styles.styl'

// ========== 4. PostCSS ==========
// 自动读取 postcss.config.js
// postcss.config.js
module.exports = {
  plugins: [
    require('autoprefixer'),
    require('tailwindcss'),
    require('postcss-nesting')
  ]
}

// ========== 5. CSS 代码分割 ==========
// 异步导入的模块中的 CSS 会自动生成独立的 CSS chunk
const LazyComponent = () => import('./LazyComponent.vue')
// LazyComponent 的 CSS 会被单独提取

// ========== 6. Tailwind CSS 集成 ==========
// vite.config.ts
export default defineConfig({
  css: {
    postcss: {
      plugins: [
        require('tailwindcss'),
        require('autoprefixer')
      ]
    }
  }
})

// ========== 7. CSS-in-JS ==========
// Vite 原生支持 @emotion/react、styled-components 等
// 配合 @vitejs/plugin-react 的 babel 配置

9. Vite 的 SSR(服务端渲染)支持?

typescript 复制代码
// Vite 内置了 SSR 支持,提供了 ssrLoadModule 等 API

// server.js(Node.js 服务端)
import express from 'express'
import { createServer as createViteServer } from 'vite'

async function createServer() {
  const app = express()

  // 创建 Vite 开发服务器
  const vite = await createViteServer({
    server: { middlewareMode: true },
    appType: 'custom'
  })
  app.use(vite.middlewares)

  app.get('*', async (req, res) => {
    const url = req.originalUrl

    try {
      // 1. 读取 HTML 模板
      let template = await vite.transformIndexHtml(url,
        `<!DOCTYPE html>
         <html>
           <head><title>SSR</title></head>
           <body>
             <div id="app"><!--ssr-outlet--></div>
             <script type="module" src="/src/entry-client.ts"></script>
           </body>
         </html>`)

      // 2. 加载服务端入口模块
      const { render } = await vite.ssrLoadModule('/src/entry-server.ts')

      // 3. 渲染应用,获取 HTML
      const appHtml = await render(url)

      // 4. 替换占位符
      const html = template.replace('<!--ssr-outlet-->', appHtml)

      res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
    } catch (e) {
      vite.ssrFixStacktrace(e as Error)
      res.status(500).end((e as Error).message)
    }
  })

  app.listen(3000, () => console.log('http://localhost:3000'))
}
createServer()

// entry-server.ts(服务端入口)
import { createApp } from './app'
import { renderToString } from 'vue/server-renderer'

export async function render(url: string) {
  const { app, router } = createApp()
  await router.push(url)
  await router.isReady()
  return await renderToString(app)
}

// entry-client.ts(客户端入口)
import { createApp } from './app'
const { app, router } = createApp()
router.isReady().then(() => app.mount('#app'))

💡 面试加分点: 实际项目中更推荐使用 Nuxt3(Vue)或 Next.js(React)等 SSR 框架,它们基于 Vite/Webpack 封装了完整的 SSR 能力。Vite 的 SSR 支持更偏底层。


10. Vite vs Webpack 全面对比?

对比维度 Vite Webpack
开发启动速度 毫秒级(不打包) 秒级-分钟级(需打包)
HMR 速度 ~50ms(精确更新) 秒级(chunk 重编译)
生产构建 Rollup Webpack
配置复杂度 简单(约定优于配置) 复杂(需要大量配置)
生态系统 较新但快速增长 最成熟、最丰富
插件 Rollup 兼容 + Vite 扩展 独立的 Plugin 体系
CSS 处理 内置 需要 loader 配置
TypeScript 内置(esbuild 编译) 需要 ts-loader/babel
JSON 导入 内置 需要配置
静态资源 内置 需要 loader 配置
Module Federation 需要社区插件 原生支持
代码分割 Rollup 自动分割 SplitChunksPlugin
旧浏览器兼容 @vitejs/plugin-legacy babel-loader
适用场景 新项目(强烈推荐) 老项目、复杂定制需求
javascript 复制代码
// Vite 开箱即用 vs Webpack 需要配置

// ===== Vite 零配置即可启动 =====
// npm create vite@latest my-app -- --template vue-ts
// cd my-app && npm install && npm run dev
// 就这样!不需要任何额外配置

// ===== Webpack 需要手动配置 =====
// 需要安装和配置:
// webpack, webpack-cli, webpack-dev-server,
// babel-loader, @babel/core, @babel/preset-env,
// css-loader, style-loader, html-webpack-plugin,
// ts-loader 或 @babel/preset-typescript...

11. Vite 的依赖预构建机制详解?

typescript 复制代码
// 预构建的三个目的:
// 1. CommonJS/UMD → ESM(浏览器只能加载 ESM)
// 2. 合并小模块(减少 HTTP 请求)
// 3. 缓存(加速后续启动)

// vite.config.ts 预构建配置
export default defineConfig({
  optimizeDeps: {
    // 强制预构建这些依赖(即使 Vite 没有自动发现)
    include: [
      'axios',
      'dayjs',
      'lodash-es',
      // 深层导入也可以
      'vue > @vue/compiler-sfc'
    ],

    // 排除预构建(如已经是 ESM 的包)
    exclude: ['@vueuse/core'],

    // esbuild 配置
    esbuildOptions: {
      target: 'es2020',
      plugins: [/* 自定义 esbuild 插件 */]
    }
  }
})

// 预构建缓存位置:node_modules/.vite/deps/
// 缓存失效条件:
// 1. package.json 的 dependencies 变化
// 2. lockfile(package-lock.json 等)变化
// 3. vite.config.ts 中相关配置变化

// 手动清除缓存:
// npx vite --force      // 强制重新预构建
// 或删除 node_modules/.vite 目录

12. Vite 的多页面应用(MPA)配置?

typescript 复制代码
// vite.config.ts
import { defineConfig } from 'vite'
import { resolve } from 'path'

export default defineConfig({
  build: {
    rollupOptions: {
      input: {
        // 多个入口
        main: resolve(__dirname, 'index.html'),
        admin: resolve(__dirname, 'admin/index.html'),
        login: resolve(__dirname, 'login/index.html')
      }
    }
  }
})

// 项目结构:
// ├── index.html           → /
// ├── admin/
// │   └── index.html       → /admin/
// ├── login/
// │   └── index.html       → /login/
// └── src/
//     ├── main/
//     │   └── main.ts
//     ├── admin/
//     │   └── main.ts
//     └── login/
//         └── main.ts

13. Vite 如何处理静态资源?

typescript 复制代码
// ========== 1. 导入资源获取 URL ==========
import imgUrl from './img.png'       // 获取解析后的 URL
document.getElementById('hero-img').src = imgUrl

// ========== 2. 导入为字符串 ==========
import text from './shader.glsl?raw'  // 获取文件原始内容

// ========== 3. 导入为 Worker ==========
import Worker from './worker?worker'
const worker = new Worker()

// ========== 4. 特殊后缀 ==========
import svgUrl from './icon.svg?url'        // 强制作为 URL
import svgRaw from './icon.svg?raw'        // 强制作为字符串
import svgComponent from './icon.svg?component'  // 作为 Vue 组件(需插件)

// ========== 5. public 目录 ==========
// public/ 下的文件不会被处理,直接复制到 dist 根目录
// 引用方式:使用绝对路径
// <img src="/favicon.ico" />

// ========== 6. 动态导入 ==========
// Glob 导入(批量导入)
const modules = import.meta.glob('./modules/*.ts')
// 等同于:
// {
//   './modules/a.ts': () => import('./modules/a.ts'),
//   './modules/b.ts': () => import('./modules/b.ts'),
// }

// 即时加载(非懒加载)
const modules = import.meta.glob('./modules/*.ts', { eager: true })

// 导入为字符串
const htmls = import.meta.glob('./*.html', { query: '?raw', import: 'default' })

// ========== 7. JSON 导入 ==========
import json from './data.json'               // 整个 JSON
import { name, version } from './package.json'  // 具名导入(Tree Shaking)

💡 面试加分点: import.meta.glob 是 Vite 独有的功能,它在构建时会被转换为动态 import() 调用,非常适合用于自动加载路由、Store 模块等场景。


14. Vite 在大型项目中的最佳实践?

typescript 复制代码
// ========== 1. 合理的代码分割策略 ==========
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks(id) {
          // 将 node_modules 中的大型依赖单独拆分
          if (id.includes('node_modules')) {
            if (id.includes('vue') || id.includes('@vue')) return 'vue-vendor'
            if (id.includes('element-plus')) return 'element-plus'
            if (id.includes('echarts')) return 'echarts'
            if (id.includes('lodash')) return 'lodash'
            return 'vendor'  // 其他第三方库
          }
        }
      }
    }
  }
})

// ========== 2. 按需导入 UI 组件库 ==========
// 使用 unplugin-vue-components 自动按需导入
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

export default defineConfig({
  plugins: [
    Components({
      resolvers: [ElementPlusResolver()]
    })
  ]
})

// ========== 3. 构建分析 ==========
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
  plugins: [
    visualizer({
      open: true,
      gzipSize: true,
      brotliSize: true
    })
  ]
})

// ========== 4. 预加载策略 ==========
// Vite 会自动为入口 chunk 的直接导入生成 <link rel="modulepreload">
// 自定义预加载
export default defineConfig({
  build: {
    modulePreload: {
      polyfill: true  // 为不支持 modulepreload 的浏览器添加 polyfill
    }
  }
})

// ========== 5. Worker 处理 ==========
// 将 CPU 密集型任务移到 Worker
const worker = new Worker(
  new URL('./heavy-task.worker.ts', import.meta.url),
  { type: 'module' }
)

// ========== 6. 合理使用 define 替代运行时判断 ==========
export default defineConfig({
  define: {
    __DEV__: JSON.stringify(process.env.NODE_ENV !== 'production')
  }
})
// 代码中的 if (__DEV__) {} 在生产构建时会被 Tree Shaking 移除

15. Vite 5 / Vite 6 有哪些新特性?

typescript 复制代码
// ========== Vite 5 新特性 ==========
// 1. Rollup 4(比 Rollup 3 快 2 倍)
// 2. 弃用 CJS Node API(完全拥抱 ESM)
// 3. 运行时 API 优化
// 4. 改进的 define 处理
// 5. 更好的 SSR 外部化

// vite.config.ts 必须使用 ESM 格式
// "type": "module" 或使用 .mts 扩展名

// ========== Vite 6 新特性 ==========
// 1. Environment API(多环境支持)
// 同时处理客户端、SSR、RSC 等不同运行环境
export default defineConfig({
  environments: {
    client: {
      build: { outDir: 'dist/client' }
    },
    ssr: {
      build: { outDir: 'dist/server' }
    }
  }
})

// 2. 更好的 CSS 支持
// 改进的 CSS 预处理器性能

// 3. 实验性 Oxc 集成
// 使用 Oxc(Rust 编写)替代部分 esbuild 工作

// 4. JSON 的 Tree Shaking 更完善
import { version } from './package.json'  // 只包含 version 字段

// 5. 改进的预构建
// 更智能的依赖发现,减少不必要的预构建

💡 面试加分点: Vite 的未来方向------Rolldown(Rust 写的 Rollup 替代品,由 Vite 团队开发)将用于替代 esbuild + Rollup 的双引擎架构,实现开发和生产使用同一个打包器,彻底解决开发/生产行为不一致的问题。

相关推荐
GISer_Jing1 小时前
全栈AI实战:基于 TypeScript + LangChain + MCP 的企业级智能研发助手
前端·后端·ai·langchain·前端框架
禁止摆烂_才浅1 小时前
微信小程序高频面试题
前端·面试·微信小程序
禁止摆烂_才浅1 小时前
Vue2 高频面试题
前端·vue.js·面试
禁止摆烂_才浅1 小时前
前端性能优化面试题
前端·面试·性能优化
ClouGence1 小时前
文件上传也能录制回放了:CueCast UI 自动化测试更新
前端·测试
禁止摆烂_才浅1 小时前
Vue3 高频面试题
前端·vue.js·面试
天才熊猫君1 小时前
从“改自己”到“改布局”——拖拽调整的通用设计思路
前端·javascript
何时梦醒1 小时前
CSS 三栏布局与 BFC 深度解析:Float、Flex、Grid 三种方案一网打尽
前端·css·面试
橘子星1 小时前
别只会用 Pick/Omit/Partial!从拼多多一道面试题搞懂 TS 全部内置工具类型
前端