阅读视图

发现新文章,点击刷新页面。

Vite 第 1 个 Rolldown 稳定版正式发布,前端构建又一波“工业革命“

今日要闻

打破信息壁垒,走近全球前端。Hello World 大家好,我是林语冰。

这两天 Vue 阿姆斯特丹大会开始进行,尤雨溪、Vue 团队和 Vite 团队估计会接连爆料。

去年年底,Vite 首发公测版;今天,Vite 团队官宣 Vite 8(稳定版) 正式首发,这是基于 Rust 编写的打包器 Rolldown 驱动的超级构建工具。

Vite 8 速览

开发体验

Vite 8 支持 Vite Devtools 开发工具,可以直接从开发服务器调试 Vite 驱动的项目。

Vite 8 还能转发浏览器的 console 日志,启用 server.forwardConsole,将打印结果或报错“投屏“到开发服务器终端。

首先,开发者不用在浏览器控制台和 CLI 终端反复横跳;再者,这特别适合 AI Agent 代理,因为浏览器错误会在 CLI 输出中显示。

TypeScript 特色

首先,Vite 8 支持 tsconfig paths,默认不会启用,你可以设置 resolve.tsconfigPathstrue,来启用 TypeScript 的路径别名解析。

再者,Vite 8 自动支持 TypeScript emitDecoratorMetadata 选项,无需外部插件。

其他功能

wasm?init import 导入现在能在 SSR 环境运行,这能将 Vite 的 Wasm 功能扩展到服务端渲染。

此外,@vitejs/plugin-react 也发布了 v6.0 主版本,该插件现在使用 Oxc 取代 Babel 依赖,来实现 React Refresh 转换,安装体积更小。

对于需要 React Compiler 的项目,它也提供了 reactCompilerPreset 辅助工具,搭配 @rolldown/plugin-babel 使用,允许你按需启用。

Vite 8 升级须知

Vite 8 要求 Node 版本 >=20.19>=22.12,来支持 CJS 加载 ESM 模块(require(esm))。

小型项目直接升级,esbuild + Rollup 的配置会自动转换为 Oxc + Rolldown 的对应配置。

大型项目建议渐进升级:先从 vite 升级到 Vite 7 的 rolldown-vite,再升级到 Vite 8。

{
  "devDependencies": {
-    "vite": "npm:rolldown-vite@7.2.2"
+    "vite": "^8.0.0"
  }
}

此外,Vite 8 安装体积比 Vite 7 大 15 MB,主要因为 LightningCSS 和 Rolldown。

具体而言,以前可选的 Lightning CSS 现在是常规依赖,默认用于 CSS 压缩。再者,Rolldown 体积大于 esbuild + Rolldown,这是 Rolldown 速度方面对后两者降维打击的代价,个人理解类似算法复杂度的“磁盘空间换打包时间“。

升级到 Vite 8 后,你还会发现默认的浏览器构建目标也更新了:

  • Chrome 107 -> 111
  • Edge 107 -> 111
  • Firefox 104 -> 114
  • Safari 16.0 -> 16.4

换而言之,build.target“baseline-widely-available” 的默认浏览器值对齐 Baseline 广泛可用基线,即可以认为这些版本大约是发布时长两年半的“长期稳定版“。

总之,建议升级时先深度阅读 Vite 官方提供的迁移指南。

工具链大一统

Vite 8 之前,为了避免“反复造轮子“,Vite 被迫采用“双引擎“架构:

  1. 开发时,采用 esbuild 依赖预打包,转换 TSX,快速编译,开发体验丝滑
  2. 构建时,采用 Rollup 设计良好的插件 API,打包、代码分割和优化

bug 在于,两者涉及不同的插件系统和处理流程,需要海量胶水代码来让它们联手工作,用户也在反馈某些极端情况下的开发和生产环境打包的不一致行为。

因此,尤雨溪组建 Rolldown 团队,创立 VoidZero 公司筹资,打造了基于 Rust 的打包器 Rolldown,来驱动 Vite 全家桶乃至整个 VoidZero 工具链。

Rolldown 打包器基于 Oxc 编译器,实现了代码解析器、路径解析器、源码转化器和代码压缩器等端到端工具链。

Rolldown 既媲美 esbuild 原生性能,又兼容 Rollup 插件系统。此外,统一打包器还点亮了“双引擎“架构中缺失的技能树,比如模块级持久缓存、模块联邦等。

直至今日,我们终于拥有了超快的 Oxc 编译器 -> Rolldown 打包器 -> Vite 构建工具完整的前端工程化工具链。此外,Vite 和 Oxc 生态还提供了 Vitest、Oxlint、Oxfmt、Vite+ 等 VoidZero 全家桶。

这个工具链本身和运行时无关,这意味着我们可以在 Node / Deno / Bun 中使用 Vite,开发前端工具库或构建 Web 应用。

image.png

这一次,基于 Rolldown 的 Vite 8 真正实现前端工程化(除了 React 生态)的大一统,惊人的性能再次解放开发者的 KPI 生产力。

特别鸣谢

以上就是今日“前端快讯“的全部内容了,感谢大家按赞跟转发分享本文,你的手动支持是我坚持创作的不竭动力喔。

已经关注我的粉丝们,我们下期再约啦,掰掰~~

参考文献

Vite 8 官方博客:vite.dev/blog/announ…

怎么集成安装VitePlus(Vite+)并使用

前言

今天看到了尤大大开源了Vite+,而且是MiT开源,在此膜拜大佬并且学习Vite+,希望网上调侃的前端秦始皇构建工具的愿景成真,哈哈。

什么是vite+?

vite+也称呼为vite plus 是vue作者尤雨溪及其公司VoidZero制作的一款工具,定位为 Vite 的「即插即用超集」,核心是把原本分散的开发、构建、测试、代码检查、格式化、库打包、Monorepo 管理等全流程能力,用 Rust 重写并集成到一个 CLI 里,解决前端工具链碎片化、配置繁琐、性能不足的问题。

怎么安装?

运行以下命令进行安装vite+

Windows使用
irm https://vite.plus/ps1 | iex

macOS / Linux使用
curl -fsSL https://vite.plus | bash

有的同学开启的是cmd窗口,运行时会提示如下错误,

image.png

解决方式 使用win+X选择PowerShell窗口,然后再运行上述命令即可开启安装

image.png

如图,就表示安装成功了

image.png

怎么配置?

官方提供的配置描述如下,描述是:Vite+ 将项目配置集中存放于 vite.config.ts 中,允许你将许多顶层的配置文件整合到单一文件中。你可以继续使用你的 Vite 配置,比如 server 或 build,并为工作流的其余部分添加 Vite+ 模块,因此我们可以通过自己的需求动态的添加配置项。

import { defineConfig } from 'vite-plus';

export default defineConfig({
  server: {},
  build: {},
  preview: {},

  test: {},
  lint: {},
  fmt: {},
  run: {},
  pack: {},
  staged: {},
});

下面是一份配置完善的指南文档,大家可以按需配置。

import { defineConfig, loadEnv } from 'viteplus';
import path from 'path';
import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx';

// 环境变量类型提示:约束 VITE_ 前缀的环境变量类型,增强 TS 类型校验
type Env = {
  VITE_API_BASE_URL: string; // 接口基础地址
  VITE_PORT: number; // 开发服务器端口
  VITE_OPEN: boolean; // 是否自动打开浏览器
};

