「周更第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 链接依赖与缓存失效策略

相关链接

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

相关推荐
Qrun几秒前
Windows11安装nvm管理node多版本
前端·vscode·react.js·ajax·npm·html5
中国lanwp1 分钟前
全局 npm config 与多环境配置
前端·npm·node.js
JELEE.1 小时前
Django登录注册完整代码(图片、邮箱验证、加密)
前端·javascript·后端·python·django·bootstrap·jquery
TeleostNaCl3 小时前
解决 Chrome 无法访问网页但无痕模式下可以访问该网页 的问题
前端·网络·chrome·windows·经验分享
前端大卫4 小时前
为什么 React 中的 key 不能用索引?
前端
你的人类朋友5 小时前
【Node】手动归还主线程控制权:解决 Node.js 阻塞的一个思路
前端·后端·node.js
小李小李不讲道理6 小时前
「Ant Design 组件库探索」五:Tabs组件
前端·react.js·ant design
毕设十刻6 小时前
基于Vue的学分预警系统98k51(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js
mapbar_front7 小时前
在职场生存中如何做个不好惹的人
前端
牧杉-惊蛰7 小时前
纯flex布局来写瀑布流
前端·javascript·css