「周更第7期」实用JS库推荐:Vite

引言

本期我们深度解析 Vite 的各个环节实现原理,从开发服务器、依赖预构建(esbuild)、热模块替换(HMR)、插件管线到 Rollup 生产构建,帮助读者建立清晰的工作机制认知与调试方法。

库介绍

基本信息

  • 库名称:Vite
  • 官方定位:下一代前端构建工具(Dev Server + Build)

主要特性

  • 极速冷启动:原生 ESM 按需加载,首次请求即编译
  • 依赖预构建:使用 esbuild 将第三方依赖统一转为高效 ESM
  • HMR:毫秒级热更新,以模块为粒度更新边界
  • 插件体系:统一生命周期钩子(resolve、load、transform 等)
  • 生产构建:基于 Rollup 的稳定打包与优化

JS第三方库介绍标准

  • GitHub Stars:发布前更新为最新
  • 维护状态:活跃维护(需发布前确认)
  • 兼容性:现代浏览器(ESM),Node.js ≥ 18(推荐使用 Vite 5.x),Node.js ≥ 20.19+ 或 22.12+(Vite 7.x+)
  • 包大小:CLI 包与依赖体积需以 npm 发布信息为准(发布前确认)
  • 依赖关系:核心依赖包括 esbuild(预构建)、Rollup(生产构建)等

深层原理解析

1. ES 模块原理与按需加载机制

Vite 的核心优势来自于对原生 ES 模块的充分利用:

javascript 复制代码
/**
 * ES 模块导入原理演示
 * 浏览器原生支持的模块加载机制
 */

// 静态导入 - 编译时确定依赖关系
import { debounce } from 'lodash-es';
import utils from './utils.js';

/**
 * 动态导入 - 运行时按需加载
 * @param {string} modulePath 模块路径
 * @returns {Promise<any>} 模块对象
 */
const loadModule = async (modulePath) => {
  try {
    // 浏览器会发起网络请求获取模块
    const module = await import(modulePath);
    return module;
  } catch (error) {
    console.error(`模块加载失败: ${modulePath}`, error);
    throw error;
  }
};

// Vite 开发服务器处理流程:
// 1. 浏览器请求 /src/main.js
// 2. Vite 拦截请求,实时转换 TypeScript/JSX
// 3. 返回标准 ES 模块代码
// 4. 浏览器解析 import 语句,继续请求依赖模块
// 5. 形成模块依赖图,实现按需加载

2. esbuild 预构建机制深度解析

esbuild 是 Vite 快速启动的关键:

javascript 复制代码
/**
 * 依赖预构建配置与原理
 * esbuild 将 CommonJS/UMD 转换为 ESM
 */
export default defineConfig({
  optimizeDeps: {
    // 强制预构建的依赖
    include: [
      'lodash-es',
      'react',
      'react-dom'
    ],
    // 排除预构建的依赖
    exclude: [
      'some-esm-package'
    ],
    // esbuild 配置选项
    esbuildOptions: {
      target: 'es2020',
      define: {
        global: 'globalThis'
      }
    }
  }
});

/**
 * 预构建过程详解:
 * 1. 扫描入口文件的 import 语句
 * 2. 识别需要预构建的第三方依赖
 * 3. 使用 esbuild 将 CommonJS/UMD 转换为单一 ESM 文件
 * 4. 生成依赖映射表,缓存到 node_modules/.vite 目录
 * 5. 开发服务器启动时直接使用缓存,避免重复构建
 */

3. HMR 实现原理与更新边界

热模块替换的核心是模块依赖图和更新传播:

javascript 复制代码
/**
 * HMR 更新边界与传播机制
 */

// 模块 A - 自接受更新
if (import.meta.hot) {
  import.meta.hot.accept((newModule) => {
    // 模块更新时的处理逻辑
    console.log('模块 A 已更新');
    // 执行清理和重新初始化
    cleanup();
    init();
  });
  
  // 清理副作用
  import.meta.hot.dispose(() => {
    cleanup();
  });
}

// 模块 B - 接受依赖更新
if (import.meta.hot) {
  import.meta.hot.accept(['./moduleA.js'], (modules) => {
    // 依赖模块更新时的处理
    console.log('依赖模块已更新:', modules);
  });
}

/**
 * HMR 更新流程:
 * 1. 文件系统监听器检测到文件变化
 * 2. 重新编译变化的模块
 * 3. 通过 WebSocket 向客户端发送更新消息
 * 4. 客户端接收消息,查找更新边界
 * 5. 如果模块自接受,执行热更新
 * 6. 如果无法热更新,向上冒泡到父模块
 * 7. 最终回退到页面刷新
 */

4. 插件架构设计与生命周期

Vite 的插件系统基于 Rollup 插件 API:

javascript 复制代码
/**
 * 完整的插件生命周期演示
 * @returns {import('vite').Plugin} 插件对象
 */
const comprehensivePlugin = () => ({
  name: 'comprehensive-plugin',
  
  // 构建开始
  buildStart(opts) {
    console.log('构建开始', opts);
  },
  
  // 解析模块 ID
  resolveId(id, importer) {
    if (id === 'virtual:my-module') {
      return id; // 返回解析后的 ID
    }
    return null; // 交给下一个插件处理
  },
  
  // 加载模块内容
  load(id) {
    if (id === 'virtual:my-module') {
      return 'export const msg = "Hello from virtual module"';
    }
    return null;
  },
  
  // 转换模块代码
  transform(code, id) {
    if (id.endsWith('.special')) {
      // 自定义文件类型的转换逻辑
      return {
        code: `export default ${JSON.stringify(code)}`,
        map: null // source map
      };
    }
    return null;
  },
  
  // 生成 bundle 时的钩子
  generateBundle(options, bundle) {
    // 可以修改生成的 bundle
    console.log('生成 bundle', Object.keys(bundle));
  },
  
  // Vite 特有的 HMR 钩子
  handleHotUpdate(ctx) {
    // 自定义 HMR 更新逻辑
    console.log('文件更新:', ctx.file);
    
    // 可以过滤需要更新的模块
    return ctx.modules.filter(mod => {
      return !mod.id?.includes('node_modules');
    });
  }
});

5. Rollup 构建流程与优化策略

生产构建时,Vite 切换到 Rollup:

javascript 复制代码
/**
 * Rollup 构建配置与优化
 */