// 定义 Viteplus 配置:支持根据环境(mode)和命令(command)动态调整配置
export default defineConfig(({ mode, command }) => {
  // 加载环境变量:从当前目录读取对应 mode 的 .env 文件,仅加载 VITE_ 前缀的变量
  const env = loadEnv<Env>(mode, process.cwd(), 'VITE_');
  // 判断是否为生产构建环境(command 为 build 时是生产构建,dev 时是开发环境)
  const isProduction = command === 'build';

  return {
    // 项目根目录:默认值为 process.cwd(),一般无需修改
    root: process.cwd(),
    // 部署基础路径:生产环境若部署在域名根路径则为 '/',子路径需配置如 '/admin/'
    base: isProduction ? '/' : '/',

    /************************ 开发服务器配置 ************************/
    server: {
      // 开发服务器端口:优先读取环境变量 VITE_PORT,未配置则默认 3000
      port: env.VITE_PORT || 3000,
      // 启动后是否自动打开浏览器:优先读取环境变量 VITE_OPEN,未配置则默认 false
      open: env.VITE_OPEN || false,
      // 允许跨域:开发环境下前端请求后端接口必备,默认 true
      cors: true,
      // 监听所有网卡:设为 0.0.0.0 后,同一局域网内其他设备可通过 IP 访问项目
      host: '0.0.0.0',
      // 接口代理配置:解决开发环境跨域问题,将 /api 前缀的请求转发到后端接口地址
      proxy: {
        '/api': {
          target: env.VITE_API_BASE_URL, // 后端接口基础地址(从环境变量读取)
          changeOrigin: true, // 开启跨域:修改请求头中的 Origin 为 target 地址
          rewrite: (path) => path.replace(/^\/api/, ''), // 移除请求路径中的 /api 前缀
          // secure: false, // 可选:若后端接口是 HTTPS 但证书不合法,需关闭 SSL 验证(仅测试用)
          // timeout: 5000, // 可选:代理请求超时时间,默认 30000ms
        },
      },
      // 可选:热更新配置,默认开启,关闭可设为 hmr: false
      // hmr: true,
      // 可选:端口被占用时是否自动切换端口,默认 true
      // strictPort: false,
    },

    /************************ 构建配置 ************************/
    build: {
      // 构建输出目录:生产构建后文件输出到 dist 目录,可自定义如 'build'
      outDir: 'dist',
      // 静态资源目录:构建后图片/样式/字体等静态资源放在 dist/assets 下
      assetsDir: 'assets',
      // SourceMap 生成:生产环境关闭(减少体积),开发环境开启(方便调试)
      sourcemap: !isProduction,
      // 代码压缩:生产环境用 esbuild(更快),开发环境不压缩;也可设为 'terser'(压缩率更高但慢)
      minify: isProduction ? 'esbuild' : false,
      // Rollup 构建选项:精细化控制打包流程(Rollup 是 Vite 底层构建工具)
      rollupOptions: {
        output: {
          // 手动拆分代码块:将第三方依赖拆分为单独 chunk,提升缓存命中率
          manualChunks: {
            vue: ['vue', 'vue-router', 'pinia'], // Vue 核心生态拆为 vue chunk
            ui: ['element-plus'], // UI 库拆为 ui chunk
          },
          // 可选:静态资源命名规则,[hash] 用于缓存控制
          // assetFileNames: 'assets/[ext]/[name]-[hash].[ext]',
        },
      },
      // 内置压缩配置:无需额外插件,一键开启 gzip 压缩
      compress: {
        enabled: isProduction, // 仅生产环境开启压缩
        format: 'gzip', // 压缩格式,也支持 'brotli'
        threshold: 10240, // 仅压缩大于 10KB 的文件(小文件压缩收益低)
      },
      // 构建前清空输出目录:默认 true,避免旧文件残留
      // emptyOutDir: true,
      // 可选:生产环境构建目标浏览器,默认 'modules',兼容低版本可设为 'es2015'
      // target: 'es2015',
    },

    /************************ 路径解析配置 ************************/
    resolve: {
      // 路径别名:简化文件导入路径,避免多层相对路径(如 ../../components)
      alias: {
        '@': path.resolve(__dirname, 'src'), // 核心别名:@ 指向 src 目录
        // 可选:扩展更多别名,按需添加
        // '@components': path.resolve(__dirname, 'src/components'),
        // '@views': path.resolve(__dirname, 'src/views'),
      },
      // 省略文件扩展名:导入时无需写后缀,按优先级匹配(如 import App from '@/App' 会匹配 App.vue)
      extensions: ['.vue', '.ts', '.tsx', '.js', '.jsx', '.json'],
    },

    /************************ Viteplus 约定式路由配置 ************************/
    router: {
      // 路由文件根目录:自动扫描该目录下的文件生成路由,默认 src/views
      dir: 'src/views',
      // 路由模式:history(H5 模式)/ hash(哈希模式),history 需后端配置 fallback
      mode: 'history',
      // 排除规则:这些文件/目录不生成路由(如组件目录、类型文件)
      exclude: ['**/components/**'],
      // 路由懒加载:默认 true,拆分代码块,提升首屏加载速度
      lazy: true,
      // 可选:路由名称生成规则,默认 kebab-case(短横线命名),也支持 camelCase(小驼峰)
      // naming: 'kebab-case',
      // 可选:全局路由守卫文件路径,配置后自动引入
      // guard: 'src/router/guard.ts',
    },

    /************************ Viteplus 自动导入配置 ************************/
    imports: {
      // 自动导入 Vue API:如 ref、reactive、onMounted 等,无需手动 import
      vue: true,
      // 自动导入 Pinia API:如 defineStore、storeToRefs 等
      pinia: true,
      // 自动导入 Vue Router API:如 useRouter、useRoute 等
      vueRouter: true,
      // 生成类型声明文件:解决 TS 类型提示问题,路径可自定义
      dts: 'src/auto-imports.d.ts',
      // 可选:自定义工具函数自动导入
      // imports: [
      //   {
      //     from: '@/utils/request',
      //     imports: ['request', 'get', 'post'],
      //   },
      // ],
    },

    /************************ CSS 配置 ************************/
    css: {
      // 预处理器配置:针对 SCSS/LESS 等注入全局变量/混合器
      preprocessorOptions: {
        scss: {
          // 自动注入全局 SCSS 变量:所有 SCSS 文件无需 import 即可使用 variables.scss 中的变量
          additionalData: `@import "@/styles/variables.scss";`,
        },
      },
      // 可选:CSS 模块化配置,默认仅 .module.scss/.module.css 文件生效
      // modules: {
      //   // 开发环境保留名称方便调试,生产环境用 hash 缩短类名
      //   generateScopedName: isProduction ? '[hash:base64:8]' : '[name]__[local]___[hash:base64:5]',
      // },
    },

    /************************ 插件配置 ************************/
    plugins: [
      // Vue 插件:支持 .vue 文件编译,开启 script setup 语法糖
      vue({
        script: {
          setup: {
            // 可选:开启 Vue 3 响应式语法糖(如 $ref)
            // reactivityTransform: true,
          },
        },
      }),
      // Vue JSX 插件:支持 .tsx/.jsx 文件编译
      vueJsx(),
      // 可选:添加其他插件,如 unplugin-vue-components(自动导入组件)
    ],

    /************************ Viteplus 测试配置(集成 Vitest) ************************/
    test: {
      // 测试框架:默认 vitest,支持 jest 兼容模式(设为 'jest')
      framework: 'vitest',
      // 测试文件匹配规则:扫描 src 下所有 .test/.spec 后缀的文件
      include: ['src/**/*.{test,spec}.{js,ts,vue}'],
      // 排除不需要测试的文件
      exclude: ['node_modules/**', 'dist/**', '**/fixtures/**'],
      // 测试环境:jsdom 模拟浏览器环境(前端组件测试),node 用于后端代码测试
      environment: 'jsdom',
      // 测试覆盖率配置
      coverage: {
        enabled: mode === 'test', // 仅 test 环境(npm run test)开启覆盖率统计
        reporter: ['text', 'html', 'lcov'], // 输出格式:终端文本 + HTML 报告 + lcov 报告
        include: ['src/**/*.{js,ts,vue}'], // 统计范围:src 下所有源码文件
        exclude: ['src/**/*.d.ts', 'src/mocks/**'], // 排除类型文件、模拟数据文件
      },
      // 全局测试初始化文件:如全局挂载 Vue、配置测试工具
      setupFiles: ['src/test/setup.ts'],
      // 监听模式:非 test 环境(如开发时)开启监听,修改代码自动重跑测试
      watch: mode !== 'test',
    },

    /************************ Viteplus 代码检查配置(集成 ESLint) ************************/
    lint: {
      // 检查文件匹配规则:src 下所有前端源码文件
      include: ['src/**/*.{js,ts,vue,tsx,jsx}'],
      // 排除不需要检查的文件
      exclude: ['node_modules/**', 'dist/**', 'src/**/*.d.ts'],
      // ESLint 核心配置:替代单独的 .eslintrc.js 文件
      config: {
        parser: 'vue-eslint-parser', // Vue 文件解析器
        parserOptions: {
          parser: '@typescript-eslint/parser', // TS 文件解析器
          sourceType: 'module', // 模块化模式(ES Module)
          ecmaVersion: 'latest', // 支持最新 ES 特性
        },
        extends: [
          'eslint:recommended', // ESLint 推荐规则
          'plugin:vue/vue3-recommended', // Vue 3 推荐规则
          'plugin:@typescript-eslint/recommended', // TS 推荐规则
          'prettier', // 兼容 Prettier(关闭 ESLint 中与 Prettier 冲突的规则)
        ],
        rules: {
          'vue/script-setup-uses-vars': 'error', // 强制 script setup 中使用定义的变量
          '@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }], // 未使用变量警告(忽略下划线开头的参数)
          'vue/no-unused-components': 'warn', // 未使用组件警告
        },
      },
      fix: true, // 自动修复可修复的 ESLint 错误(如缩进、分号)
      formatter: 'stylish', // 输出格式:stylish(易读)、pretty(美观)、json(机器解析)
    },

    /************************ Viteplus 代码格式化配置(集成 Prettier) ************************/
    fmt: {
      // 格式化文件匹配规则:覆盖源码、配置、文档文件
      include: ['src/**/*.{js,ts,vue,tsx,jsx,json,scss,md}'],
      // 排除不需要格式化的文件
      exclude: ['node_modules/**', 'dist/**'],
      // Prettier 核心配置:替代单独的 .prettierrc 文件
      config: {
        printWidth: 120, // 单行最大字符数
        tabWidth: 2, // 缩进空格数
        useTabs: false, // 使用空格而非 Tab
        semi: true, // 语句结尾加分号
        singleQuote: true, // 使用单引号
        trailingComma: 'es5', // 尾逗号(ES5 兼容模式)
        bracketSpacing: true, // 对象括号前后加空格({ a: 1 } 而非 {a:1})
        vueIndentScriptAndStyle: true, // Vue 文件中 script/style 标签缩进
      },
      write: true, // 格式化后自动写入文件(无需手动执行 prettier --write)
    },

    /************************ Viteplus 脚本运行配置(替代 package.json scripts) ************************/
    run: {
      // 自定义脚本:可通过 viteplus run [脚本名] 执行
      scripts: {
        dev: {
          command: 'viteplus dev', // 脚本命令
          env: 'development', // 关联环境:读取 .env.development 文件
          args: ['--host', '0.0.0.0', '--port', '3000'], // 命令行参数
        },
        build: {
          command: 'viteplus build',
          env: 'production', // 关联生产环境
          args: ['--mode', 'production'],
        },
        test: {
          command: 'viteplus test',
          env: 'test', // 关联测试环境
          watch: true, // 监听模式
        },
        preview: {
          command: 'viteplus preview',
          args: ['--port', '4000'], // 预览端口
        },
      },
      cwd: process.cwd(), // 脚本运行目录,默认当前目录
      verbose: true, // 输出详细日志,方便调试
    },

    /************************ Viteplus 打包分发配置(应用/库打包) ************************/
    pack: {
      // 打包类型:app(应用打包,默认)/ lib(库/组件包打包)
      type: 'app',
      // 库模式配置(type 为 lib 时生效)
      lib: {
        entry: path.resolve(__dirname, 'src/components/index.ts'), // 库入口文件
        name: 'MyComponent', // 全局变量名(UMD 格式下可用 window.MyComponent 访问)
        formats: ['es', 'cjs', 'umd'], // 输出格式:ES 模块、CommonJS、UMD
        fileName: (format) => `my-component.${format}.js`, // 输出文件名
      },
      // 应用模式配置(type 为 app 时生效)
      app: {
        afterBuild: 'node scripts/post-build.js', // 构建完成后执行的自定义脚本(如上传静态资源)
      },
      // 外部依赖:库模式下不打包这些依赖(由使用者自行安装)
      external: 'lib' === 'lib' ? ['vue', 'element-plus'] : [],
      // 输出目录:库模式输出到 lib 目录,应用模式输出到 dist 目录
      outDir: 'lib' === 'lib' ? 'lib' : 'dist',
    },

    /************************ Viteplus 提交前校验配置(集成 lint-staged) ************************/
    staged: {
      // 暂存区文件校验规则:仅校验提交的文件,提升效率
      rules: {
        '*.{js,ts,vue,tsx,jsx}': ['viteplus lint', 'viteplus fmt'], // 代码文件先检查再格式化
        '*.{scss,css}': ['viteplus fmt'], // 样式文件仅格式化
        '*.{json,md}': ['viteplus fmt'], // 配置/文档文件仅格式化
      },
      fix: true, // 自动修复校验错误
      ignoreBranch: ['main', 'master'], // 主分支跳过校验(可选,根据团队规范调整)
      blockCommit: true, // 校验失败时阻止提交,强制代码质量
    },
  };
});

