vite-plugin-fake-server require 函数问题深度分析报告
报告生成时间: 2025-10-10
分析工具: Claude Code
插件版本: vite-plugin-fake-server@2.2.0
项目: vue-pure-admin@6.1.0
问题概述
在使用 vue-pure-admin 作为二次开发模板时,当 vite-plugin-fake-server 的配置项 enableProd: true 时,生产环境构建打包后的应用会出现 require is not defined 错误,导致应用无法正常运行。
环境信息
- 项目模板: vue-pure-admin v6.1.0
- 插件版本: vite-plugin-fake-server v2.2.0
- Vite 版本: v7.1.9
- Node 版本要求: ^20.19.0 || >=22.13.0
- 问题配置:
enableProd: true
问题现象
构建产物检测结果
在 apps/dist/static/js/index-DbKXK31u.js 中发现以下关键内容:
存在
__VITE__PLUGIN__FAKE__SERVER__对象- 包含插件元数据
- 包含配置选项(
enableProd: true)
存在 xhook 相关代码
window.__VITE__PLUGIN__FAKE__SERVER__.xhook
存在未转换的 require 函数调用
- 在浏览器环境中直接使用了 Node.js 的
require函数 - 导致运行时错误:
ReferenceError: require is not defined
- 在浏览器环境中直接使用了 Node.js 的
问题根源分析
1. 插件工作机制
当 enableProd: true 时,vite-plugin-fake-server 在生产环境构建过程中会执行以下操作:
源码位置:node_modules/vite-plugin-fake-server/dist/index.mjs
第 637-677 行:buildPackage 函数
const require = createRequire(import.meta.url); // Node.js 环境的 require
async function buildPackage(packageName) {
const camelCasePackageName = packageName.replace(/-\w/g, (str) => str[1].toUpperCase());
const result = await build({
configFile: false,
build: {
commonjsOptions: {
strictRequires: "auto", // 尝试自动处理 CommonJS
},
write: false,
lib: {
entry: require.resolve(packageName), // 使用 Node.js 的 require.resolve
name: camelCasePackageName,
formats: ["iife"], // 输出为 IIFE 格式
fileName: packageName,
},
rollupOptions: {
output: {
exports: "named",
extend: true,
},
},
minify: false,
},
});
const _result = Array.isArray(result) ? result[0] : result;
if (!("output" in _result)) {
return;
}
// 将构建结果包装后返回
return `window.__VITE__PLUGIN__FAKE__SERVER__.${camelCasePackageName} = (function() { ${_result.output[0].code} return this.${camelCasePackageName}; }).apply({});`;
}第 1996-2079 行:transformIndexHtml hook
transformIndexHtml: {
order: "pre",
handler: async (htmlString) => {
if (isDevServer || !opts.enableProd) {
return htmlString; // 开发环境或 enableProd=false 时不处理
}
// ... 其他代码 ...
// 关键:构建 path-to-regexp 包并注入到页面
const pathToRegexpContent = await buildPackage("path-to-regexp");
scriptTagList.push({
...scriptTagOptions,
children: `${pathToRegexpContent}` // 直接注入构建结果
});
// ... 其他代码 ...
}
}2. 问题发生的具体流程
┌─────────────────────────────────────────────────────────────────┐
│ 1. Vite 开始生产环境构建 │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 2. vite-plugin-fake-server 检测到 enableProd: true │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 3. 在 transformIndexHtml hook 中调用 buildPackage() │
│ - 目标:将 path-to-regexp 打包成浏览器可用的代码 │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 4. buildPackage 使用 Vite build API 构建 path-to-regexp │
│ - 入口:require.resolve("path-to-regexp") │
│ - 格式:iife (立即执行函数) │
│ - CommonJS 配置:strictRequires: "auto" │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 5. 【问题点】path-to-regexp 包含 CommonJS require 调用 │
│ - Vite 的 strictRequires: "auto" 未能完全转换 │
│ - 构建产物中仍然保留了 require() 函数调用 │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 6. 将构建结果注入到 index.html 的 <script> 标签中 │
│ window.__VITE__PLUGIN__FAKE__SERVER__.pathToRegexp = ... │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ 7. 浏览器加载页面并执行 JavaScript │
│ ✗ require is not defined (浏览器环境无 require 函数) │
└─────────────────────────────────────────────────────────────────┘3. 为什么会出现 require 函数
原因 A:path-to-regexp 依赖问题
path-to-regexp@8.2.0 可能包含或依赖了使用 CommonJS 格式的代码:
// 可能存在类似这样的代码
const someModule = require("some-dependency");原因 B:Vite 构建配置不完善
commonjsOptions: {
strictRequires: "auto"; // "auto" 模式在某些情况下无法处理所有 require
}Vite 6 将 strictRequires 的默认值从 "auto" 改为 true,插件使用了 "auto" 配置,这在某些边缘情况下无法正确转换所有的 CommonJS require 调用。
原因 C:缺少 polyfill
插件没有为浏览器环境提供 require 函数的 polyfill,直接假设构建产物可以在浏览器中运行。
4. 当前项目配置
位置:apps/build/plugins.ts:55-60
vitePluginFakeServer({
logger: false,
include: "mock",
infixName: false,
enableProd: true // ← 问题的触发点
}),解决方案
方案 1:禁用生产环境 Mock 功能(强烈推荐)⭐⭐⭐⭐⭐
Mock 功能通常只在开发环境中使用,生产环境不应该包含 mock 数据。
修改配置
// apps/build/plugins.ts
vitePluginFakeServer({
logger: false,
include: "mock",
infixName: false,
enableProd: false, // ← 改为 false
enableDev: true // 保持开发环境可用
}),优点
- ✅ 彻底解决问题:不会再有 require 函数问题
- ✅ 减少打包体积:移除不必要的 mock 相关代码
- ✅ 符合最佳实践:生产环境不应包含测试/开发用的 mock 数据
- ✅ 提高安全性:避免 API 接口结构泄露
- ✅ 提升性能:减少运行时开销
验证修复
cd apps
pnpm build检查构建产物:
# 检查是否还有 require 函数
grep -r "require" dist/static/js/*.js
# 检查是否还有 __VITE__PLUGIN__FAKE__SERVER__
grep -r "__VITE__PLUGIN__FAKE__SERVER__" dist/static/js/*.js如果都没有输出(或只在注释中),说明问题已解决。
方案 2:使用条件构建 ⭐⭐⭐
如果确实需要在某些特殊场景下启用生产环境 mock(如测试环境),可以通过环境变量控制。
修改配置
// apps/build/plugins.ts
vitePluginFakeServer({
logger: false,
include: "mock",
infixName: false,
enableProd: process.env.ENABLE_MOCK_IN_PROD === 'true',
enableDev: true
}),使用方式
# 正常生产构建(不启用 mock)
cd apps
pnpm build
# 带 mock 的生产构建(仅用于测试环境)
ENABLE_MOCK_IN_PROD=true pnpm build优点
- ✅ 灵活性高
- ✅ 默认情况下安全
- ✅ 可用于特殊测试场景
缺点
- ⚠️ 容易误操作(忘记设置环境变量)
- ⚠️ 仍然存在潜在的构建问题
方案 3:升级或降级插件版本 ⭐⭐
检查是否有新版本修复了此问题。
检查更新
cd apps
pnpm outdated vite-plugin-fake-server尝试更新
pnpm update vite-plugin-fake-server@latest或指定版本
pnpm add -D vite-plugin-fake-server@2.1.0 # 尝试其他版本注意事项
- 检查插件的 GitHub Issues
- 查看 Changelog 是否有相关修复
- 测试新版本是否引入其他问题
方案 4:Patch 插件(高级方案)⭐
如果必须在生产环境使用 mock 且其他方案都不可行,可以使用 patch-package 修改插件源码。
安装 patch-package
cd apps
pnpm add -D patch-package修改 package.json
{
"scripts": {
"postinstall": "patch-package"
}
}修改插件源码
修改 node_modules/vite-plugin-fake-server/dist/index.mjs:
// 第 644-646 行,修改 strictRequires
commonjsOptions: {
strictRequires: true; // 改为 true,强制转换所有 require
}创建 patch
pnpm patch-package vite-plugin-fake-server缺点
- ⚠️ 维护成本高
- ⚠️ 升级插件时需要重新创建 patch
- ⚠️ 可能不完全解决问题
替代方案
如果确实需要在生产环境模拟 API,建议使用更成熟的方案:
1. Mock Service Worker (MSW) ⭐⭐⭐⭐⭐
pnpm add -D mswMSW 使用 Service Worker 拦截请求,更适合生产环境测试。
优点:
- ✅ 真正的网络层拦截
- ✅ 不修改业务代码
- ✅ 支持浏览器和 Node.js
- ✅ 社区活跃,文档完善
2. 独立的 Mock 服务器
搭建独立的 Mock API 服务器(如使用 json-server、mockoon 等)。
优点:
- ✅ 完全隔离
- ✅ 更接近真实场景
- ✅ 可以被多个项目共享
3. 后端提供测试环境
让后端团队提供专门的测试环境,而不是在前端使用 mock。
优点:
- ✅ 最真实的测试场景
- ✅ 测试前后端集成
- ✅ 前端无需维护 mock 数据
技术细节深入分析
Vite 的 CommonJS 处理
Vite 使用 @rollup/plugin-commonjs 来处理 CommonJS 模块。strictRequires 选项控制如何处理动态 require:
commonjsOptions: {
// "auto": 自动检测(可能遗漏某些情况)
// true: 严格模式,所有可能的 require 都转换
// false: 宽松模式,只转换明确的 require
strictRequires: "auto";
}Vite 6 改变了默认值:
- Vite 5: 默认
"auto" - Vite 6: 默认
true
插件使用了 "auto",这在 Vite 6+ 中可能导致某些 require 调用未被转换。
path-to-regexp 的模块格式
path-to-regexp@8.2.0 的 package.json:
{
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
}
}该包同时提供 ESM 和 CommonJS 版本,但在某些构建场景下,Vite 可能会引入 CommonJS 版本的依赖。
createRequire 的作用
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);这是 Node.js ESM 中使用 CommonJS require 的官方方式,但这个 require 只能在 Node.js 环境中使用,不能在浏览器中运行。
最佳实践建议
开发环境配置
vitePluginFakeServer({
logger: true, // 开发时显示日志
include: "mock",
infixName: false,
enableDev: true, // 开发环境启用
enableProd: false, // 生产环境禁用
watch: true // 监听 mock 文件变化
}),生产环境策略
- 完全移除 mock(推荐)
- 连接测试后端:配置真实的测试环境 API
- 使用环境变量区分:
// vite.config.ts
export default defineConfig(({ mode }) => {
const isMockEnabled = mode === "staging"; // 只在 staging 模式启用
return {
plugins: [
vitePluginFakeServer({
enableProd: isMockEnabled,
enableDev: true,
}),
],
};
});构建命令:
# 生产环境(无 mock)
pnpm build
# 测试环境(有 mock)
pnpm build:staging // vite build --mode staging项目结构建议
project/
├── apps/
│ ├── mock/ # Mock 数据
│ │ ├── user.ts
│ │ └── api.ts
│ ├── src/
│ │ ├── api/ # API 调用
│ │ └── config/ # 配置文件
│ └── vite.config.ts
└── docs/
└── api-mock.md # Mock 使用文档相关资源
总结
问题本质
vite-plugin-fake-server 在生产环境构建时,尝试将 Node.js 的 path-to-regexp 包动态构建并注入到浏览器代码中,但由于 CommonJS 模块转换不完全,导致最终产物包含浏览器不支持的 require 函数。
推荐方案
强烈建议使用方案 1:将 enableProd 设置为 false。
这是最简单、最安全、最符合最佳实践的解决方案,可以:
- 彻底避免此问题
- 减少生产环境风险
- 提升应用性能
- 减小打包体积
关键配置
// apps/build/plugins.ts
vitePluginFakeServer({
logger: false,
include: "mock",
infixName: false,
enableProd: false, // ← 关键修改
enableDev: true
}),