export default defineConfig({
  build: {
    // 构建目标
    target: 'es2015',
    
    // 输出目录
    outDir: 'dist',
    
    // 是否生成 source map
    sourcemap: true,
    
    // 是否压缩代码
    minify: 'esbuild', // 或 'terser'
    
    // Rollup 特定配置
    rollupOptions: {
      // 外部依赖
      external: ['react', 'react-dom'],
      
      // 输出配置
      output: {
        // 手动代码分割
        manualChunks: {
          vendor: ['react', 'react-dom'],
          utils: ['lodash-es', 'date-fns']
        },
        
        // 文件命名规则
        chunkFileNames: 'js/[name]-[hash].js',
        entryFileNames: 'js/[name]-[hash].js',
        assetFileNames: 'assets/[name]-[hash].[ext]'
      }
    },
    
    // 代码分割阈值
    chunkSizeWarningLimit: 500
  }
});

/**
 * 构建优化策略:
 * 1. Tree Shaking - 移除未使用的代码
 * 2. 代码分割 - 按需加载,减少初始包大小
 * 3. 资源压缩 - 使用 esbuild 或 terser 压缩
 * 4. 缓存优化 - 文件名包含 hash,利用浏览器缓存
 * 5. 预加载 - 生成 preload/prefetch 链接
 */

安装使用

安装方式(仅使用 pnpm)

bash 复制代码
# 安装到现有项目
pnpm add -D vite

# 启动开发服务器(简单场景)
pnpm vite

基础使用

1. 开发服务器与 HMR 自接受示例

javascript 复制代码
/**
 * 模块自接受 HMR 更新示例
 * @returns {void} 无返回值
 */
export const bootstrap = () => {
  /**
   * 获取欢迎文案
   * @returns {string} 文案字符串
   */
  const getWelcome = () => "Hello Vite HMR";

  /**
   * 输出文案
   * @param {string} text 文本
   * @returns {void} 无返回值
   */
  const print = (text) => console.log(text);

  print(getWelcome());

  // 生产环境需条件保护,避免残留 HMR 代码
  if (import.meta.hot) {
    import.meta.hot.accept(() => {
      print("模块已热更新");
    });
  }
};

2. 配置选项(插件钩子简例)

javascript 复制代码
// vite.config.js / vite.config.ts
import { defineConfig } from "vite";

/**
 * 自定义插件示例(展示 resolve/load/transform 钩子)
 * @returns {import('vite').Plugin} 插件对象
 */
const simplePlugin = () => ({
  name: "simple-plugin",
  /** 解析模块ID */
  resolveId: (id) => (id === "virtual:hello" ? id : null),
  /** 加载模块内容 */
  load: (id) => (id === "virtual:hello" ? "export const msg = 'hi'" : null),
  /** 转换模块代码 */
  transform: (code, id) => (id.includes(".js") ? code : null),
});

export default defineConfig({
  plugins: [simplePlugin()],
});

实际应用

企业级 Vue 3 项目搭建

完整的企业级项目配置示例:

javascript 复制代码
/**
 * 企业级 Vue 3 + Vite 项目配置
 * 包含路由、状态管理、UI 库、工具链等完整配置
 */
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';