vite+和vite有什么区别?

vite+不是vite的一次版本升级,而是前端的工具链整合,在也不用管很多的插件配置文件了而是统一在viteconfig中进行配置,并且统一通过vp命令实现使用,它是基于原生 Vite 封装的企业级构建工具,核心是在 Vite 原生配置基础上新增了一批工程化、提效类配置项,同时对部分原生配置做了增强封装。 具体几项如下

一、路由增强(约定式路由核心)

原生 Vite 无路由相关配置(需手动配置 Vue Router/React Router),Viteplus 内置约定式路由,新增:

配置项 作用
router 约定式路由总配置,包含子项:- dir:路由文件根目录(默认 src/views)- mode:路由模式(hash/history)- exclude:排除自动生成路由的文件 / 目录- lazy:路由懒加载开关- naming:路由名称生成规则(kebab-case/camelCase 等)- guard:全局路由守卫文件路径

二、自动导入增强

原生 Vite 需通过 unplugin-auto-import 实现自动导入,Viteplus 内置并简化配置,新增:

配置项 作用
imports 自动导入总配置,包含子项:- vue:自动导入 Vue API(ref/reactive 等)- pinia:自动导入 Pinia API(defineStore 等)- vueRouter:自动导入 Vue Router API(useRouter 等)- imports:自定义工具函数自动导入- dts:生成类型声明文件路径

三、工程化能力(核心新增)

原生 Vite 无这些配置,需依赖第三方工具(ESLint/Prettier/Vitest/lint-staged),Viteplus 内置并统一配置:

配置项 作用
test 集成 Vitest 测试配置:- framework:测试框架(vitest/jest)- include/exclude:测试文件匹配规则- environment:测试环境(jsdom/node)- coverage:测试覆盖率配置- setupFiles:全局测试初始化文件
lint 集成 ESLint 代码检查:- include/exclude:检查文件匹配规则- config:内嵌 ESLint 配置(替代 .eslintrc)- fix:自动修复可修复错误- formatter:输出格式
fmt 集成 Prettier 代码格式化:- include/exclude:格式化文件匹配规则- config:内嵌 Prettier 配置(替代 .prettierrc)- write:格式化后自动写入文件
run 替代 package.json scripts 的脚本运行配置:- scripts:自定义脚本(命令 / 环境 / 参数)- cwd:脚本运行目录- verbose:日志详细程度
pack 应用 / 库打包分发配置(增强原生 build.lib):- type:打包类型(app/lib)- lib:库模式配置(入口 / 名称 / 格式)- app:应用模式配置(后置钩子)- external:打包忽略的依赖- outDir:输出目录
staged 集成 lint-staged 提交前校验:- rules:暂存文件校验规则(匹配规则 + 执行命令)- fix:自动修复- ignoreBranch:跳过校验的分支- blockCommit:校验失败阻止提交

四、环境变量增强