export default defineConfig({
  plugins: [
    vue(),
    
    // 自动导入 Vue API
    AutoImport({
      imports: ['vue', 'vue-router', 'pinia'],
      resolvers: [ElementPlusResolver()],
      dts: 'src/auto-imports.d.ts'
    }),
    
    // 自动注册组件
    Components({
      resolvers: [ElementPlusResolver()],
      dts: 'src/components.d.ts'
    })
  ],
  
  // 路径别名
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src'),
      '@components': resolve(__dirname, 'src/components'),
      '@utils': resolve(__dirname, 'src/utils'),
      '@api': resolve(__dirname, 'src/api'),
      '@stores': resolve(__dirname, 'src/stores')
    }
  },
  
  // CSS 配置
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@import "@/styles/variables.scss";`
      }
    }
  },
  
  // 开发服务器
  server: {
    port: 3000,
    open: true,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  },
  
  // 构建配置
  build: {
    outDir: 'dist',
    sourcemap: false,
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['vue', 'vue-router', 'pinia'],
          elementPlus: ['element-plus'],
          utils: ['lodash-es', 'dayjs']
        }
      }
    }
  }
});

React 18 + TypeScript 项目配置

javascript 复制代码
/**
 * React 18 + TypeScript + Vite 项目配置
 * 支持 JSX、TypeScript、CSS Modules 等
 */
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';

export default defineConfig({
  plugins: [
    react({
      // 启用 React Fast Refresh
      fastRefresh: true,
      
      // Babel 配置
      babel: {
        plugins: [
          ['import', { libraryName: 'antd', style: true }]
        ]
      }
    })
  ],
  
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src'),
      '@components': resolve(__dirname, 'src/components'),
      '@hooks': resolve(__dirname, 'src/hooks'),
      '@utils': resolve(__dirname, 'src/utils')
    }
  },
  
  // TypeScript 配置
  esbuild: {
    target: 'es2020',
    jsxFactory: 'React.createElement',
    jsxFragment: 'React.Fragment'
  },
  
  // CSS Modules 配置
  css: {
    modules: {
      localsConvention: 'camelCase',
      generateScopedName: '[name]__[local]___[hash:base64:5]'
    },
    preprocessorOptions: {
      less: {
        modifyVars: {
          '@primary-color': '#1890ff'
        },
        javascriptEnabled: true
      }
    }
  },
  
  // 依赖优化
  optimizeDeps: {
    include: ['react', 'react-dom', 'antd'],
    exclude: ['@types/react']
  }
});

微前端主应用配置

javascript 复制代码
/**
 * 微前端主应用配置
 * 使用 qiankun 框架集成多个子应用
 */
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import qiankun from 'vite-plugin-qiankun';

export default defineConfig({
  plugins: [
    vue(),
    qiankun('main-app', {
      useDevMode: true
    })
  ],
  
  server: {
    port: 8080,
    cors: true,
    headers: {
      'Access-Control-Allow-Origin': '*'
    }
  },
  
  build: {
    target: 'esnext',
    lib: {
      name: 'main-app',
      formats: ['umd']
    }
  }
});

/**
 * 主应用入口文件
 */
import { createApp } from 'vue';
import { registerMicroApps, start } from 'qiankun';
import App from './App.vue';

const app = createApp(App);

// 注册子应用
registerMicroApps([
  {
    name: 'user-center',
    entry: '//localhost:3001',
    container: '#user-center',
    activeRule: '/user'
  },
  {
    name: 'order-system',
    entry: '//localhost:3002',
    container: '#order-system',
    activeRule: '/order'
  }
]);

// 启动 qiankun
start();

app.mount('#app');

移动端 H5 项目配置

javascript 复制代码
/**
 * 移动端 H5 项目配置
 * 支持 viewport 适配、PWA、移动端调试等
 */
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { VitePWA } from 'vite-plugin-pwa';
import postcssPxToViewport from 'postcss-px-to-viewport';

export default defineConfig({
  plugins: [
    vue(),
    
    // PWA 配置
    VitePWA({
      registerType: 'autoUpdate',
      workbox: {
        globPatterns: ['**/*.{js,css,html,ico,png,svg}']
      },
      manifest: {
        name: 'Mobile App',
        short_name: 'MobileApp',
        description: 'A mobile application built with Vite',
        theme_color: '#ffffff',
        icons: [
          {
            src: 'pwa-192x192.png',
            sizes: '192x192',
            type: 'image/png'
          }
        ]
      }
    })
  ],
  
  // PostCSS 配置
  css: {
    postcss: {
      plugins: [
        postcssPxToViewport({
          viewportWidth: 375,
          viewportHeight: 667,
          unitPrecision: 3,
          viewportUnit: 'vw',
          selectorBlackList: ['.ignore'],
          minPixelValue: 1,
          mediaQuery: false
        })
      ]
    }
  },
  
  // 移动端调试
  server: {
    host: '0.0.0.0',
    port: 3000
  },
  
  build: {
    target: 'es2015',
    cssTarget: 'chrome61'
  }
});

项目迁移场景

从 Webpack 迁移到 Vite 的完整指南:

javascript 复制代码
/**
 * Webpack 到 Vite 迁移配置对比
 */

// Webpack 配置 (webpack.config.js)
module.exports = {
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js'
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        exclude: /node_modules/
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin(),
    new HtmlWebpackPlugin({
      template: 'public/index.html'
    })
  ]
};

// Vite 配置 (vite.config.js)
export default defineConfig({
  plugins: [vue()],
  
  // 无需配置入口,默认为 index.html
  // 无需配置输出,默认为 dist
  // 无需配置 loader,内置支持
  
  build: {
    rollupOptions: {
      output: {
        entryFileNames: '[name].[hash].js',
        chunkFileNames: '[name].[hash].js',
        assetFileNames: '[name].[hash].[ext]'
      }
    }
  }
});

/**
 * 迁移步骤清单
 */
const migrationSteps = [
  '1. 安装 Vite 和相关插件',
  '2. 创建 vite.config.js 配置文件',
  '3. 更新 package.json 脚本',
  '4. 调整 index.html 结构',
  '5. 更新环境变量前缀(VITE_)',
  '6. 替换 Webpack 特定的 API',
  '7. 调整静态资源引用方式',
  '8. 测试构建和开发环境'
];

大型项目优化

javascript 复制代码
/**
 * 大型项目性能优化配置
 * 针对 1000+ 组件的大型应用
 */
export default defineConfig({
  // 依赖预构建优化
  optimizeDeps: {
    include: [
      // 大型 UI 库
      'element-plus',
      'ant-design-vue',
      '@ant-design/icons-vue',
      
      // 工具库
      'lodash-es',
      'dayjs',
      'axios',
      
      // 图表库
      'echarts',
      '@antv/g2'
    ],
    
    // 排除本地包
    exclude: ['@company/design-system'],
    
    // esbuild 优化
    esbuildOptions: {
      target: 'es2020',
      supported: {
        'top-level-await': true
      }
    }
  },
  
  // 构建优化
  build: {
    // 代码分割策略
    rollupOptions: {
      output: {
        manualChunks: (id) => {
          // 第三方库分离
          if (id.includes('node_modules')) {
            if (id.includes('vue')) return 'vue-vendor';
            if (id.includes('element-plus')) return 'ui-vendor';
            if (id.includes('echarts')) return 'chart-vendor';
            if (id.includes('lodash')) return 'utils-vendor';
            return 'vendor';
          }
          
          // 业务模块分离
          if (id.includes('src/modules/user')) return 'user-module';
          if (id.includes('src/modules/order')) return 'order-module';
          if (id.includes('src/modules/product')) return 'product-module';
          
          // 公共组件
          if (id.includes('src/components')) return 'components';
        }
      }
    },
    
    // 压缩优化
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true,
        pure_funcs: ['console.log']
      }
    },
    
    // 资源优化
    assetsInlineLimit: 4096,
    cssCodeSplit: true
  },
  
  // 开发服务器优化
  server: {
    // 预热关键文件
    warmup: {
      clientFiles: [
        './src/components/**/*.vue',
        './src/modules/**/*.vue',
        './src/utils/**/*.js'
      ]
    }
  }
});

依赖预构建与冷启动优化

javascript 复制代码
/**
 * 依赖预构建深度优化
 * 解决大型项目冷启动慢的问题
 */
export default defineConfig({
  optimizeDeps: {
    // 强制预构建的依赖
    include: [
      'vue',
      'vue-router',
      'pinia',
      'element-plus',
      'lodash-es',
      'dayjs',
      'axios'
    ],
    
    // 排除预构建
    exclude: [
      '@company/internal-lib',
      'virtual:*'
    ],
    
    // esbuild 配置
    esbuildOptions: {
      target: 'es2020',
      define: {
        global: 'globalThis'
      },
      supported: {
        'top-level-await': true
      },
      plugins: [
        // 自定义 esbuild 插件
        {
          name: 'custom-resolve',
          setup(build) {
            build.onResolve({ filter: /^@company/ }, (args) => {
              return {
                path: args.path,
                external: true
              };
            });
          }
        }
      ]
    }
  },
  
  // 缓存配置
  cacheDir: 'node_modules/.vite',
  
  // 文件系统优化
  server: {
    fs: {
      strict: false,
      allow: ['..']
    }
  }
});

HMR 更新边界与回退策略

javascript 复制代码
/**
 * HMR 更新边界配置
 * 精确控制热更新范围和回退策略
 */

// 组件级 HMR
// src/components/UserCard.vue
export default {
  name: 'UserCard',
  // ... 组件逻辑
};

// HMR 自接受
if (import.meta.hot) {
  import.meta.hot.accept((newModule) => {
    console.log('UserCard 组件已更新');
    // 自定义更新逻辑
  });
}

// 工具模块 HMR
// src/utils/api.js
export const apiClient = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL
});

// 接受依赖更新
if (import.meta.hot) {
  import.meta.hot.accept('./config.js', (newConfig) => {
    // 重新配置 API 客户端
    apiClient.defaults.baseURL = newConfig.apiBaseUrl;
  });
  
  // 处理更新失败
  import.meta.hot.invalidate = () => {
    console.log('API 模块更新失败,执行完整重载');
    window.location.reload();
  };
}

// 状态管理 HMR
// src/stores/user.js
import { defineStore } from 'pinia';

export const useUserStore = defineStore('user', {
  state: () => ({
    currentUser: null,
    permissions: []
  }),
  
  actions: {
    async fetchUser() {
      // 获取用户信息
    }
  }
});

// Pinia HMR 支持
if (import.meta.hot) {
  import.meta.hot.accept(acceptHMRUpdate(useUserStore, import.meta.hot));
}

优缺点分析

优点 ✅

  • 原生 ESM 按需加载,开发体验流畅
  • esbuild 预构建,大幅提升冷启动与依赖解析效率
  • HMR 快速稳定、插件体系扩展性强
  • 与 Rollup 生态兼容,生产构建成熟

缺点 ❌

  • 非 ESM 生态或复杂打包场景需要额外适配
  • 某些极端 HMR 边界下可能回退到整页刷新

最佳实践

1. 性能优化

  • 合理配置预构建入口,避免重复解析
  • 使用按需转换与缓存,减少不必要编译

2. 错误处理

  • 明确区分开发/生产环境变量注入策略
  • 生产构建条件保护 HMR 逻辑(import.meta.hot)

3. 样式与预处理器

  • 统一使用 Less 并通过全局变量管理颜色/字体/间距(参见 styles/variables.less)

进阶用法

自定义插件开发

Vite 插件基于 Rollup 插件架构,支持额外的 Vite 特定钩子:

javascript 复制代码
/**
 * 自定义 Vite 插件示例
 * 实现文件内容转换和虚拟模块
 */
const customPlugin = () => {
  return {
    name: 'custom-plugin',
    
    /**
     * 配置解析钩子
     * @param {Object} config - Vite 配置对象
     * @param {Object} env - 环境信息
     */
    config(config, { command }) {
      if (command === 'serve') {
        // 开发模式配置
        config.define = config.define || {};
        config.define.__DEV__ = true;
      }
    },
    
    /**
     * 配置开发服务器
     * @param {Object} server - 开发服务器实例
     */
    configureServer(server) {
      server.middlewares.use('/api', (req, res, next) => {
        if (req.url === '/api/health') {
          res.end('OK');
        } else {
          next();
        }
      });
    },
    
    /**
     * 解析模块 ID
     * @param {string} id - 模块标识符
     * @param {string} importer - 导入者路径
     */
    resolveId(id, importer) {
      if (id === 'virtual:my-module') {
        return id;
      }
    },
    
    /**
     * 加载模块内容
     * @param {string} id - 模块标识符
     */
    load(id) {
      if (id === 'virtual:my-module') {
        return 'export const msg = "Hello from virtual module!"';
      }
    },
    
    /**
     * 转换模块内容
     * @param {string} code - 源代码
     * @param {string} id - 模块标识符
     */
    transform(code, id) {
      if (id.endsWith('.special')) {
        return {
          code: `export default ${JSON.stringify(code)}`,
          map: null
        };
      }
    },
    
    /**
     * HMR 更新处理
     * @param {Object} ctx - HMR 上下文
     */
    handleHotUpdate(ctx) {
      if (ctx.file.endsWith('.special')) {
        console.log('Special file updated:', ctx.file);
        // 自定义更新逻辑
        ctx.server.ws.send({
          type: 'full-reload'
        });
        return [];
      }
    }
  };
};

// 使用插件
export default defineConfig({
  plugins: [customPlugin()]
});

性能优化策略

javascript 复制代码
/**
 * Vite 性能优化配置
 * 涵盖构建、开发、网络等多个维度
 */
export default defineConfig({
  // 依赖预构建优化
  optimizeDeps: {
    // 强制预构建
    include: ['lodash-es', 'axios'],
    // 排除预构建
    exclude: ['@my/local-package'],
    // esbuild 选项
    esbuildOptions: {
      target: 'es2020',
      supported: {
        'top-level-await': true
      }
    }
  },
  
  // 构建优化
  build: {
    // 目标环境
    target: ['es2020', 'edge88', 'firefox78', 'chrome87', 'safari13.1'],
    
    // 代码分割策略
    rollupOptions: {
      output: {
        manualChunks: {
          // 第三方库分离
          vendor: ['vue', 'vue-router'],
          utils: ['lodash-es', 'date-fns'],
          
          // 动态分割
          ...(() => {
            const chunks = {};
            // 按目录分割
            return (id) => {
              if (id.includes('node_modules')) {
                return 'vendor';
              }
              if (id.includes('src/components')) {
                return 'components';
              }
              if (id.includes('src/utils')) {
                return 'utils';
              }
            };
          })()
        }
      }
    },
    
    // 压缩配置
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    },
    
    // 资源内联阈值
    assetsInlineLimit: 4096,
    
    // CSS 代码分割
    cssCodeSplit: true,
    
    // 生成 sourcemap
    sourcemap: process.env.NODE_ENV === 'development'
  },
  
  // 开发服务器优化
  server: {
    // 预热文件
    warmup: {
      clientFiles: ['./src/components/*.vue', './src/utils/*.js']
    },
    
    // 文件系统缓存
    fs: {
      cachedChecks: false
    }
  },
  
  // 实验性功能
  experimental: {
    // 构建高级基础路径
    renderBuiltUrl(filename, { hostType }) {
      if (hostType === 'js') {
        return { js: `https://cdn.example.com/${filename}` };
      } else {
        return { relative: true };
      }
    }
  }
});