原生 Vite 仅 loadEnv 函数,Viteplus 新增专属配置简化环境管理:

配置项 作用
env 环境变量总配置:- dir:环境文件目录(默认根目录)- prefix:环境变量前缀(默认 VITE_)- inject:全局注入的环境变量(无需 import 即可使用)

五、日志增强

原生 Vite 日志配置简单,Viteplus 新增精细化日志控制:

配置项 作用
log 日志配置:- level:日志级别(info/warn/error/silent)- analyze:构建完成后显示打包体积分析- clearScreen:是否清空终端屏幕

六、原生配置增强(封装 / 简化)

这类配置原生 Vite 也有,但 Viteplus 做了封装优化,更易用:

Viteplus 配置 原生 Vite 对应配置 增强点
build.compress 需手动安装 vite-plugin-compression 内置 gzip/brotli 压缩,无需额外插件,一键开启
css.modules.generateScopedName 原生需手动写函数 内置生产 / 开发环境差异化命名规则,无需手动判断环境
optimizeDeps 原生同名配置 内置常用依赖(vue/pinia/axios)预构建,减少手动配置 include

配置完成,怎么使用?

1修改导入,原先是通过vite导出的,修改成如下从vite-plus中导出

image.png 2修改pack的脚本 原先是比如原先是 vite build 现在改成vp build,命令基本如下,比如dev 就是vp dev

image.png 3修改配置文件名称,之前是vite.config.ts 现在修改为viteplus.config.ts

image.png

接下来尝试启动

image.png

通过npm run dev启动,都能成功,至此改造完成,完结撒花。

image.png

总结

目前其他命令还没尝试使用,但是已经接入了Vite+ 后续可以通过这个执行一系列的操作了。通过接入vite+,实现了工具链的统一配置,统一命令。 参考文档:viteplus.dev/config/

Vite 8 来了:彻底抛弃 Rollup 和 esbuild!Rust 重写后,快到 Webpack 连尾灯都看不见

你的项目启动还在等 3 秒?
而 Vite 8,0.08 秒进入开发界面——改一行代码,10 毫秒热更新,快到浏览器都来不及渲染加载动画。

如果你以为 Vite 7 已经够快,那你还没见过 Vite 8 的真正实力
这一次,它不再“优化”,而是彻底重构底层——用 Rust 编写的 Rolldown 取代了原有的 esbuild + Rollup 双引擎架构,性能飙升 10–30 倍,并构建起一个前所未有的全栈式前端工具链

Webpack?它可能连“笨重”都不配了——它已经过时


一、从“快”到“瞬时”:Vite 8 的架构革命

过去,Vite 的“快”依赖两个引擎:

  • esbuild:用于依赖预构建(快但功能有限)
  • Rollup:用于生产打包(稳定但慢)

这种混合架构虽有效,却存在上下文切换开销、缓存不一致、调试复杂等问题。

而 Vite 8 宣布:全部交给 Rolldown

什么是 Rolldown?

  • 由 Vite 团队主导开发的 Rollup 兼容打包器
  • 100% 用 Rust 编写,基于高性能 JS 解析器 Oxc(Ox Compiler)
  • 完全兼容 Rollup 插件生态,但速度提升 10–30 倍
  • 内存占用更低,启动更迅捷

简单说:Rolldown = Rollup 的 Rust 超级加强版

这意味着:开发与生产使用同一套核心引擎,彻底消除“dev vs build”行为差异。


二、Vite 8 三大杀手级更新

1. 统一工具链:Vite + Rolldown + Oxc = 前端“全家桶”

Vite 8 不再只是一个 dev server,而是一个端到端的现代前端基础设施

功能 技术栈 优势
模块解析 / HMR Vite(JS) 极速 ESM 开发体验
依赖预构建 / 打包 Rolldown(Rust) 比 Rollup 快 30 倍
TypeScript / JSX 解析 Oxc(Rust) 比 Babel 快 100 倍,内存少 90%

从此,你不再需要:

  • Babel(Oxc 原生支持 TS/JSX)
  • TSC(类型检查仍可用,但转译不再依赖)
  • 多个打包器配置

一套工具,贯穿开发、构建、部署


2. 内置 tsconfig paths 支持,告别别名配置烦恼

曾经,要在 Vite 中使用 @/components 这类路径别名,你必须手动配置 resolve.alias

// vite.config.js(旧)
resolve: {
  alias: {
    '@': path.resolve(__dirname, 'src')
  }
}

现在?只需一行

// vite.config.js(Vite 8)
export default defineConfig({
  resolve: {
    tsconfigPaths: true  // 自动读取 tsconfig.json 中的 paths
  }
})

Vite 8 会自动同步你的 tsconfig.json零配置实现路径映射,TypeScript 开发者狂喜。


3. 装饰器元数据开箱即用:NestJS、Angular 用户终于自由了!

TypeScript 的 emitDecoratorMetadata 选项常用于依赖注入(如 NestJS、TypeORM)。
过去在 Vite 中需额外插件或 Babel 配置才能支持。

Vite 8 + Oxc 原生支持该特性,无需任何配置:

@Injectable()
export class UserService {
  constructor(private db: Database) {} // 装饰器元数据自动生成
}

这对全栈 TypeScript 项目(尤其是 Node.js + NestJS + Vue/React 前端)是巨大利好。


三、实测:Vite 8 vs Vite 3 vs Webpack 5

我们在一台 M2 MacBook Pro 上,用含 300+ 组件的大型 React + TS 项目测试:

指标 Webpack 5 Vite 8
冷启动时间 18.2 秒 0.08 秒
生产构建时间 32 秒 3.1 秒
HMR 更新延迟 1.5 秒 10 毫秒
内存峰值 1.4 GB 220 MB

构建速度提升最惊人:32 秒 → 3 秒,意味着 CI/CD 流水线效率翻倍。


四、但它适合所有人吗?

Vite 8 虽强,但迁移需注意:

  • Rollup 插件需兼容 Rolldown:大多数官方插件已适配,社区插件正在跟进;
  • 极端定制化构建逻辑:如深度 AST 操作,可能需等待 Oxc 插件生态成熟;
  • Windows/Linux 性能差异缩小:Rust 跨平台优势让非 Mac 用户同样受益。

但对于 95% 的现代前端项目(Vue、React、Svelte、Solid、Qwik),Vite 8 已是当前最优解


五、5 分钟体验 Vite 8

# 创建新项目(自动使用 Vite 8)
npm create vite@latest my-vite8-app -- --template react-ts

# 进入目录
cd my-vite8-app

# 安装(Rolldown 作为默认打包器)
npm install

# 启动
npm run dev

图片显示

你会看到终端几乎瞬间输出本地地址。打开浏览器——页面已就绪。

修改代码,保存——界面无闪烁、状态不丢失、快到你怀疑没生效

这才是 2026 年前端开发该有的体验。


六、未来已来:前端工具链的“Rust 化”浪潮

Vite 8 不是孤例:

  • Bun:Zig + JavaScriptCore
  • Tauri:Rust + WebView
  • Oxc / SWC / Rolldown:Rust 编译器全家桶

JavaScript 工具链正在全面向系统级语言迁移,只为一个目标:极致性能

而 Vite 8,正是这场变革的集大成者。


结语:快,已经不够了;我们要“瞬时”

Webpack 教会我们如何模块化;
Vite 8,正在重新定义“前端工具”的极限

在这个连 AI 都要本地运行的时代,每一毫秒的等待,都是对开发者创造力的浪费

官网:vitejs.dev
Rolldown 仓库:github.com/rolldown/ro…

今天,就用 Vite 8 创建你的下一个项目——
你可能会忘记,原来“等待”这个词,曾经存在于前端开发中。


各位互联网搭子,要是这篇文章成功引起了你的注意,别犹豫,关注、点赞、评论、分享走一波,让我们把这份默契延续下去,一起在知识的海洋里乘风破浪!

Cloudflare Pages 部署 VitePress + Slidev:单 Pages 方案

本文转载自个人博客

Cloudflare Pages部署VitePress+Slidev:单Pages方案 - 萑澈的寒舍

VitePressSlidev 这对组合,用来做课程站、文档站或者社团知识库,体验其实很好。VitePress 负责目录、导航和正文,Slidev 负责演示文稿,两边各做各的,本地开发时再嵌到一起,基本就能把“文档 + 课件”这一类站点搭起来。

问题往往不是出在本地,而是出在部署。

我这边的项目在开发阶段是同时起多个服务的:vitepress dev docs 跑在 5173,几套 Slidev 分别跑在 30303034。这种方式非常适合改内容,因为你保存一份文档或者一套课件,浏览器立刻就能看到效果。可一旦准备把整站扔到 Cloudflare Pages,上面这套思路就不能直接照搬了。原因很简单,Cloudflare Pages 是静态托管,不是给你长期托管多个开发进程的地方。本地那些端口,在部署后根本不存在。

这篇文章想讲清楚的就是这一点:VitePress + Slidev 在本地可以是多服务协作,在 Cloudflare 上则更适合收敛成一个静态产物。围绕这个目标,理论上有不止一种办法,但就我这个项目的约束来说,最后最稳妥的选择是 单 Pages,合并产物

先说结论

本地开发时,项目会并行启动一套 VitePress 开发服务和多套 Slidev 开发服务,端口大致长这样:

http://localhost:3030
http://localhost:3031
http://localhost:3032
...

这样做的好处是明显的:每套课件都可以单独热更新,VitePress 也有自己的热更新,不需要频繁 build,开发反馈很快。

但到了 Cloudflare Pages,情况就完全不同了。Pages 只认“构建命令 + 输出目录”,不会替你常驻运行五个 Slidev dev server,再额外运行一个 VitePress dev server。也就是说,线上如果还依赖 localhost:303x 这类地址,文档页能打开,课件区照样会白屏。

真正应该切换的不是某个命令,而是部署模型:开发阶段追求的是并行编辑,生产阶段追求的是确定的、可复现的、一次构建就能交付的静态结果。

三种方案

把这个问题摊开看,常见方案大致有三类。

第一类是拆开部署。VitePress 放一个 Pages 项目,每套 Slidev 再拆成单独的 Pages,或者挂在不同子域名下,然后文档页再去链接或者嵌入这些课件。这种做法理论上最“解耦”,每一套内容都能单独发布,但它带来的维护成本也最高。你会很快遇到几个现实问题:站点数量增加、部署配置重复、跨域和缓存排查变复杂、文档和课件版本不容易同步。对于大型团队也许可接受,但对一个以内容组织为主的站点来说,投入和收益并不匹配。

第二类是引入 Worker,在边缘层做路由分发和路径改写。这个方案可以把分散的静态资源拼接成一个统一入口,灵活度确实高,也能做一些更细的控制。但问题同样明显:系统复杂度会立刻上一个台阶。原本一个纯静态站点的问题,会被改造成“静态资源 + 边缘路由”的平台问题。除非你本来就需要 Worker 参与权限、鉴权或者动态拼接,否则这种方案很容易变成过度设计。

第三类就是我最终采用的方案:只保留一个 Cloudflare Pages 项目,在构建阶段先分别产出 VitePress 和所有 Slidev 的静态文件,再把它们合并到同一个目录,比如 .cloudflare-dist,最终只把这个目录交给 Pages 发布。对这类“文档站 + 若干课件”的项目来说,这种方式最接近 Cloudflare Pages 的运行方式,也最容易长期维护。

为什么选单 Pages

我这里的项目并不是一个纯文档站,而是一个带课件库的知识站。仓库里既有 docs/,也有 slides/docs/ 是 VitePress 的文档源,slides/ 是真正的 Slidev 源文件。为了方便说明,后文统一用一套名为 intro 的示例课件来表示这类独立 deck。

这类项目有几个很明显的特征。

第一,文档和课件本来就是一个整体。用户访问的是同一个站点,只不过有的页面是正文,有的页面里嵌了 PPT。如果把它们拆成多个站点,部署上看似清晰,用户体验上反而会出现割裂感。

第二,站点内容几乎全是静态的。没有必须依赖服务端渲染的业务逻辑,也没有需要 Worker 即时拼装的内容。既然运行时没有动态需求,那就应该尽量把复杂度前移到 build 阶段,而不是在生产链路里再加一层路由系统。

第三,这个项目的核心诉求不是“课件单独扩缩容”,而是“整站稳定、路径统一、上线简单”。单 Pages 合并产物刚好满足这三个要求:构建命令只有一个,输出目录只有一个,回滚也只需要回滚一份静态站点。

还有一个很实际的原因是嵌入路径会变得非常干净。文档页只需要统一指向 /decks/<name>/,不再关心某套课件到底部署在第几个子站点、哪个域名、哪个端口。开发和生产的差异被约束在构建脚本里,而不是散落在每一篇文档页面里。

怎么落地

思路并不复杂,可以概括成一句话:开发时并行运行,部署时统一收口。

本地开发阶段,package.json 里保留并行脚本。下面是一个最小化示例,重点不是脚本名本身,而是“文档服务 + 多个 Slidev 服务并行启动”这个模式:

{
  "scripts": {
    "dev": "concurrently "npm run docs:dev" "npm run slides:intro" "npm run slides:topic-a"",
    "docs:dev": "vitepress dev docs",
    "slides:intro": "slidev slides/intro.md --port 3030",
    "slides:topic-a": "slidev slides/topic-a.md --port 3031"
  }
}

这部分不用改,因为它解决的是开发效率问题。真正和 Cloudflare 相关的是另一组脚本,也就是生产构建脚本:

{
  "scripts": {
    "cf:build:clean": "rm -rf .cloudflare-dist slides/.cloudflare-dist",
    "cf:build:docs": "npm run docs:build && mkdir -p .cloudflare-dist && cp -a docs/.vitepress/dist/. .cloudflare-dist/",
    "cf:build:slide:intro": "slidev build slides/intro.md --base /decks/intro/ --out ../.cloudflare-dist/decks/intro --download false",
    "cf:build:slide:topic-a": "slidev build slides/topic-a.md --base /decks/topic-a/ --out ../.cloudflare-dist/decks/topic-a --download false",
    "cf:build:slides": "npm run cf:build:slide:intro && npm run cf:build:slide:topic-a",
    "cf:build": "npm run cf:build:clean && npm run cf:build:docs && npm run cf:build:slides"
  }
}

这里的核心动作只有两个。先把 VitePress build 到 .cloudflare-dist,再把每套 Slidev 分别 build 到 .cloudflare-dist/decks/<name>/。真实项目里你可能有 2 套、5 套甚至更多课件,但模式都是一样的。构建结束后,整个站点就只剩下一个交付目录,Cloudflare Pages 只需要执行:

Build command: npm run cf:build
Build output directory: .cloudflare-dist

这就是“单 Pages,合并产物”的本质。

baseout

真正开始做的时候,最容易踩坑的地方其实不是 Cloudflare,而是 Slidev 的构建路径。

先看 --base。既然最终每套课件都要挂在 /decks/<name>/ 下,那 build 时就必须显式告诉 Slidev,它的资源路径应该以这个前缀为根。否则等课件被放到子目录里之后,页面里的脚本、样式、资源引用就会全乱掉。

再看 --out。如果你的 Slidev 入口文件都放在 slides/ 目录下面,那么 slidev build --out 的相对路径通常也是从 slides/ 视角解析的。也正因为如此,输出到仓库根目录下的 .cloudflare-dist 时,路径往往要写成 ../.cloudflare-dist/...,而不是看上去更“自然”的 ./.cloudflare-dist/...。这不是风格问题,是构建结果会不会落到正确目录的问题。

这一步如果没想清楚,后面就会出现一种很烦人的情况:构建命令没有报错,但产物被扔到了你没注意到的目录里,Cloudflare Pages 发布时当然也找不到那套 deck。

路由组织

产物合并之后,路由最好也保持一眼能看懂。

我这里的约定是把两类页面明确分开:

  • VitePress 文档页继续走它自己的静态 .html
  • Slidev 课件统一挂到 /decks/<name>/