/**
 * 性能监控插件
 * 监控构建时间和包大小
 */
const performancePlugin = () => {
  let startTime;
  
  return {
    name: 'performance-monitor',
    
    buildStart() {
      startTime = Date.now();
      console.log('🚀 构建开始...');
    },
    
    buildEnd() {
      const duration = Date.now() - startTime;
      console.log(`✅ 构建完成,耗时: ${duration}ms`);
    },
    
    generateBundle(options, bundle) {
      const sizes = Object.entries(bundle).map(([name, chunk]) => ({
        name,
        size: chunk.code ? chunk.code.length : 0
      }));
      
      console.table(sizes);
    }
  };
};

多环境构建配置

javascript 复制代码
/**
 * 多环境构建配置
 * 支持开发、测试、生产等不同环境
 */
import { defineConfig, loadEnv } from 'vite';

export default defineConfig(({ command, mode }) => {
  // 加载环境变量
  const env = loadEnv(mode, process.cwd(), '');
  
  /**
   * 获取基础配置
   * @param {string} mode - 构建模式
   * @returns {Object} 配置对象
   */
  const getBaseConfig = (mode) => ({
    plugins: [
      // 基础插件
    ],
    
    define: {
      __APP_VERSION__: JSON.stringify(process.env.npm_package_version),
      __BUILD_TIME__: JSON.stringify(new Date().toISOString())
    },
    
    css: {
      preprocessorOptions: {
        less: {
          modifyVars: {
            'primary-color': env.VITE_PRIMARY_COLOR || '#1890ff'
          }
        }
      }
    }
  });
  
  // 环境特定配置
  const configs = {
    development: {
      ...getBaseConfig(mode),
      server: {
        port: 3000,
        proxy: {
          '/api': {
            target: env.VITE_API_URL,
            changeOrigin: true,
            rewrite: (path) => path.replace(/^\/api/, '')
          }
        }
      }
    },
    
    testing: {
      ...getBaseConfig(mode),
      build: {
        sourcemap: true,
        minify: false
      },
      test: {
        environment: 'jsdom',
        setupFiles: ['./src/test/setup.js']
      }
    },
    
    production: {
      ...getBaseConfig(mode),
      build: {
        outDir: 'dist',
        assetsDir: 'assets',
        rollupOptions: {
          output: {
            chunkFileNames: 'js/[name]-[hash].js',
            entryFileNames: 'js/[name]-[hash].js',
            assetFileNames: '[ext]/[name]-[hash].[ext]'
          }
        }
      }
    }
  };
  
  return configs[mode] || configs.development;
});

微前端集成

javascript 复制代码
/**
 * 微前端模块联邦配置
 * 使用 @originjs/vite-plugin-federation
 */
import { defineConfig } from 'vite';
import federation from '@originjs/vite-plugin-federation';

export default defineConfig({
  plugins: [
    federation({
      name: 'host-app',
      remotes: {
        // 远程模块
        mfApp: 'http://localhost:3001/assets/remoteEntry.js'
      },
      shared: {
        // 共享依赖
        vue: {
          singleton: true,
          requiredVersion: '^3.0.0'
        },
        'vue-router': {
          singleton: true
        }
      }
    })
  ],
  
  build: {
    target: 'esnext',
    minify: false,
    cssCodeSplit: false
  }
});

/**
 * 远程模块配置
 */
export default defineConfig({
  plugins: [
    federation({
      name: 'remote-app',
      filename: 'remoteEntry.js',
      exposes: {
        // 暴露组件
        './Button': './src/components/Button.vue',
        './utils': './src/utils/index.js'
      },
      shared: {
        vue: {
          singleton: true
        }
      }
    })
  ]
});

开发工具集成

javascript 复制代码
/**
 * 开发工具集成配置
 * 包括 ESLint、Prettier、TypeScript 等
 */
export default defineConfig({
  plugins: [
    // ESLint 集成
    eslint({
      include: ['src/**/*.js', 'src/**/*.vue', 'src/**/*.ts'],
      exclude: ['node_modules', 'dist'],
      cache: false
    }),
    
    // 类型检查
    checker({
      typescript: true,
      vueTsc: true,
      eslint: {
        lintCommand: 'eslint "./src/**/*.{js,ts,vue}"'
      }
    }),
    
    // 自动导入
    AutoImport({
      imports: ['vue', 'vue-router'],
      dts: true,
      eslintrc: {
        enabled: true
      }
    }),
    
    // 组件自动注册
    Components({
      dts: true,
      resolvers: [ElementPlusResolver()]
    })
  ],
  
  // TypeScript 配置
  esbuild: {
    target: 'es2020',
    include: /\.(ts|tsx|js|jsx)$/,
    exclude: []
  }
});

插件生命周期

  • 在 resolve/load/transform/handleHotUpdate 等钩子中接入,记录与调试执行顺序。

构建与代码分割

  • 使用 Rollup 的动态/静态导入实现代码分割与资源优化。

故障排除

兼容性

  • 浏览器支持:现代浏览器(原生 ESM)
  • Node.js 支持:≥ 18
  • 框架兼容:Vue、React、Svelte、Solid 等主流框架生态均有官方或社区适配
  • TypeScript 支持:内置 TS 转换(开发态),生产态由 Rollup 插件链处理

TypeScript 支持

  • 开发环境:按需转换 TS/TSX,结合 HMR 提供良好 DX
  • 生产构建:结合 Rollup 与相关插件(如 @rollup/plugin-typescript)完成类型与产物处理

自定义扩展

javascript 复制代码
/**
 * 自定义扩展:在 handleHotUpdate 中记录热更新信息
 * @returns {import('vite').Plugin} 插件对象
 */
export const hotLogger = () => ({
  name: "hot-logger",
  /**
   * 记录热更新文件
   * @param {import('vite').HmrContext} ctx 上下文
   * @returns {void} 无返回值
   */
  handleHotUpdate: (ctx) => {
    const log = (msg) => console.log(`[HMR] ${msg}`);
    log(`updated: ${ctx.file}`);
  },
});

工具集成

  • 构建工具:与 Rollup 深度集成,生产环境以 Rollup 打包
  • 测试框架:可与 Vitest/Playwright 等集成
  • 开发工具:丰富的插件生态(别名、环境变量、预处理器等)

环境变量与模式深度解析

Vite 的环境变量系统基于 dotenv 实现:

javascript 复制代码
/**
 * 环境变量加载优先级
 * 
 * 1. .env                # 所有情况下都会加载
 * 2. .env.local          # 所有情况下都会加载,但会被 git 忽略
 * 3. .env.[mode]         # 只在指定模式下加载
 * 4. .env.[mode].local   # 只在指定模式下加载,但会被 git 忽略
 */

// .env 文件示例
// VITE_API_URL=https://api.example.com
// DB_PASSWORD=secret  # 不会暴露给客户端

/**
 * 环境变量使用示例
 * 只有 VITE_ 前缀的变量会暴露给客户端
 */
const apiUrl = import.meta.env.VITE_API_URL;
const mode = import.meta.env.MODE;
const isDev = import.meta.env.DEV;
const isProd = import.meta.env.PROD;

/**
 * 自定义环境变量前缀
 */
export default defineConfig({
  // 自定义环境变量前缀
  envPrefix: ['VITE_', 'APP_'],
  
  // 指定构建模式
  mode: 'development', // 'production' | 'development' | 'test'
});

/**
 * 环境变量类型定义(TypeScript)
 */
// vite-env.d.ts
interface ImportMetaEnv {
  readonly VITE_API_URL: string;
  readonly VITE_APP_TITLE: string;
  readonly MODE: string;
  readonly DEV: boolean;
  readonly PROD: boolean;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

SSR 架构与中间件模式

Vite 的 SSR 支持基于中间件模式:

javascript 复制代码
/**
 * Vite SSR 中间件模式示例
 * 结合 Express 实现服务端渲染
 */
import express from 'express';
import { createServer as createViteServer } from 'vite';

/**
 * 创建 SSR 开发服务器
 * @returns {Promise<void>} 无返回值
 */
const createSSRDevServer = async () => {
  const app = express();
  
  // 创建 Vite 服务器
  const vite = await createViteServer({
    server: { middlewareMode: true },
    appType: 'custom'
  });
  
  // 使用 Vite 中间件
  app.use(vite.middlewares);
  
  // 处理所有请求
  app.use('*', async (req, res) => {
    const url = req.originalUrl;
    
    try {
      // 1. 读取 index.html
      let template = fs.readFileSync(
        path.resolve(__dirname, 'index.html'),
        'utf-8'
      );
      
      // 2. 应用 Vite HTML 转换
      template = await vite.transformIndexHtml(url, template);
      
      // 3. 加载服务器入口
      const { render } = await vite.ssrLoadModule('/src/entry-server.js');
      
      // 4. 渲染应用 HTML
      const { html: appHtml, state } = await render(url);
      
      // 5. 注入渲染后的 HTML 到模板
      const html = template
        .replace('<!--app-html-->', appHtml)
        .replace('<!--app-state-->', `<script>window.__INITIAL_STATE__=${JSON.stringify(state)}</script>`);
      
      // 6. 发送渲染后的 HTML
      res.status(200).set({ 'Content-Type': 'text/html' }).end(html);
    } catch (e) {
      // 错误处理
      vite.ssrFixStacktrace(e);
      console.error(e);
      res.status(500).end(e.message);
    }
  });
  
  app.listen(3000, () => {
    console.log('SSR 服务器启动在 http://localhost:3000');
  });
};

createSSRDevServer();

/**
 * SSR 架构关键点:
 * 1. 客户端/服务端入口分离
 * 2. 避免有副作用的代码
 * 3. 使用 vite.ssrLoadModule 加载服务端模块
 * 4. 使用 vite.transformIndexHtml 处理 HTML
 * 5. 使用 vite.ssrFixStacktrace 修复错误堆栈
 */

实际开发问题解决方案

问题1:图片资源 CDN 配置与优化

在实际项目中,图片资源通常需要上传到 CDN 以提升加载速度。以下是完整的解决方案:

javascript 复制代码
// vite.config.js - CDN 配置
import { defineConfig } from 'vite';

/**
 * CDN 配置函数
 * @param {string} env 环境变量
 * @returns {object} 配置对象
 */
const getCDNConfig = (env) => {
  const cdnMap = {
    development: 'http://localhost:3000',
    staging: 'https://staging-cdn.example.com',
    production: 'https://cdn.example.com'
  };
  
  return {
    base: cdnMap[env] || cdnMap.development,
    build: {
      assetsDir: 'assets',
      rollupOptions: {
        output: {
          // 静态资源分类
          assetFileNames: (assetInfo) => {
            const info = assetInfo.name.split('.');
            const ext = info[info.length - 1];
            
            if (/\.(png|jpe?g|gif|svg|webp|ico)$/i.test(assetInfo.name)) {
              return `images/[name]-[hash][extname]`;
            }
            if (/\.(woff2?|eot|ttf|otf)$/i.test(assetInfo.name)) {
              return `fonts/[name]-[hash][extname]`;
            }
            return `assets/[name]-[hash][extname]`;
          }
        }
      }
    }
  };
};

export default defineConfig(({ mode }) => ({
  ...getCDNConfig(mode),
  
  // 图片压缩插件
  plugins: [
    // 图片优化插件
    {
      name: 'image-optimizer',
      /**
       * 处理图片资源
       * @param {string} id 文件ID
       * @param {object} options 选项
       * @returns {string|null} 处理结果
       */
      load: (id) => {
        if (/\.(png|jpe?g|gif|webp)$/i.test(id)) {
          // 这里可以集成图片压缩逻辑
          console.log(`优化图片: ${id}`);
        }
        return null;
      }
    }
  ],
  
  // 静态资源处理
  assetsInclude: ['**/*.png', '**/*.jpg', '**/*.jpeg', '**/*.gif', '**/*.svg', '**/*.webp']
}));
javascript 复制代码
// src/utils/imageUtils.js - 图片工具函数
/**
 * 获取 CDN 图片 URL
 * @param {string} imagePath 图片路径
 * @param {object} options 选项
 * @returns {string} 完整的图片 URL
 */
export const getCDNImageUrl = (imagePath, options = {}) => {
  const { 
    width, 
    height, 
    quality = 80, 
    format = 'webp' 
  } = options;
  
  const baseUrl = import.meta.env.VITE_CDN_BASE_URL || '';
  const params = new URLSearchParams();
  
  if (width) params.append('w', width);
  if (height) params.append('h', height);
  if (quality) params.append('q', quality);
  if (format) params.append('f', format);
  
  const queryString = params.toString();
  return `${baseUrl}${imagePath}${queryString ? `?${queryString}` : ''}`;
};

/**
 * 响应式图片组件
 * @param {object} props 组件属性
 * @returns {string} HTML 字符串
 */
export const createResponsiveImage = (props) => {
  const { src, alt, sizes = [] } = props;
  
  const srcSet = sizes.map(size => 
    `${getCDNImageUrl(src, { width: size.width })} ${size.width}w`
  ).join(', ');
  
  return `
    <img 
      src="${getCDNImageUrl(src, { width: 800 })}"
      srcset="${srcSet}"
      sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
      alt="${alt}"
      loading="lazy"
    />
  `;
};

// 使用示例
const imageUrl = getCDNImageUrl('/images/hero.jpg', {
  width: 1200,
  height: 600,
  quality: 85,
  format: 'webp'
});

console.log(imageUrl); 
// 输出: https://cdn.example.com/images/hero.jpg?w=1200&h=600&q=85&f=webp

问题2:第三方库兼容性问题

某些第三方库可能不支持 ES 模块或存在兼容性问题:

javascript 复制代码
// vite.config.js - 第三方库兼容性配置
export default defineConfig({
  optimizeDeps: {
    // 强制预构建的依赖
    include: [
      'lodash-es',
      'axios',
      'dayjs',
      // 解决 CJS 模块问题
      'some-cjs-library'
    ],
    // 排除预构建的依赖
    exclude: [
      // 已经是 ES 模块的库
      'es-module-library'
    ],
    // 自定义 esbuild 选项
    esbuildOptions: {
      // 解决全局变量问题
      define: {
        global: 'globalThis'
      },
      // 处理 Node.js 内置模块
      plugins: [
        {
          name: 'node-globals-polyfill',
          setup(build) {
            build.onResolve({ filter: /^(buffer|process)$/ }, args => ({
              path: args.path,
              namespace: 'node-globals'
            }));
          }
        }
      ]
    }
  },
  
  // 解决模块解析问题
  resolve: {
    alias: {
      // 解决路径别名
      '@': path.resolve(__dirname, 'src'),
      // 解决模块兼容性
      'problematic-lib': 'problematic-lib/dist/es/index.js'
    }
  },
  
  // 定义全局变量
  define: {
    // 解决 process.env 问题
    'process.env': process.env,
    // 解决 __DEV__ 问题
    __DEV__: JSON.stringify(process.env.NODE_ENV === 'development')
  },
  
  plugins: [
    // 兼容性插件
    {
      name: 'legacy-support',
      /**
       * 处理遗留模块
       * @param {string} id 模块ID
       * @returns {string|null} 处理结果
       */
      load: (id) => {
        // 处理特定的兼容性问题
        if (id.includes('legacy-library')) {
          return `
            // 兼容性包装
            import originalLib from 'legacy-library/dist/umd/index.js';
            export default originalLib;
            export const { method1, method2 } = originalLib;
          `;
        }
        return null;
      }
    }
  ]
});
javascript 复制代码
// src/utils/compatUtils.js - 兼容性工具
/**
 * 动态导入兼容性包装
 * @param {string} moduleName 模块名称
 * @returns {Promise<any>} 模块对象
 */
export const safeImport = async (moduleName) => {
  try {
    const module = await import(moduleName);
    return module.default || module;
  } catch (error) {
    console.warn(`模块 ${moduleName} 导入失败,使用降级方案:`, error);
    
    // 降级方案
    switch (moduleName) {
      case 'problematic-lib':
        return await import('./fallbacks/problematic-lib-fallback.js');
      default:
        throw new Error(`无法加载模块: ${moduleName}`);
    }
  }
};

/**
 * 检查浏览器兼容性
 * @returns {object} 兼容性信息
 */
export const checkCompatibility = () => {
  const features = {
    esModules: 'noModule' in HTMLScriptElement.prototype,
    dynamicImport: typeof import === 'function',
    webp: (() => {
      const canvas = document.createElement('canvas');
      return canvas.toDataURL('image/webp').indexOf('data:image/webp') === 0;
    })(),
    intersectionObserver: 'IntersectionObserver' in window
  };
  
  return features;
};

// 使用示例
const compatibility = checkCompatibility();
if (!compatibility.esModules) {
  // 加载 polyfill
  await import('./polyfills/es-modules.js');
}

问题3:大型项目构建性能优化

大型项目在构建时可能遇到性能瓶颈:

javascript 复制代码
// vite.config.js - 大型项目优化配置
import { defineConfig } from 'vite';
import { resolve } from 'path';

/**
 * 获取优化配置
 * @param {boolean} isProduction 是否生产环境
 * @returns {object} 优化配置
 */
const getOptimizationConfig = (isProduction) => ({
  build: {
    // 启用 Rollup 的多线程
    rollupOptions: {
      // 代码分割策略
      output: {
        manualChunks: (id) => {
          // 第三方库分离
          if (id.includes('node_modules')) {
            // 大型库单独分包
            if (id.includes('lodash')) return 'lodash';
            if (id.includes('antd') || id.includes('@ant-design')) return 'antd';
            if (id.includes('echarts')) return 'echarts';
            if (id.includes('moment') || id.includes('dayjs')) return 'date-utils';
            
            // 其他第三方库
            return 'vendor';
          }
          
          // 业务代码分离
          if (id.includes('/src/pages/')) {
            const pageName = id.split('/src/pages/')[1].split('/')[0];
            return `page-${pageName}`;
          }
          
          if (id.includes('/src/components/')) {
            return 'components';
          }
          
          if (id.includes('/src/utils/')) {
            return 'utils';
          }
        }
      },
      
      // 外部化大型依赖
      external: isProduction ? [] : ['lodash', 'moment']
    },
    
    // 构建优化
    target: 'es2015',
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: isProduction,
        drop_debugger: isProduction
      }
    },
    
    // 启用 gzip 压缩
    reportCompressedSize: false,
    chunkSizeWarningLimit: 1000
  },
  