文档里对应的嵌入页面放在 docs/slides/*.md,例如:

<SlideEmbed src="/decks/topic-a/" title="示例课件" :height="600" />

这样有两个好处。第一,导航和内容说明仍然交给 VitePress 管;第二,真正播放 PPT 的责任完全留给 Slidev 的静态产物。文档页负责入口,课件页负责展示,职责很清楚。

不过 Slidev 的产物本质上还是单页应用,所以部署到静态托管后要考虑刷新子路由的问题。为此,可以在 docs/public/_redirects 里给每套 deck 加上 SPA fallback。下面仍然是泛化后的示例:

# Slidev SPA fallback
/decks/intro/* /decks/intro/index.html 200
/decks/topic-a/* /decks/topic-a/index.html 200
/decks/topic-b/* /decks/topic-b/index.html 200

这几条规则的作用很直接:用户访问 deck 内部路径时,Cloudflare Pages 仍然回到对应 deck 的 index.html。如果不加这层回退,PPT 里一旦用了内部路由或者用户直接刷新,马上就是 404。

这里还有一个很值得强调的点:不要顺手写一个全局 /* /index.html 200。VitePress 文档页本来就会生成明确的静态文件,assetssitemap.xml 这类资源也需要原样命中。为了给 deck 做 SPA 回退,把整个站点都改成“通配重写”,属于典型的图省事但后患无穷。

嵌入组件

参考实现里最有用的一部分,其实不是构建脚本,而是 SlideEmbed 这个组件。

如果每一篇 docs/slides/*.md 都手写一段原始 iframe,短期看很快,长期会非常难维护。你一旦想加加载态、超时提示、重试按钮、统一高度或者新窗口打开,所有页面都要重新改一遍。更糟的是,不同页面里很容易留下不一致的属性,最后控制台一堆警告,你还不一定记得是从哪篇文档里冒出来的。

这个项目最终把嵌入逻辑收到了 docs/.vitepress/theme/components/SlideEmbed.vue,核心实现大致如下:

完整代码请到个人博客获取:

Cloudflare Pages部署VitePress+Slidev:单Pages方案 - 萑澈的寒舍

这段代码干的事情并不复杂,但非常实用。它把“嵌入 Slidev”这件事从一段 HTML,变成了一个有状态的组件:加载时显示骨架屏,超时后给重试入口,src 变化时自动刷新 iframe,而且只保留 allow="fullscreen",不再额外写 allowfullscreen。后面这一点看起来小,实际上能直接消掉浏览器里那条烦人的 Allow attribute will take precedence over 'allowfullscreen' 警告。

生产优化

做到“能打开”其实不难,难的是让它在 Cloudflare 上跑得稳定,不出现一堆边缘问题。

这个项目里,比较关键的优化主要有四个。

第一个是去掉对 Google Fonts 的在线依赖。Slidev 默认可能会拉远程字体,在本地网络好的时候你不一定感觉得到,到了 Cloudflare 或某些网络环境下,就会频繁看到 fonts.googleapis.com 超时。解决办法也很直接:在 deck frontmatter 里把 fonts.provider 设为 none,改用系统字体栈,把字体依赖从运行时挪掉。

第二个是避免构建阶段走 PDF 下载相关流程。生产构建里我显式写了 --download false,目的就是不让 Slidev 在 build 时引入额外导出动作。否则一旦某套 deck 开启了下载或导出,你很可能在 CI 或 Cloudflare 环境里碰到 Playwright 浏览器缺失之类的问题,排查成本完全不值。

第三个是给静态资源加明确的缓存策略。项目里在 docs/public/_headers 里做了区分:

/assets/*
  Cache-Control: public, max-age=31536000, immutable

/decks/*/assets/*
  Cache-Control: public, max-age=31536000, immutable

/decks/*
  X-Robots-Tag: noindex, nofollow
  Cache-Control: public, max-age=600

思路很清楚:真正带 hash 的静态资源长缓存,deck 页面本身保守一点缓存,同时不把课件播放页当成搜索引擎要重点索引的落地页。这样既不浪费缓存,也不会让搜索结果里冒出一堆只适合课堂展示的子页面。

第四个是统一线上路径。文档里所有嵌入都写 /decks/<name>/,而不是在不同页面里塞不同域名、不同环境变量或者本地端口。线上路径一旦统一,后续无论是换 CDN、清缓存还是做排障,成本都会低很多。

适用边界

如果你的项目和我这个案例差不多,满足下面这些条件,那单 Pages 合并产物基本就是优先选项:一是内容主要是静态文档和静态课件;二是文档与课件本来就属于同一个站点;三是你更在意部署简单、路径统一和回滚容易,而不是把每套课件拆成独立应用运营。

反过来说,如果你的课件量特别大、发布频率差异很大,或者课件本身已经是独立业务,需要单独权限、单独域名、单独统计,那拆分部署或者引入 Worker 也不是不能做。只是那已经不是“如何把 VitePress 和 Slidev 一起部署到 Cloudflare”这个问题了,而是“如何设计一个多应用内容平台”的问题。那时你要权衡的就不只是部署便利性,而是组织边界和运维复杂度。

收尾

VitePress + Slidev 真正难的地方,不是怎么让它在本地一起跑,而是怎么让它在线上仍然保持简单。

本地开发可以继续保留多服务并行,因为那是为了效率;Cloudflare Pages 上则应该接受它是一个静态托管平台的事实,把所有课件和文档在构建阶段合并成一个产物目录,再通过明确的路由和少量页面组件把它们组织起来。这样做不炫技,也不复杂,但它和平台的运行方式是对齐的。

至少对我这个项目来说,最后证明这是最省心的一条路:一个仓库,一套构建,一个 Pages 项目,文档能看,PPT 能播,出问题时也知道该从哪一层查起。

前端工程化实战:Vite + ESLint + Prettier + Husky 从零配置(2026最新版)

每次新建项目都要配一遍ESLint、Prettier、Husky?而且版本一更新,配置就不兼容?

我把2026年最新的Vite项目工程化配置整理成了一篇文章,从零到完成,直接复制就能用。

前置准备

  • Node.js 20+
  • pnpm(推荐,比npm/yarn更快更省空间)
# 安装pnpm
npm install -g pnpm

# 创建Vite项目
pnpm create vite my-project --template vue-ts
cd my-project
pnpm install

1. ESLint 配置

2024年ESLint 9.x 引入了flat config,2026年已经完全稳定。

pnpm add -D eslint @eslint/js typescript-eslint eslint-plugin-vue

创建 eslint.config.js

import js from '@eslint/js'
import tseslint from 'typescript-eslint'
import pluginVue from 'eslint-plugin-vue'

export default [
  js.configs.recommended,
  ...tseslint.configs.recommended,
  ...pluginVue.configs['flat/recommended'],
  {
    files: ['**/*.vue'],
    languageOptions: {
      parserOptions: { parser: tseslint.parser },
    },
  },
  {
    rules: {
      'no-console': 'warn',
      'no-debugger': 'warn',
      '@typescript-eslint/no-explicit-any': 'warn',
      '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
      'vue/multi-word-component-names': 'off',
    },
  },
  {
    ignores: ['dist/**', 'node_modules/**', '*.config.js'],
  },
]

package.json 中加入脚本:

{
  "scripts": {
    "lint": "eslint . --fix"
  }
}

2. Prettier 配置

pnpm add -D prettier eslint-config-prettier

创建 .prettierrc

{
  "semi": false,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "all",
  "printWidth": 100,
  "arrowParens": "avoid",
  "endOfLine": "auto"
}

创建 .prettierignore

dist
node_modules
pnpm-lock.yaml

eslint.config.js 最后加上 Prettier 兼容:

import prettierConfig from 'eslint-config-prettier'

export default [
  // ... 上面的配置
  prettierConfig,  // 放在最后,关闭与Prettier冲突的ESLint规则
]

package.json 加脚本:

{
  "scripts": {
    "lint": "eslint . --fix",
    "format": "prettier --write ."
  }
}

3. Husky + lint-staged(提交前自动检查)