  // 开发服务器优化
  server: {
    // 预热常用文件
    warmup: {
      clientFiles: [
        './src/components/**/*.vue',
        './src/utils/**/*.js'
      ]
    }
  },
  
  // 依赖优化
  optimizeDeps: {
    // 大型项目依赖预构建
    include: [
      'vue',
      'vue-router',
      'pinia',
      'axios',
      'lodash-es',
      'dayjs'
    ],
    
    // 强制重新构建
    force: process.env.FORCE_OPTIMIZE === 'true'
  }
});

export default defineConfig(({ mode }) => {
  const isProduction = mode === 'production';
  
  return {
    ...getOptimizationConfig(isProduction),
    
    plugins: [
      // 构建分析插件
      {
        name: 'build-analyzer',
        /**
         * 构建完成后分析
         * @param {object} options 构建选项
         * @returns {void} 无返回值
         */
        closeBundle: () => {
          if (isProduction) {
            console.log('📊 构建分析完成,检查 dist 目录大小分布');
          }
        }
      },
      
      // 缓存优化插件
      {
        name: 'cache-optimizer',
        /**
         * 配置开发服务器
         * @param {object} server 服务器实例
         * @returns {void} 无返回值
         */
        configureServer: (server) => {
          // 设置缓存策略
          server.middlewares.use('/assets', (req, res, next) => {
            res.setHeader('Cache-Control', 'public, max-age=31536000');
            next();
          });
        }
      }
    ]
  };
});
javascript 复制代码
// scripts/build-optimization.js - 构建优化脚本
import { execSync } from 'child_process';
import { statSync, readdirSync } from 'fs';
import { join } from 'path';