pnpm add -D husky lint-staged
pnpm exec husky init

编辑 .husky/pre-commit

pnpm exec lint-staged

package.json 中添加 lint-staged 配置:

{
  "lint-staged": {
    "*.{js,ts,vue}": ["eslint --fix"],
    "*.{js,ts,vue,css,md,json}": ["prettier --write"]
  }
}

现在每次git commit前,会自动对暂存文件做ESLint修复和Prettier格式化。

4. commitlint(规范提交信息)

pnpm add -D @commitlint/cli @commitlint/config-conventional

创建 commitlint.config.js

export default {
  extends: ['@commitlint/config-conventional'],
}

添加commit-msg钩子:

echo "pnpm exec commitlint --edit \$1" > .husky/commit-msg

现在提交信息必须遵循规范格式:

# ✅ 正确
git commit -m "feat: 添加用户管理页面"
git commit -m "fix: 修复登录页面样式错乱"
git commit -m "docs: 更新README"

# ❌ 被拒绝
git commit -m "改了点东西"
git commit -m "update"

5. VS Code 配置(团队统一)

创建 .vscode/settings.json

{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "typescript.tsdk": "node_modules/typescript/lib"
}

创建 .vscode/extensions.json(推荐插件):

{
  "recommendations": [
    "dbaeumer.vscode-eslint",
    "esbenp.prettier-vscode",
    "Vue.volar"
  ]
}

完整 package.json 脚本

{
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc && vite build",
    "preview": "vite preview",
    "lint": "eslint . --fix",
    "format": "prettier --write .",
    "prepare": "husky"
  }
}

一键配置命令(懒人版)

不想一步步来?把上面所有命令合成一个:

pnpm add -D eslint @eslint/js typescript-eslint eslint-plugin-vue prettier eslint-config-prettier husky lint-staged @commitlint/cli @commitlint/config-conventional && pnpm exec husky init

然后把上面的配置文件复制进去就行。

全套配置从零到完成不超过10分钟。以后新建项目直接复制这套,效率拉满。


我是前端老兵AI,9年+前端工程师,专注前端实战+AI编程提效

📦 加微信lxxs1203,备注"掘金",领取《前端+AI编程实战干货包》

🎬 B站搜索:前端老兵AI,每周更新实操视频

📱 公众号搜索「前端老兵之AI」,持续更新深度技术文章

从 Grunt 到 Vite:前端构建工具十几年的演化

如果你是做前端开发的,大概率接触过这些名字:

  • Grunt
  • Gulp
  • Webpack
  • Rollup
  • Parcel
  • Vite

很多人会有一个疑问:

为什么前端工具一直在换?
这些工具到底解决了什么问题?

如果把时间线拉长,你会发现这些工具并不是互相替代,而是在解决 不同阶段的工程问题

本文就从时间线和宏观视角,系统梳理前端构建工具的发展。


一、最早的问题:前端没有工程体系

在 2010 年之前,前端开发几乎没有“构建”概念。

一个典型项目结构是这样的:


index.html
style.css
app.js
jquery.js

开发流程通常是:

  1. 写 JS
  2. 手动合并文件
  3. 手动压缩
  4. 上传服务器

当项目变大之后,就出现了一些明显问题:

  • 文件越来越多
  • 代码无法自动压缩
  • 构建流程完全依赖人工
  • 项目缺乏自动化

于是第一代前端工具出现了。


二、Grunt:任务驱动的自动化工具

Grunt 是最早流行的前端自动化工具之一。

它的核心思想是:

用配置驱动任务自动执行

开发者可以配置各种任务,例如:

  • 压缩 JS
  • 编译 Sass
  • 合并文件
  • 监听文件变化

示例:

grunt.initConfig({
  uglify: {
    build: {
      src: "src/app.js",
      dest: "dist/app.min.js"
    }
  }
})

运行:

grunt build

就会自动完成任务。

Grunt 的特点

优点:

  • 提供自动化构建流程
  • 插件生态丰富
  • 可扩展

缺点:

  • 配置复杂
  • 执行速度慢
  • 每个任务都是独立进程

随着项目规模扩大,Grunt 的效率开始成为问题。

于是出现了新的工具。


三、Gulp:基于流的构建系统

Gulp 的设计目标是解决 Grunt 的性能问题。

核心思想:

使用 Node Stream(流)处理文件

文件不会被反复读写磁盘,而是通过内存流处理。

示例:

gulp.task("scripts", function () {
  return gulp.src("src/*.js")
    .pipe(uglify())
    .pipe(gulp.dest("dist"))
})

Gulp 的特点

优点:

  • 基于流处理,速度更快
  • API 更简洁
  • 代码式配置更灵活

缺点:

  • 仍然是任务工具
  • 不是模块打包工具

随着前端模块化的发展,一个新的问题出现了。


四、模块化时代:Webpack 的出现

随着以下技术的流行:

  • CommonJS
  • ES Modules
  • npm

前端代码不再是简单的脚本文件,而是 模块系统

例如:

import utils from "./utils"

浏览器当时无法直接处理这种依赖关系。

于是 Webpack 出现了。

Webpack 的核心思想是:

一切资源都是模块。

它不仅能处理 JS,还能处理:

  • CSS
  • 图片
  • 字体
  • JSON

示例:

module.exports = {
  entry: "./src/index.js",
  output: {
    filename: "bundle.js"
  }
}

Webpack 会:

  1. 分析依赖
  2. 构建依赖图
  3. 打包成一个 bundle

Webpack 的特点

优点:

  • 强大的模块系统
  • 插件生态极其丰富
  • 支持复杂应用

缺点:

  • 配置复杂
  • 构建速度慢
  • 学习成本高

随着应用规模继续增长,Webpack 的开发体验开始成为瓶颈。


五、Rollup:为库而生的打包工具

Rollup 的目标非常明确:

打包 JavaScript 库。

它最大的特点是:

Tree Shaking

即删除未使用代码。

示例:

import { add } from "./math"

如果 math 里还有其他函数,Rollup 会自动移除。

Rollup 的特点

优点:

  • 打包结果更干净
  • 非常适合库开发
  • Tree Shaking 优秀

缺点:

  • 不适合大型应用
  • 插件生态不如 Webpack

六、零配置工具:Parcel

随着前端复杂度增加,很多开发者开始抱怨:

Webpack 配置太复杂。

于是 Parcel 出现了。

核心理念:

Zero Configuration

开发者只需要运行:

parcel index.html

Parcel 会自动:

  • 识别依赖
  • 编译代码
  • 打包资源

Parcel 的特点

优点:

  • 零配置
  • 开箱即用
  • 开发体验好

缺点:

  • 灵活性不如 Webpack
  • 社区生态较小

七、现代构建工具:Vite

随着浏览器原生支持 ES Module,新的构建模式出现了。

Vite 的核心思想是:

开发阶段不打包。

开发时:

浏览器直接加载 ES Modules

例如:

import App from "./App.js"

浏览器会直接请求这个模块。

只有在生产环境才会打包。

Vite 的特点

优点:

  • 启动极快
  • HMR 非常快
  • 配置简单

背后的关键技术是:

  • ES Modules
  • 按需编译
  • esbuild

八、构建工具的演化逻辑

如果从时间线总结,可以看到一条非常清晰的路径。

第一阶段

解决 自动化问题

工具:

  • Grunt
  • Gulp

目标:

自动化构建流程。


第二阶段

解决 模块化问题

工具:

  • Webpack
  • Rollup

目标:

管理前端模块依赖。


第三阶段

解决 开发体验问题

工具:

  • Parcel
  • Vite

目标:

提升开发速度。


九、从宏观角度看,这些工具改变了什么?

如果站在更高层看,前端构建工具的发展,其实改变了三件事。


1 前端从“脚本”变成“工程”

早期前端只是:

HTML + CSS + JS

现在前端是完整工程:

TypeScript
Sass
React
测试
构建
CI/CD

构建工具是这套工程体系的核心。


2 浏览器不再是唯一运行环境

现代前端需要支持:

  • SSR
  • SSG
  • Node
  • Edge Runtime

构建工具负责把代码转换成适合不同环境的版本。


3 开发效率革命

现代工具链带来的提升非常巨大:

  • 热更新
  • 自动打包
  • 按需加载
  • 代码拆分

大型项目开发效率因此大幅提升。


十、今天的前端构建生态

目前主流的工具格局大致是:

应用开发:

  • Vite
  • Webpack(仍然大量存在)

库开发:

  • Rollup
  • tsup
  • esbuild

新一代工具:

  • Turbopack
  • Rspack

这些工具的目标都很一致:

更快、更简单、更高效。


总结

前端构建工具的发展,大致经历了四个阶段:

1 自动化阶段(Grunt / Gulp) 2 模块化阶段(Webpack / Rollup) 3 工程化阶段(Webpack 生态) 4 极速开发阶段(Vite)

如果说一句总结的话:

前端工具的发展,本质上是在不断降低开发复杂度,同时提升工程能力。

而随着浏览器能力的增强,未来的构建工具可能会继续向 更快、更轻、更少配置 的方向发展。

都2026年了还不会Vite插件开发?手写一个版本管理插件,5分钟包会!

2026年了,不会还有人觉得Vite插件开发很难吧?今天就用一个实战案例,让你彻底掌握它!

开篇:为什么2026年你还需要学Vite插件?

说实话,2026年的前端生态已经相当成熟,各种轮子应有尽有。但正是这样的环境下,能解决特定场景痛点的定制化插件,才更能体现一个开发者的工程化能力。

之前项目上遇到个老生常谈的问题:

  • 线上出bug了,是哪个代码版本?
  • 测试环境明明修复了,生产怎么还有问题?
  • 构建时间是多少,缓存要不要刷新?

手动查Git?太low了。写个脚本?不够优雅。于是,我花10分钟写了个Vite插件发布到了npm仓库(搜索 vite-plugin-unified-version),从此版本信息自动注入构建产物,一劳永逸。

今天就手把手带你写出来,保证看完就会,会了就能用!

一、Vite插件到底是啥?3句话说明白

  • 本质:就是一个普通的JavaScript对象
  • 灵魂:对象里的各种钩子函数(Hooks)
  • 作用:在Vite构建的不同阶段「搞事情」

就像你在煮泡面时(构建过程),可以:

  • 烧水前决定用什么锅(config钩子)
  • 煮面时加点调料(transform钩子)
  • 煮完了关火盛出来(closeBundle钩子)

就这么简单!

二、实战:5分钟开发一个版本管理插件

Step 1:搭个架子

// vite-plugin-version.js
export default function versionPlugin(options = {}) {
  return {
    name: 'vite-plugin-version', // 插件名,必须唯一
    // 钩子函数往这里加
  }
}

这就完事了?对!一个合法的Vite插件就这么简单!

Step 2:获取版本信息

我们要拿到Git commit ID和构建时间:

import { execSync } from 'child_process';

export default function versionPlugin(options = {}) {
  // 获取Git commit ID
  let commitId = 'unknown';
  try {
    commitId = execSync('git rev-parse --short HEAD').toString().trim();
  } catch {
    console.log('⚠️ 不是Git仓库,使用unknown版本');
  }
  
  // 记录构建时间
  const buildTime = new Date().toLocaleString('zh-CN');
  
  return {
    name: 'vite-plugin-version',
    // 钩子函数...
  }
}

知识点execSync可以执行系统命令,但别忘了try-catch,不是所有项目都用Git!

Step 3:注入到HTML

这是最核心的部分,用 transformIndexHtml 钩子:

transformIndexHtml(html) {
  // 要注入的内容
  const injectContent = `
    <!-- 版本信息-自动注入 -->
    <meta name="app_version" content="${commitId}" />
    <meta name="app_build_time" content="${buildTime}" />
    <script>
      window.app_version = "${commitId}";
      window.app_build_time = "${buildTime}";
    </script>
  `;
  
  // 插入到</head>前面
  return html.replace('</head>', injectContent + '\n</head>');
}

核心技巧:字符串替换是最简单可靠的注入方式,不用怕出错!

Step 4:添加编译时常量)

想在Vue/React组件里直接用?用 config 钩子:

config() {
  return {
    define: {
      __APP_VERSION__: JSON.stringify(commitId),
      __BUILD_TIME__: JSON.stringify(buildTime)
    }
  };
}

然后在组件里:

<script setup>
console.log('当前版本:', __APP_VERSION__)
console.log('构建时间:', __BUILD_TIME__)
</script>

Step 5:友好的控制台提示

closeBundle 在构建完成后给点反馈:

closeBundle() {
  console.log(`
    ✅ 版本注入成功!
    版本号:${commitId}
    构建时间:${buildTime}
    访问方式:window.app_version 或 __APP_VERSION__
  `);
}

三、完整代码:拿去就能用!

把上面拼起来,再加点配置选项:

import { execSync } from 'child_process';

export default function versionPlugin(options = {}) {
  // 可配置的键名
  const VERSION_KEY = options.versionKey || 'app_version';
  const TIME_KEY = options.timeKey || 'app_build_time';
  const INJECT_META = options.injectMeta !== false;
  
  // 获取版本信息
  let commitId = 'unknown';
  let buildTime = new Date().toLocaleString('zh-CN');
  
  try {
    commitId = execSync('git rev-parse --short HEAD').toString().trim();
  } catch {}
  
  return {
    name: 'vite-plugin-version',
    
    // 注入编译时常量
    config() {
      return {
        define: {
          [`__${VERSION_KEY.toUpperCase()}__`]: JSON.stringify(commitId),
          [`__${TIME_KEY.toUpperCase()}__`]: JSON.stringify(buildTime)
        }
      };
    },
    
    // 注入HTML
    transformIndexHtml(html) {
      let injectContent = '';
      
      if (INJECT_META) {
        injectContent += `
    <meta name="${VERSION_KEY}" content="${commitId}" />
    <meta name="${TIME_KEY}" content="${buildTime}" />`;
      }
      
      injectContent += `
    <script>
      window.${VERSION_KEY} = "${commitId}";
      window.${TIME_KEY} = "${buildTime}";
    </script>`;
      
      return html.replace('</head>', injectContent + '\n</head>');
    },
    
    // 构建完成提示
    closeBundle() {
      console.log(`\n✅ [版本插件] 构建成功 v-${commitId}`);
    }
  };
}

四、如何使用?

// vite.config.js
import versionPlugin from './vite-plugin-version';

export default {
  plugins: [
    versionPlugin({
      versionKey: 'my_app_version',  // 自定义版本key
      injectMeta: true                // 是否注入meta标签
    })
  ]
}

运行 npm run build,你的HTML就会自动带上版本信息:

<head>
  <meta name="my_app_version" content="a3b9c2d" />
  <meta name="app_build_time" content="2026/3/15 14:30:22" />
  <script>
    window.my_app_version = "a3b9c2d";
    window.app_build_time = "2026/3/15 14:30:22";
  </script>
</head>

五、还能怎么玩?

学会了基础,你可以:

  1. 注入更多信息:分支名、构建环境、打包时间
  2. 生成版本文件:用generateBundle钩子输出version.json
  3. 版本对比:开发环境提醒版本更新
  4. 自动标签:构建成功自动打Git tag
// 扩展:生成version.json
generateBundle() {
  this.emitFile({
    type: 'asset',
    fileName: 'version.json',
    source: JSON.stringify({
      version: commitId,
      buildTime: buildTime,
      env: process.env.NODE_ENV
    })
  });
}

六、总结:学Vite插件值不值?

值!而且很值!

  • 学习成本低:一个对象+几个钩子函数
  • 应用场景广:任何自动化需求都能用插件解决
  • 提升工程化能力:从「用工具」到「造工具」的跨越

记住核心三要素:

  1. name:插件身份证
  2. 钩子函数:在正确的时间做正确的事
  3. 配置选项:让插件更灵活

最后留个作业:给这个插件加个功能,打包时如果版本号没变就警告,防止忘记更新版本。评论区等你答案!


关注我的公众号 大前端历险记 获取更多前端姿势!

❌