/**
 * 分析构建产物大小
 * @param {string} distPath 构建目录路径
 * @returns {object} 分析结果
 */
const analyzeBuildSize = (distPath) => {
  const getDirectorySize = (dirPath) => {
    let totalSize = 0;
    const files = readdirSync(dirPath);
    
    files.forEach(file => {
      const filePath = join(dirPath, file);
      const stats = statSync(filePath);
      
      if (stats.isDirectory()) {
        totalSize += getDirectorySize(filePath);
      } else {
        totalSize += stats.size;
      }
    });
    
    return totalSize;
  };
  
  const totalSize = getDirectorySize(distPath);
  const sizeInMB = (totalSize / 1024 / 1024).toFixed(2);
  
  console.log(`📦 构建产物总大小: ${sizeInMB} MB`);
  
  return {
    totalSize,
    sizeInMB
  };
};

/**
 * 构建性能监控
 * @returns {void} 无返回值
 */
const monitorBuildPerformance = () => {
  const startTime = Date.now();
  
  console.log('🚀 开始构建...');
  
  try {
    // 执行构建
    execSync('vite build', { stdio: 'inherit' });
    
    const endTime = Date.now();
    const buildTime = ((endTime - startTime) / 1000).toFixed(2);
    
    console.log(`✅ 构建完成,耗时: ${buildTime}s`);
    
    // 分析构建结果
    analyzeBuildSize('./dist');
    
    // 生成构建报告
    console.log('📊 生成构建报告...');
    execSync('npx vite-bundle-analyzer dist', { stdio: 'inherit' });
    
  } catch (error) {
    console.error('❌ 构建失败:', error.message);
    process.exit(1);
  }
};

// 执行构建监控
monitorBuildPerformance();

常见问题

  • 依赖预构建未命中:检查锁定版本、非标准导出,必要时手动配置
  • HMR 不触发:确认依赖边界自接受/依赖接受是否正确
  • 环境变量不可用:检查前缀是否为 VITE_,或自定义 envPrefix
  • SSR 渲染错误:检查模块是否有浏览器特定 API,使用 import.meta.env.SSR 条件判断
  • 图片 CDN 加载失败:检查 CDN 域名配置、图片路径拼接、网络连接状态
  • 第三方库兼容性问题:使用 optimizeDeps.include 强制预构建,或配置 alias 指向兼容版本
  • 大型项目构建缓慢:优化代码分割策略、启用并行构建、合理配置 manualChunks

调试技巧

  • 打印插件钩子执行时序,定位 transform 与热更新处理
  • 使用 --debug 启动 Vite,查看详细日志
  • 检查 .vite 缓存目录,了解预构建结果
  • 使用浏览器网络面板分析模块加载顺序和依赖关系

总结

  • 推荐理由:开发体验迅捷(原生 ESM 按需 + HMR)、生态完善(插件体系与 Rollup 构建)、配置清晰(共享选项与环境变量机制),适合多数现代前端项目。
  • 适用场景:Vue/React/Svelte 等现代框架、需要高频迭代的中大型前端、希望扩展插件链与中间件的团队。
  • 不适用场景:强依赖非 ESM 的遗留系统或对 HMR 副作用清理要求极端且不可控的项目。
  • 实施建议:
    • 开发期:优化依赖预构建(optimizeDeps)、严守 HMR 边界与副作用清理(accept/dispose)。
    • 构建期:与 Rollup 配合做代码分割与产物分析;使用 alias 明确路径与外部化策略。
    • 工程化:统一使用 pnpm;Less 管理全局变量(颜色、字体、间距);TS 仅转译,类型检查独立跑,保障热更新速度。

阅读路线(建议)

  • 新手:理解原生 ESM 与 HMR 基础,用脚手架快速上手
  • 进阶:掌握依赖预构建触发与缓存、HMR 更新边界与回退、插件钩子调试
  • 高阶:SSR 中间件模式集成、复杂插件(handleHotUpdate)、monorepo 链接依赖与缓存失效策略

相关链接

下期预告:下周我们将介绍另一个实用的前端工具库,敬请期待!

相关推荐
小中12345 小时前
异步请求的性能提升
前端
我是天龙_绍5 小时前
渐变层生成器——直接用鼠标拖拽就可以调整渐变层的各种参数,然后可以导出为svg格式
前端
我是天龙_绍5 小时前
Easing 曲线 easings.net
前端
知识分享小能手5 小时前
微信小程序入门学习教程,从入门到精通,电影之家小程序项目知识点详解 (17)
前端·javascript·学习·微信小程序·小程序·前端框架·vue
訾博ZiBo5 小时前
React组件复用导致的闪烁问题及通用解决方案
前端
Dever5 小时前
记一次 CORS 深水坑:开启 withCredentials 后Response headers 只剩 content-type
前端·javascript
临江仙4555 小时前
流式 Markdown 渲染在 AI 应用中的应用探秘:从原理到优雅实现
前端·vue.js
Hilaku5 小时前
为什么我开始减少逛技术社区,而是去读非技术的书?
前端·javascript·面试
m0_728033136 小时前
JavaWeb——(web.xml)中的(url-pattern)
xml·前端