{"id":"6c23cce7-5be4-475f-bba9-20e95f5d5000","entityType":"agent","slug":"clawhub-yyyyzj-vue2-vite-upgrade","name":"Vue 2 项目 Webpack 升级到 Vite 构建工具","canonicalUrl":"https://www.xpersona.co/agent/clawhub-yyyyzj-vue2-vite-upgrade","canonicalPath":"/agent/clawhub-yyyyzj-vue2-vite-upgrade","generatedAt":"2026-10-10T21:53:44.356Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T17:57:38.468Z","emptyReason":null},"description":"将 Vue 2 + Webpack 项目升级到 Vite 构建工具。当用户需要将 Vue 2 项目从 Webpack 迁移到 Vite、优化构建速度、解决 Webpack 配置问题，或处理 Vue CLI/Webpack 与 Vite 环境变量兼容时使用此技能。触发场景包括：(1) 用户提到\"升级到 Vite\"、...","descriptionLabel":"Source description","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.3K downloads reported by the source. Last updated 10/10/2026.","installCommand":"clawhub skill install s176a2rfmghg3asnngmysp6vyx86jr9c:vue2-vite-upgrade","sourceUrl":"https://clawhub.ai/yyyyzj/vue2-vite-upgrade","homepage":"https://clawhub.ai/yyyyzj/skills/vue2-vite-upgrade","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/yyyyzj/vue2-vite-upgrade","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/yyyyzj/skills/vue2-vite-upgrade","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":62,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Vue 2 项目 Webpack 升级到 Vite 构建工具 technical dossier on Xpersona with agent coverage, OPENCLEW support, and live trust metadata."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-10T17:57:38.468Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T17:57:38.468Z","emptyReason":null},"stars":null,"forks":null,"downloads":1307,"packageName":null,"latestVersion":"1.0.1","tractionLabel":"1.3K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T17:57:38.468Z","emptyReason":null},"lastUpdatedAt":"2026-10-10T17:57:38.468Z","lastCrawledAt":"2026-10-10T17:57:38.468Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-11T17:57:38.468Z","lastVerifiedAt":null,"highlights":[{"version":"1.0.1","createdAt":"2026-05-18T02:49:50.618Z","changelog":"Version 1.0.1 - 新增阶段 2 删除操作前的安全检查，包括要求在项目根目录、Git 状态干净并获得用户确认，优先使用 git rm。 - 阶段 4 安装依赖步骤细化，要求优先手动排查 peer dependency 冲突，再考虑使用 --force，并关注 lockfile 变更。 - 部署脚本安全更新：禁止在部署脚本中硬编码凭证，必须通过环境变量传递，并在生产部署前需显式确认。 - 文档表述更明确，强调数据安全流程，优化升级操作规范。","fileCount":7,"zipByteSize":17303},{"version":"1.0.0","createdAt":"2026-05-12T05:25:30.693Z","changelog":"Initial release – provides a comprehensive guide and step-by-step workflow for migrating Vue 2 + Webpack projects to Vite. - Outlines five migration phases: project analysis, file/config cleaning, code migration, testing/validation, and optional deployment setup. - Details environment variable compatibility and upgrade best practices. - Emphasizes maintaining Git backups and clear user communication during the upgrade process. - Includes reference links for common issues, migration steps, and configuration examples. - Specifies key changes in dependency management, config files, code adaptations, and axios security review.","fileCount":6,"zipByteSize":14521}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s176a2rfmghg3asnngmysp6vyx86jr9c:vue2-vite-upgrade","setupComplexity":"low","setupSteps":["Install using `clawhub skill install s176a2rfmghg3asnngmysp6vyx86jr9c:vue2-vite-upgrade` in an isolated environment before connecting it to live workloads.","No published capability contract is available yet, so validate auth and request/response behavior manually.","Review the upstream CLAWHUB listing at https://clawhub.ai/yyyyzj/vue2-vite-upgrade before using production credentials."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-yyyyzj-vue2-vite-upgrade/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-yyyyzj-vue2-vite-upgrade/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-yyyyzj-vue2-vite-upgrade/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-yyyyzj-vue2-vite-upgrade/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-yyyyzj-vue2-vite-upgrade/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-yyyyzj-vue2-vite-upgrade/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T21:53:44.355Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-yyyyzj-vue2-vite-upgrade/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-yyyyzj-vue2-vite-upgrade/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-yyyyzj-vue2-vite-upgrade/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-yyyyzj-vue2-vite-upgrade/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-10T17:57:38.468Z","emptyReason":null},"readme":"Skill: Vue 2 项目 Webpack 升级到 Vite 构建工具\n\nOwner: yyyyzj\n\nSummary: 将 Vue 2 + Webpack 项目升级到 Vite 构建工具。当用户需要将 Vue 2 项目从 Webpack 迁移到 Vite、优化构建速度、解决 Webpack 配置问题，或处理 Vue CLI/Webpack 与 Vite 环境变量兼容时使用此技能。触发场景包括：(1) 用户提到\"升级到 Vite\"、...\n\nTags: latest:1.0.1\n\nVersion history:\n\nv1.0.1 | 2026-05-18T02:49:50.618Z | user\n\nVersion 1.0.1\n\n- 新增阶段 2 删除操作前的安全检查，包括要求在项目根目录、Git 状态干净并获得用户确认，优先使用 git rm。\n- 阶段 4 安装依赖步骤细化，要求优先手动排查 peer dependency 冲突，再考虑使用 --force，并关注 lockfile 变更。\n- 部署脚本安全更新：禁止在部署脚本中硬编码凭证，必须通过环境变量传递，并在生产部署前需显式确认。\n- 文档表述更明确，强调数据安全流程，优化升级操作规范。\n\nv1.0.0 | 2026-05-12T05:25:30.693Z | user\n\nInitial release – provides a comprehensive guide and step-by-step workflow for migrating Vue 2 + Webpack projects to Vite.\n\n- Outlines five migration phases: project analysis, file/config cleaning, code migration, testing/validation, and optional deployment setup.\n- Details environment variable compatibility and upgrade best practices.\n- Emphasizes maintaining Git backups and clear user communication during the upgrade process.\n- Includes reference links for common issues, migration steps, and configuration examples.\n- Specifies key changes in dependency management, config files, code adaptations, and axios security review.\n\nArchive index:\n\nArchive v1.0.1: 7 files, 17303 bytes\n\nFiles: references/common-issues.md (14748b), references/deploy-config.md (3482b), references/migration-steps.md (5663b), references/vite-config.md (4124b), skill-card.md (2614b), SKILL.md (6399b), _meta.json (136b)\n\nFile v1.0.1:SKILL.md\n\n---\nname: vue2-vite-upgrade\ndescription: 将 Vue 2 + Webpack 项目升级到 Vite 构建工具。当用户需要将 Vue 2 项目从 Webpack 迁移到 Vite、优化构建速度、解决 Webpack 配置问题，或处理 Vue CLI/Webpack 与 Vite 环境变量兼容时使用此技能。触发场景包括：(1) 用户提到\"升级到 Vite\"、\"迁移到 Vite\"、\"Webpack 转 Vite\"；(2) 用户抱怨 Webpack 构建慢、热更新慢；(3) 用户想要现代化 Vue 2 项目的构建工具；(4) 用户遇到 Webpack 配置复杂难以维护的问题；(5) 用户询问如何提升 Vue 2 项目的开发体验；(6) 用户提到 process.env、VUE_APP_*、import.meta.env、VITE_* 等环境变量兼容。\n---\n\n# Vue 2 项目升级到 Vite 构建工具\n\n## 重要提示\n\n⚠️ **在开始之前，必须确认用户已经备份了代码或使用了 Git 版本控制！**\n\n### 前置安全检查\n\n阶段 2 包含删除操作（`rm -rf` / `git rm`），执行前必须确认：\n\n1. 当前在项目根目录（`ls package.json` 存在）\n2. Git 状态干净或业务代码已提交/stash（`git status`）\n3. 用户已明确确认可以执行删除\n4. 删除操作优先使用 `git rm`（可通过 `git restore` 恢复），非 Git 项目才使用 `rm`\n\n## 执行原则\n\n1. **使用 TodoWrite 工具**：创建完整的任务列表，实时更新进度\n2. **分阶段执行**：按照阶段顺序执行，每个阶段完成后标记为完成\n3. **遇到问题及时沟通**：使用 AskUserQuestion 询问用户\n4. **保留业务依赖**：只删除 Webpack/Babel 相关依赖\n\n## 执行流程概览\n\n| 阶段 | 内容 |\n|------|------|\n| 阶段 1 | 项目分析 - 分析现有项目结构、依赖和配置 |\n| 阶段 2 | 文件清理与配置 - 删除 Webpack 配置，创建 Vite 配置 |\n| 阶段 3 | 代码迁移 - 修改代码以适配 Vite |\n| 阶段 4 | 测试与验证 - 安装依赖并测试运行 |\n| 阶段 5 | 部署配置（可选）- 配置自动化部署脚本 |\n\n## 参考文档\n\n- **Vite 配置模板**：见 [references/vite-config.md](references/vite-config.md)\n- **常见问题处理**：见 [references/common-issues.md](references/common-issues.md)\n- **部署配置**：见 [references/deploy-config.md](references/deploy-config.md)\n\n---\n\n## 阶段 1：项目分析\n\n使用 TodoWrite 创建任务列表，然后依次执行：\n\n1. 分析 package.json（识别业务依赖、Webpack 依赖、需升级依赖）\n2. 提取环境变量（从 config/*.env.js 转换为 .env.* 格式）\n3. 检查代理配置（记录 proxyTable）\n4. 检查特殊功能（SVG Sprite、JSX、动态路由）\n5. 单独审计 `axios`：核对官方 release/security 公告，避免继续保留已披露漏洞版本；同时排查上传、导出、401/403 拦截和裸 `axios` 调用\n\n详细步骤见 [references/migration-steps.md](references/migration-steps.md)\n\n---\n\n## 阶段 2：文件清理与配置\n\n1. 删除 Webpack 相关文件（build/、config/、.babelrc 等）\n2. 更新 package.json（添加 type: module，替换 scripts，更新依赖）\n3. 创建 vite.config.js（使用 [references/vite-config.md](references/vite-config.md) 模板）\n4. 创建环境变量文件（.env.development、.env.staging、.env.production）\n\n详细步骤见 [references/migration-steps.md](references/migration-steps.md)\n\n---\n\n## 阶段 3：代码迁移\n\n1. 修改 index.html（移动到根目录，添加 `<script type=\"module\">`）\n2. 修改 main.js（使用 render 函数，导入加 .vue 扩展名）\n3. 迁移环境变量：优先使用 import.meta.env；如项目存在并行上线或老分支合并，需要保留 process.env.VUE_APP_* 静态写法兼容层\n4. 处理静态资源（static → public，~@ → @，global → window）\n5. 修改组件导入（所有 .vue 导入加扩展名）\n6. 修复样式（/deep/ 和 >>> → ::v-deep）\n\n详细步骤见 [references/migration-steps.md](references/migration-steps.md)\n\n---\n\n## 环境变量兼容约定\n\n迁移期如果可能有 Webpack/Vue CLI 写法的需求先合入生产，不要只做 `process.env -> import.meta.env` 的单向替换。\n\n1. `.env.*` 中新增变量优先使用 `VITE_` 前缀。\n2. Vite 代码推荐使用 `import.meta.env.VITE_*`。\n3. Webpack/Vue CLI 历史代码允许继续使用 `process.env.VUE_APP_*`。\n4. 在 `vite.config.js` 中用 `loadEnv(mode, process.cwd(), '')` 读取变量，并自动把 `VITE_XXX` 映射为 `process.env.VUE_APP_XXX`。\n5. 如果 `.env.*` 已存在 `VUE_APP_XXX`，也要原样注入为 `process.env.VUE_APP_XXX`。\n6. `define` 使用精确 key，不整体替换 `process.env`，减少对第三方库和动态访问的影响。\n7. 兼容层只保证静态写法，如 `process.env.VUE_APP_BASE_API`；不承诺兼容 `process.env[key]`。\n\n具体配置示例见 [references/vite-config.md](references/vite-config.md)。\n\n---\n\n## 阶段 4：测试与验证\n\n1. 安装依赖：先执行 `pnpm install`；如遇 peer dependency 冲突，优先手动排查并解决版本冲突，确认无法解决后再使用 `pnpm install --force`（`--force` 会忽略 lockfile，需检查 `pnpm-lock.yaml` 变更）\n2. 启动开发服务器：`pnpm run dev`\n3. 验证功能：页面显示、样式、图标、路由、API 请求\n4. 构建测试：`pnpm run build:stage && pnpm run preview`\n\n**遇到问题查看 [references/common-issues.md](references/common-issues.md)**\n\n---\n\n## 阶段 5：部署配置（可选）\n\n如需自动化部署，参考 [references/deploy-config.md](references/deploy-config.md)：\n\n1. 创建压缩脚本（export-zip.cjs）\n2. 创建 FTP 部署脚本（ftp-deploy.js）—— **部署凭证通过环境变量传入，禁止硬编码**\n3. 添加部署 NPM 脚本\n4. 安装依赖：`pnpm add -D archiver ftp-deploy rimraf`\n5. 执行生产部署前需显式确认\n\n---\n\n## 升级要点速查\n\n| 类别 | 修改内容 |\n|------|----------|\n| 依赖管理 | 保留业务依赖，删除 Webpack/Babel，升级 Vue 到 2.7.x；`axios` 使用当前 1.x 安全补丁版本，不保留已披露漏洞版本 |\n| 配置文件 | 创建 vite.config.js 和 .env.* 文件 |\n| 代码修改 | .vue 导入加扩展名，环境变量优先迁移到 import.meta.env，必要时兼容 process.env.VUE_APP_* |\n| 语法替换 | require → import，global → window，~@ → @ |\n| axios 改造 | 排查 `CancelToken`、`headers.post['Content-Type']`、裸 `axios` 调用、上传下载与登录态拦截 |\n\nFile v1.0.1:_meta.json\n\n{\n  \"ownerId\": \"kn7ff3q7khffkrpd622drjz91n86k0sv\",\n  \"slug\": \"vue2-vite-upgrade\",\n  \"version\": \"1.0.1\",\n  \"publishedAt\": 1779072590618\n}\n\nFile v1.0.1:references/common-issues.md\n\n# 常见问题处理\n\n在升级过程中如果遇到以下错误，按照对应方案处理。\n\n## 目录\n\n- [问题 1：require is not defined](#问题-1require-is-not-defined)\n- [问题 2：找不到模块](#问题-2找不到模块cannot-find-module)\n- [问题 3：process is not defined](#问题-3process-is-not-defined)\n- [问题 4：global is not defined](#问题-4global-is-not-defined)\n- [问题 5：图片 require 报错](#问题-5图片-require-报错)\n- [问题 6：环境变量判断不生效](#问题-6环境变量判断不生效)\n- [问题 7：SCSS :export 不工作](#问题-7scss-export-导出变量不工作)\n- [问题 8：JSX 报错](#问题-8vue-文件中使用-jsx-报错)\n- [问题 9：HTMLCollection 没有 forEach](#问题-9htmlcollection-没有-foreach-方法)\n- [问题 10：导航守卫重定向报错](#问题-10vue-router-导航守卫重定向报错)\n- [问题 11：Element-UI 样式丢失](#问题-11element-ui-样式丢失)\n- [问题 12：SVG Sprite 图标不显示](#问题-12svg-sprite-图标不显示)\n- [问题 13：热更新不生效](#问题-13热更新-hmr-不生效)\n- [问题 14：生产构建路径错误](#问题-14生产构建后路径错误)\n- [问题 15：path 模块不可用](#问题-15nodejs-path-模块在浏览器不可用)\n- [问题 16：axios 安全漏洞](#问题-16axios-版本存在安全漏洞)\n- [问题 17：浏览器兼容性](#问题-17浏览器兼容性问题需要支持旧浏览器)\n- [问题 18：router.app.$store 为 undefined](#问题-18routerappstore-为-undefined)\n- [问题 19：chunk 体积过大](#问题-19构建后-chunk-体积过大警告)\n- [问题 20：路由懒加载](#问题-20路由组件未实现懒加载导致主包过大)\n- [问题 21：静态资源 404](#问题-21静态资源-404-错误publicdir-配置)\n- [问题 22：正则匹配 null](#问题-22正则匹配结果为-null-导致运行时报错)\n- [问题 23：动态导入冲突](#问题-23动态导入与静态导入冲突警告)\n\n---\n\n## 问题 1：require is not defined\n\n**原因：** Vite 不支持 CommonJS 的 require 语法\n\n**解决方案：**\n\n1. 将 CommonJS 依赖加入 `vite.config.js` 的 `optimizeDeps.include`\n2. 如果是代码中使用了 require，改为 import 语法\n\n---\n\n## 问题 2：找不到模块（Cannot find module）\n\n**原因：** 导入路径缺少 `.vue` 扩展名，或 Vite 默认不自动补全扩展名\n\n**解决方案：**\n\n**方案一：配置 `resolve.extensions`（推荐，批量解决）**\n\n在 `vite.config.js` 中添加：\n\n```javascript\nresolve: {\n  extensions: ['.js', '.vue', '.json', '.ts'],\n  alias: { /* ... */ }\n}\n```\n\n> 注意：Vite 官方不推荐省略 `.vue` 扩展名，配置此项后仍建议逐步补全扩展名。\n\n**方案二：手动补全扩展名**\n\n```bash\n# 搜索所有缺少 .vue 扩展名的导入\ngrep -rn \"from ['\\\"].*@/.*['\\\"]\" src --include=\"*.js\" --include=\"*.vue\" | grep -v \"\\.vue\"\n```\n\n---\n\n## 问题 3：process is not defined\n\n**原因：** 第三方库使用了 Node.js 的 process 对象\n\n**解决方案：** 在 `vite.config.js` 中添加：\n\n```javascript\nexport default defineConfig({\n  define: {\n    'process.env': {},\n    'process.platform': JSON.stringify(''),\n    'process.version': JSON.stringify('')\n  }\n})\n```\n\n---\n\n## 问题 4：global is not defined\n\n**原因：** 代码中使用了 Node.js 的 global 对象\n\n**解决方案：** 全局替换 `global.` 为 `window.`\n\n```bash\nfind src -type f \\( -name \"*.js\" -o -name \"*.vue\" \\) -exec sed -i '' 's/global\\./window./g' {} +\n```\n\n---\n\n## 问题 5：图片 require 报错\n\n**原因：** Vite 不支持 require 动态导入图片\n\n**解决方案：**\n\n```javascript\n// ❌ 错误\n<img :src=\"require('@/assets/logo.png')\" />\n\n// ✅ 方案一：使用 import\nimport logo from '@/assets/logo.png'\n// template 中使用 :src=\"logo\"\n\n// ✅ 方案二：public 目录下的资源直接用绝对路径\n<img src=\"/images/logo.png\" />\n```\n\n---\n\n## 问题 6：环境变量判断不生效\n\n**原因：** Vite 使用 `import.meta.env` 而非 `process.env`，且只暴露 `VITE_` 前缀变量\n\n**解决方案：**\n\n**情况一：新代码用了 Vite 内置 MODE 变量但不匹配**\n\n```javascript\n// ❌ 错误\nimport.meta.env.MODE === 'testing'  // 永远不匹配\n\n// ✅ 正确：使用自定义的 VITE_RUN_ENV 变量\nimport.meta.env.VITE_RUN_ENV === 'staging'\n```\n\n**情况二：老代码使用 `process.env.VUE_APP_*`，通过配置兼容**\n\n在 `vite.config.js` 中用 `define` 静态替换，无需修改业务代码：\n\n```javascript\nimport { defineConfig, loadEnv } from 'vite'\n\nexport default defineConfig(({ mode }) => {\n  const env = loadEnv(mode, process.cwd(), '')\n\n  const define = Object.entries(env).reduce((memo, [key, value]) => {\n    if (key.startsWith('VITE_')) {\n      memo[`process.env.${key.replace(/^VITE_/, 'VUE_APP_')}`] = JSON.stringify(value)\n    }\n    if (key.startsWith('VUE_APP_')) {\n      memo[`process.env.${key}`] = JSON.stringify(value)\n    }\n    return memo\n  }, {})\n\n  if (env.NODE_ENV) define['process.env.NODE_ENV'] = JSON.stringify(env.NODE_ENV)\n\n  return { define }\n})\n```\n\n> 此方案只兼容静态访问，不兼容 `process.env[dynamicKey]` 动态访问。\n\n---\n\n## 问题 7：SCSS :export 导出变量不工作\n\n**原因：** Vite 不支持 SCSS 的 :export 语法\n\n**解决方案：** 创建对应的 JS 文件导出变量\n\n```javascript\n// src/styles/variables.js\nexport default {\n  menuText: '#101011',\n  menuActiveText: '#2255FF',\n  menuBg: '#F8F9FB'\n}\n```\n\n---\n\n## 问题 8：Vue 文件中使用 JSX 报错\n\n**原因：** 缺少 JSX 插件\n\n**解决方案：**\n\n1. 安装插件：`pnpm add -D @vitejs/plugin-vue2-jsx`\n2. 在 `vite.config.js` 中添加插件\n3. 在 `<script>` 标签添加 `lang=\"jsx\"`\n\n---\n\n## 问题 9：HTMLCollection 没有 forEach 方法\n\n**原因：** Vite 环境没有 polyfill\n\n**解决方案：** 使用 Array.from 转换\n\n```javascript\n// ❌ 错误\nconst children = document.querySelector('.container').children\nchildren.forEach(item => {})\n\n// ✅ 正确\nArray.from(children).forEach(item => )\n```\n\n---\n\n## 问题 10：Vue Router 导航守卫重定向报错\n\n**原因：** 缺少 replace: true 参数\n\n**解决方案：**\n\n```javascript\n// ❌ 可能报错\nnext({ path: '/' })\n\n// ✅ 正确\nnext({ path: '/', replace: true })\n```\n\n---\n\n## 问题 11：Element-UI 样式丢失\n\n**原因：** 未正确引入 Element-UI 样式\n\n**解决方案：** 在 main.js 中确保正确引入：\n\n```javascript\nimport ElementUI from 'element-ui'\nimport 'element-ui/lib/theme-chalk/index.css'\n\nVue.use(ElementUI)\n```\n\n---\n\n## 问题 12：SVG Sprite 图标不显示\n\n**原因：** 原项目使用 `svg-sprite-loader`，需要迁移到 `vite-plugin-svg-icons`\n\n**解决方案：**\n\n1. **安装插件：**\n\n    ```bash\n    pnpm add -D vite-plugin-svg-icons\n    ```\n\n2. **在 `vite.config.js` 中配置：**\n\n    ```javascript\n    import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'\n    import path from 'path'\n\n    export default defineConfig({\n      plugins: [\n        vue2(),\n        createSvgIconsPlugin({\n          iconDirs: [path.resolve(process.cwd(), 'src/icons/svg')],\n          symbolId: 'icon-[name]'\n        })\n      ]\n    })\n    ```\n\n3. **在 `main.js` 中添加：**\n\n    ```javascript\n    import 'virtual:svg-icons-register'\n    ```\n\n---\n\n## 问题 13：热更新 HMR 不生效\n\n**原因：** 组件缺少 name 属性\n\n**解决方案：** 确保组件有 name 属性\n\n```javascript\nexport default {\n  name: 'MyComponent',  // 确保有 name\n  // ...\n}\n```\n\n---\n\n## 问题 14：生产构建后路径错误\n\n**原因：** `base` 配置不正确\n\n**解决方案：** 检查 `vite.config.js` 中的 `base` 配置\n\n```javascript\nexport default defineConfig({\n  // 部署在根目录\n  base: '/',\n  // 部署在子目录\n  base: '/admin/',\n  // 使用相对路径（推荐用于不确定部署路径的情况）\n  base: './'\n})\n```\n\n---\n\n## 问题 15：Node.js path 模块在浏览器不可用\n\n**原因：** 代码中使用了 Node.js 的 `path` 模块（如 `path.resolve`）\n\n**解决方案：**\n\n1. **安装浏览器兼容版本：**\n\n    ```bash\n    pnpm add path-browserify\n    ```\n\n2. **在 `vite.config.js` 中配置 alias：**\n\n    ```javascript\n    resolve: {\n      alias: {\n        \"@\": fileURLToPath(new URL(\"./src\", import.meta.url)),\n        vue: \"vue/dist/vue.esm.js\",\n        path: \"path-browserify\",  // 添加这行\n      },\n    },\n    ```\n\n---\n\n## 问题 16：axios 版本存在安全漏洞\n\n**原因：** 项目继续保留旧的 axios 版本，或迁移文档仍固定推荐已经落后的补丁版本。Vue 2 是否迁移到 Vite，不会改变 axios 的安全风险；需要单独按官方安全公告升级。\n\n**已知漏洞：**\n\n- CVE-2023-45857: XSRF Token 泄露风险（< 1.6.0）\n- CVE-2024-39338: 路径解析导致的 SSRF 风险（1.3.2 - 1.7.3）\n- CVE-2025-27152: 绝对 URL 可绕过 baseURL，导致 SSRF / 凭据泄露（< 1.8.2）\n- CVE-2025-58754: Node 端 `data:` URL 可触发内存型 DoS（< 1.12.0）\n- `mergeConfig` 处理 `__proto__` 时可触发 DoS（参考官方 advisory，按最新已修复补丁线处理）\n\n**解决方案：**\n\n1. **升级 axios 到当前 1.x 安全补丁版本：**\n\n    先核对官方 release 和 security advisories，再决定具体补丁号；不要把 skill 写死在某个会过时的版本。\n\n    可以直接写成“升级到当前 1.x 安全补丁版本”，由执行当日再确认具体版本。\n\n2. **说明兼容性边界：**\n\n    - 对 Vue 2 + Vite 迁移场景，axios 与 Vue 主版本没有强绑定，`axios.create()`、请求/响应拦截器、`responseType: 'blob'`、普通上传下载通常可以继续使用。\n    - 需要额外排查旧写法：`CancelToken`、`axios.all`、`axios.spread`、`headers.post['Content-Type']`、直接裸用 `axios` 绕过统一封装。\n    - 如果项目在 Node 侧会拼接外部 URL，升级后仍要校验 `baseURL` 和入参 URL，不能只依赖版本升级。\n\n3. **修正上传相关旧写法：**\n\n    ```javascript\n    // ❌ 旧写法：axios 1.x 下 headers.post 不存在，可能直接报错\n    request({\n      url: '/upload',\n      method: 'post',\n      data: formData,\n      transformRequest: [(data, headers) => {\n        delete headers.post['Content-Type'];\n        return data;\n      }],\n      headers: {\n        'Content-Type': 'multipart/form-data'\n      }\n    });\n    ```\n\n    ```javascript\n    // ✅ 推荐：直接传 FormData，让 axios / 浏览器自动处理 multipart boundary\n    request({\n      url: '/upload',\n      method: 'post',\n      data: formData\n    });\n    ```\n\n4. **优化 axios 配置文件（如果存在响应拦截器中的 FileReader 异步问题）：**\n\n    ```javascript\n    // ✅ 正确：将 FileReader 包装为 Promise\n    service.interceptors.response.use((response) => {\n      let res = response.data;\n      if (res instanceof Blob && res.type === \"application/json\") {\n        return new Promise((resolve, reject) => {\n          const file = new FileReader();\n          file.readAsText(res, \"utf-8\");\n          file.onload = () => {\n            try {\n              const jsonRes = JSON.parse(file.result);\n              resolve(jsonRes);\n            } catch (error) {\n              reject(error);\n            }\n          };\n          file.onerror = () => reject(new Error(\"文件读取失败\"));\n        });\n      }\n      return res;\n    });\n    ```\n\n---\n\n## 问题 17：浏览器兼容性问题（需要支持旧浏览器）\n\n**原因：** Vite 默认只支持现代浏览器\n\n**解决方案：**\n\n1. **安装 legacy 插件：**\n\n    ```bash\n    pnpm add -D @vitejs/plugin-legacy terser\n    ```\n\n2. **在 `vite.config.js` 中配置：**\n\n    ```javascript\n    import legacy from '@vitejs/plugin-legacy'\n    \n    export default defineConfig({\n      plugins: [\n        vue2(),\n        legacy({\n          targets: ['defaults', 'not IE 11'],\n          additionalLegacyPolyfills: ['regenerator-runtime/runtime']\n        })\n      ]\n    })\n    ```\n\n---\n\n## 问题 18：router.app.$store 为 undefined\n\n**原因：** 在 Vite 环境下，`router.app` 在初始导航时可能尚未设置\n\n**错误示例：**\n\n```text\nTypeError: Cannot read properties of undefined (reading 'dispatch')\n```\n\n**解决方案：** 直接使用导入的 store 模块\n\n```javascript\n// ❌ 错误\nrouter.app.$store.dispatch('setBreadcurmbItems', breadCrumbItems);\n\n// ✅ 正确\nimport store from '../store/index';\nstore.dispatch('setBreadcurmbItems', breadCrumbItems);\n```\n\n---\n\n## 问题 19：构建后 chunk 体积过大警告\n\n**原因：** 构建后某些 chunk 超过 1000 kB\n\n**解决方案：**\n\n1. **扩展 manualChunks 配置：**\n\n    ```javascript\n    manualChunks: {\n        'element-ui': ['element-ui'],\n        'vue-vendor': ['vue', 'vue-router', 'vuex'],\n        'utils': ['axios', 'moment', 'qs'],\n    },\n    ```\n\n2. **实现路由懒加载**（见问题 20）\n\n3. **调整警告阈值：**\n\n    ```javascript\n    build: {\n        chunkSizeWarningLimit: 1500,\n    }\n    ```\n\n---\n\n## 问题 20：路由组件未实现懒加载导致主包过大\n\n**原因：** 使用 `import.meta.glob` 时设置了 `eager: true`\n\n**解决方案：**\n\n1. **移除 eager: true：**\n\n    ```javascript\n    // ✅ 正确：懒加载模式\n    const viewModules = import.meta.glob('@/views/*/index.vue')\n    ```\n\n2. **排除静态导入的组件：**\n\n    ```javascript\n    const viewModules = import.meta.glob([\n        '@/views/*/index.vue',\n        '!@/views/login/index.vue'  // 排除 login\n    ])\n    ```\n\n---\n\n## 问题 21：静态资源 404 错误（publicDir 配置）\n\n**原因：** Vite 默认使用 `public` 作为静态资源目录\n\n**解决方案：**\n\n1. **配置 publicDir：**\n\n    ```javascript\n    export default defineConfig({\n        publicDir: 'static',\n    })\n    ```\n\n2. **修改引用路径（去掉 /static 前缀）：**\n\n    ```html\n    <!-- 修改前 -->\n    <script src=\"/static/ckeditor/ckeditor.js\"></script>\n    \n    <!-- 修改后 -->\n    <script src=\"/ckeditor/ckeditor.js\"></script>\n    ```\n\n---\n\n## 问题 22：正则匹配结果为 null 导致运行时报错\n\n**原因：** 正则匹配未对 null 结果进行检查\n\n**解决方案：** 添加空值检查\n\n```javascript\n// ✅ 正确：先检查匹配结果\nconst match = location.hostname.match(/([a-zA-Z0-9]+\\.com)/g)\nif (match) {\n    this.domain = match[0]\n}\n```\n\n---\n\n## 问题 23：动态导入与静态导入冲突警告\n\n**原因：** 同一个文件既被静态导入又被 `import.meta.glob` 动态导入\n\n**解决方案：** 在 glob 模式中排除已静态导入的组件\n\n```javascript\n// ✅ 正确：使用否定模式排除\nconst viewModules = import.meta.glob([\n    \"@/views/*/index.vue\",\n    \"!@/views/login/index.vue\"\n]);\n```\n\nFile v1.0.1:references/deploy-config.md\n\n# 部署配置\n\n## NPM 脚本\n\n```json\n\"scripts\": {\n  \"dev\": \"vite\",\n  \"start\": \"pnpm run dev\",\n  \"build\": \"vite build\",\n  \"preview\": \"vite preview\",\n  \"build:stage\": \"pnpm run build --mode staging\",\n  \"build:prod\": \"pnpm run build --mode production\",\n  \"clean\": \"rimraf dist.zip\",\n  \"zip\": \"node export-zip.cjs\",\n  \"upload:stage\": \"node ftp-deploy.js stage\",\n  \"upload:prod\": \"node ftp-deploy.js production\",\n  \"deploy:stage\": \"pnpm run build:stage && pnpm run clean && pnpm run zip && pnpm run upload:stage\",\n  \"deploy:prod\": \"pnpm run build:prod && pnpm run clean && pnpm run zip && pnpm run upload:prod\"\n}\n```\n\n---\n\n## 压缩脚本 (export-zip.cjs)\n\n```javascript\nconst fs = require('fs');\nconst archiver = require('archiver');\n\nconst homedir = __dirname;\nconst target = ['dist'];\n\nconst output = fs.createWriteStream(homedir + '/dist.zip');\nconst archive = archiver('zip', { zlib: { level: 9 } });\n\narchive.on('error', function (err) { throw err; });\n\noutput.on('close', function () {\n    console.log(`压缩完毕，大小: ${(archive.pointer() / 1024 / 1024).toFixed(1)} MB`);\n});\n\narchive.pipe(output);\nfor (let i of target) {\n    archive.directory(i, i);\n}\narchive.finalize();\n```\n\n---\n\n## FTP 部署脚本 (ftp-deploy.js)\n\n> ⚠️ **安全提示**：部署凭证**不要**硬编码在脚本中或使用 Git 提交。通过环境变量或 `.env` 文件（已加入 `.gitignore`）传入。生产环境部署只在显式确认后执行。\n\n```javascript\nimport FtpDeploy from \"ftp-deploy\";\nimport { fileURLToPath } from \"node:url\";\nimport path from \"node:path\";\n\nconst ftpDeploy = new FtpDeploy();\nconst env = process.argv.splice(2)[0];\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// 从环境变量读取部署凭证，未设置时报错\nfunction getEnvConfig(targetEnv) {\n    const prefix = targetEnv === 'production' ? 'PROD' : 'STAGE';\n    const user = process.env[`${prefix}_FTP_USER`];\n    const password = process.env[`${prefix}_FTP_PASSWORD`];\n    const host = process.env[`${prefix}_FTP_HOST`];\n    const remoteRoot = process.env[`${prefix}_FTP_REMOTE_ROOT`];\n    if (!user || !password || !host || !remoteRoot) {\n        console.error(`\\x1b[31mError: Missing ${prefix}_FTP_* environment variables\\x1b[0m`);\n        process.exit(1);\n    }\n    return { user, password, host, remoteRoot };\n}\n\nif (!env || !['stage', 'production'].includes(env)) {\n    console.error('\\x1b[31m%s\\x1b[0m', 'error: 环境参数错误，请使用 stage 或 production');\n    process.exit(1);\n}\n\nconst envConfig = getEnvConfig(env);\n\nconst config = {\n    ...envConfig,\n    port: 21,\n    localRoot: __dirname + \"/\",\n    include: [\"dist.zip\"],\n    deleteRemote: false,\n    forcePasv: true,\n    sftp: false,\n};\n\nftpDeploy.deploy(config)\n    .then(() => console.log('upload completed'))\n    .catch((err) => console.log(err));\n```\n\n**使用方式：**\n\n```bash\n# 临时设置（单次部署）\nSTAGE_FTP_USER=myuser STAGE_FTP_PASSWORD=mypass STAGE_FTP_HOST=ftp.example.com STAGE_FTP_REMOTE_ROOT=/staging/ pnpm run upload:stage\n\n# 或通过 .env.local 文件（需已加入 .gitignore）\n# 在 .env.local 中写入：\n#   STAGE_FTP_USER=myuser\n#   STAGE_FTP_PASSWORD=mypass\n#   STAGE_FTP_HOST=ftp.example.com\n#   STAGE_FTP_REMOTE_ROOT=/staging/\n#   PROD_FTP_USER=myuser\n#   PROD_FTP_PASSWORD=mypass\n#   PROD_FTP_HOST=ftp.example.com\n#   PROD_FTP_REMOTE_ROOT=/prod/\n```\n\n---\n\n## 部署依赖\n\n```bash\npnpm add -D archiver ftp-deploy rimraf\n```\n\nFile v1.0.1:references/migration-steps.md\n\n# 迁移步骤详解\n\n## 目录\n\n- [1. 分析 package.json](#1-分析-packagejson)\n- [2. 提取环境变量](#2-提取环境变量)\n- [3. 删除 Webpack 文件](#3-删除-webpack-文件)\n- [4. 更新 package.json](#4-更新-packagejson)\n- [5. 创建环境变量文件](#5-创建环境变量文件)\n- [6. 修改 index.html](#6-修改-indexhtml)\n- [7. 修改 main.js](#7-修改-mainjs)\n- [8. 全局替换环境变量](#8-全局替换环境变量)\n- [9. 处理静态资源](#9-处理静态资源)\n- [10. 修改组件导入](#10-修改组件导入)\n- [11. 修复样式问题](#11-修复样式问题)\n\n---\n\n## 1. 分析 package.json\n\n读取 package.json，识别依赖类型：\n\n**保留的业务依赖：** axios, element-ui, lodash, qs, echarts, moment 等\n\n**需要删除的 devDependencies：**\n- webpack, webpack-*, babel-*\n- html-webpack-plugin, extract-text-webpack-plugin\n- css-loader, style-loader, sass-loader, vue-loader\n- node-sass（替换为 sass）\n- vue-template-compiler（Vue 2.7 不需要）\n\n**需要升级的核心依赖：**\n- vue → ^2.7.16\n- vue-router → ^3.6.5\n- vuex → ^3.6.2\n- element-ui → ^2.15.14\n- axios → 当前 1.x 安全补丁版本（执行迁移当天先核对官方 release/security，不再在 skill 中固定写死具体补丁号）\n\n**axios 迁移检查：**\n- 如果项目还在 `0.x` 或早期 `1.x`，先升级到当前 1.x 安全补丁版本，再继续做 Vite 迁移\n- 搜索是否存在 `CancelToken`、`axios.all`、`axios.spread`、`headers.post['Content-Type']`\n- 搜索是否存在裸 `import axios from 'axios'`；如果这些请求也需要统一 token、401/403、加解密逻辑，应迁回统一 request 封装\n- 对 `FormData` 上传优先直接传 `FormData`，不要继续依赖 `delete headers.post['Content-Type']` 这类旧写法\n- 重点回归上传、文件导出、401/403 跳转、刷新 token 这几类接口\n\n---\n\n## 2. 提取环境变量\n\n读取 config/dev.env.js、config/prod.env.js 等文件，转换格式：\n\n```javascript\n// Webpack 格式\nmodule.exports = { BASE_API: '\"https://api.example.com\"' }\n\n// Vite 格式 (.env.development)\nVITE_BASE_API=https://api.example.com\n```\n\n---\n\n## 3. 删除 Webpack 文件\n\n> ⚠️ **执行前确认**：以下命令会永久删除文件。确保已在正确的项目根目录执行，且代码已提交或备份。\n\n**3.1 前置安全检查**\n\n```bash\n# 确认在项目根目录\npwd\n# 确认 Git 状态干净（无未提交的破坏性变更）\ngit status --short\n# 如果存在未提交的业务代码，先提交或 stash\ngit stash push -m \"before vite migration\"\n```\n\n**3.2 使用 Git 删除（可撤销）**\n\n优先使用 `git rm` 而非 `rm`，以便通过 `git restore` 恢复：\n\n```bash\n# 删除 Webpack 构建目录\ngit rm -rf build/ config/\n# 删除 Babel/PostCSS 配置\ngit rm -f .babelrc .postcssrc.js babel.config.js\n# 删除旧锁文件（pnpm 项目不需要 npm/yarn 锁文件）\ngit rm -f package-lock.json yarn.lock 2>/dev/null || true\n```\n\n如果项目未纳入 Git 版本控制，使用以下命令（**不可撤销，执行前务必确认**）：\n\n```bash\nrm -rf build/ config/\nrm -f .babelrc .postcssrc.js babel.config.js\nrm -f package-lock.json yarn.lock\n```\n\n**3.3 删除后验证**\n\n```bash\n# 确认目标目录已不存在\nls build config .babelrc .postcssrc.js babel.config.js 2>&1 | grep -q \"No such\" && echo \"清理完成\" || echo \"警告：部分文件仍存在\"\n```\n\n---\n\n## 4. 更新 package.json\n\n**关键修改：**\n\n1. 添加 `\"type\": \"module\"`\n\n2. 替换 scripts：\n\n```json\n\"scripts\": {\n  \"dev\": \"vite\",\n  \"build\": \"vite build\",\n  \"preview\": \"vite preview\",\n  \"build:stage\": \"pnpm run build --mode staging\",\n  \"build:prod\": \"pnpm run build --mode production\"\n}\n```\n\n3. 新的 devDependencies：\n\n```json\n\"devDependencies\": {\n  \"@vitejs/plugin-vue2\": \"^2.3.1\",\n  \"sass\": \"^1.77.0\",\n  \"vite\": \"^4.5.5\",\n  \"vite-plugin-compression\": \"^0.5.1\"\n}\n```\n\n4. 如果使用 JSX，添加：`\"@vitejs/plugin-vue2-jsx\": \"^2.1.1\"`\n\n5. 如果使用 SVG Sprite，添加：`\"vite-plugin-svg-icons\": \"^2.0.1\"`\n\n---\n\n## 5. 创建环境变量文件\n\n创建 .env.development、.env.staging、.env.production：\n\n```bash\nVITE_RUN_ENV=development\nVITE_BASE_API=https://dev-api.example.com\n```\n\n**注意：等号后面不能有空格！**\n\n---\n\n## 6. 修改 index.html\n\n1. 移动 index.html 到项目根目录（如果不在）\n2. 修改 script 标签：\n\n```html\n<script type=\"module\" src=\"/src/main.js\"></script>\n```\n\n---\n\n## 7. 修改 main.js\n\n```javascript\n// 修改前\nimport App from \"./App\";\nnew Vue({ template: \"<App/>\", components: { App } });\n\n// 修改后\nimport App from \"./App.vue\";\nnew Vue({ render: h => h(App) });\n```\n\n---\n\n## 8. 全局替换环境变量\n\n搜索并替换：\n\n- `process.env.NODE_ENV` → `import.meta.env.VITE_RUN_ENV`\n- `process.env.VUE_APP_*` → `import.meta.env.VITE_*`\n- `process.env.BASE_API` → `import.meta.env.VITE_BASE_API`\n\n---\n\n## 9. 处理静态资源\n\n```bash\n# 重命名 static 目录为 public\nmv static public\n\n# 替换 ~@ 为 @\nfind src -type f \\( -name \"*.scss\" -o -name \"*.vue\" \\) -exec sed -i '' 's/~@/@/g' {} +\n\n# 替换 global. 为 window.\nfind src -type f \\( -name \"*.js\" -o -name \"*.vue\" \\) -exec sed -i '' 's/global\\./window./g' {} +\n```\n\n---\n\n## 10. 修改组件导入\n\n所有 .vue 文件导入必须加扩展名：\n\n```javascript\n// 修改前\nimport LoginView from \"@/views/login/index\";\n\n// 修改后\nimport LoginView from \"@/views/login/index.vue\";\n```\n\n---\n\n## 11. 修复样式问题\n\n```bash\n# 替换深度选择器\nfind src -type f -name \"*.vue\" -exec sed -i '' 's/\\/deep\\//::v-deep /g' {} +\nfind src -type f -name \"*.vue\" -exec sed -i '' 's/>>>/::v-deep /g' {} +\n```\n\nFile v1.0.1:references/vite-config.md\n\n# Vite 配置模板\n\n## 基础配置\n\n```javascript\nimport { defineConfig } from \"vite\";\nimport vue2 from \"@vitejs/plugin-vue2\";\nimport { fileURLToPath, URL } from \"node:url\";\nimport viteCompression from \"vite-plugin-compression\";\n\nexport default defineConfig({\n  base: \"/\",\n  plugins: [\n    vue2(),\n    viteCompression({\n      algorithm: \"gzip\",\n      ext: \".gz\",\n      threshold: 10240,\n      deleteOriginFile: false,\n    }),\n  ],\n  resolve: {\n    alias: {\n      \"@\": fileURLToPath(new URL(\"./src\", import.meta.url)),\n      vue: \"vue/dist/vue.esm.js\",\n    },\n  },\n  server: {\n    port: 8080,\n    open: false,\n  },\n  optimizeDeps: {\n    include: [\"vue\", \"vue-router\", \"vuex\", \"element-ui\"],\n  },\n  css: {\n    preprocessorOptions: {\n      scss: {\n        silenceDeprecations: [\"legacy-js-api\", \"import\"],\n      },\n    },\n  },\n  build: {\n    outDir: \"dist\",\n    sourcemap: false,\n    cssCodeSplit: true,\n    assetsInlineLimit: 4096,\n    chunkSizeWarningLimit: 1000,\n    rollupOptions: {\n      output: {\n        manualChunks: {\n          \"vue-vendor\": [\"vue\", \"vue-router\", \"vuex\"],\n          \"element-ui\": [\"element-ui\"],\n          \"utils-vendor\": [\"axios\", \"lodash\", \"qs\"],\n        },\n        entryFileNames: \"assets/js/[name]-[hash].js\",\n        chunkFileNames: \"assets/js/[name]-[hash].js\",\n        assetFileNames: (assetInfo) => {\n          if (assetInfo.name?.endsWith(\".css\")) {\n            return \"assets/css/[name]-[hash][extname]\";\n          }\n          if (/\\.(png|jpe?g|gif|svg|webp|ico)$/i.test(assetInfo.name || \"\")) {\n            return \"assets/images/[name]-[hash][extname]\";\n          }\n          if (/\\.(woff2?|eot|ttf|otf)$/i.test(assetInfo.name || \"\")) {\n            return \"assets/fonts/[name]-[hash][extname]\";\n          }\n          return \"assets/[name]-[hash][extname]\";\n        },\n      },\n    },\n    commonjsOptions: {\n      include: [/node_modules/],\n      transformMixedEsModules: true,\n    },\n    minify: \"esbuild\",\n    esbuildOptions: {\n      drop: [\"console\", \"debugger\"],\n    },\n  },\n});\n```\n\n---\n\n## Webpack/Vite 环境变量兼容\n\n迁移期如果仍会合入 Vue CLI/Webpack 风格代码，可以在 Vite 中自动兼容静态 `process.env.VUE_APP_*` 写法。\n\n```javascript\nimport { defineConfig, loadEnv } from \"vite\";\n\nconst createProcessEnvDefine = (env) => {\n  const define = Object.entries(env).reduce((memo, [key, value]) => {\n    if (key.startsWith(\"VITE_\")) {\n      memo[`process.env.${key.replace(/^VITE_/, \"VUE_APP_\")}`] =\n        JSON.stringify(value);\n    }\n    if (key.startsWith(\"VUE_APP_\")) {\n      memo[`process.env.${key}`] = JSON.stringify(value);\n    }\n    return memo;\n  }, {});\n\n  if (env.NODE_ENV) {\n    define[\"process.env.NODE_ENV\"] = JSON.stringify(env.NODE_ENV);\n  }\n\n  return define;\n};\n\nexport default defineConfig(({ mode }) => {\n  const env = loadEnv(mode, process.cwd(), \"\");\n\n  return {\n    define: createProcessEnvDefine(env),\n  };\n});\n```\n\n约定：`.env.*` 新增变量优先使用 `VITE_` 前缀；业务新代码优先使用 `import.meta.env.VITE_*`；老代码或并行上线代码允许使用 `process.env.VUE_APP_*`。\n\n不要整体定义 `process.env`，优先定义 `process.env.VUE_APP_XXX` 这种精确 key。该方案只兼容静态访问，不兼容 `process.env[key]` 这类动态访问。\n\n---\n\n## 代理配置\n\n```javascript\nserver: {\n  port: 8080,\n  proxy: {\n    '/api': {\n      target: 'https://api.example.com',\n      changeOrigin: true,\n      rewrite: (path) => path.replace(/^\\/api/, '')\n    }\n  }\n}\n```\n\n---\n\n## 可选插件\n\n### JSX 支持\n\n```javascript\nimport vue2Jsx from \"@vitejs/plugin-vue2-jsx\";\n\nplugins: [vue2(), vue2Jsx()]\n```\n\n### SVG Sprite\n\n```javascript\nimport { createSvgIconsPlugin } from 'vite-plugin-svg-icons'\nimport path from 'path'\n\nplugins: [\n  vue2(),\n  createSvgIconsPlugin({\n    iconDirs: [path.resolve(process.cwd(), 'src/icons/svg')],\n    symbolId: 'icon-[name]'\n  })\n]\n```\n\n### Legacy 浏览器支持\n\n```javascript\nimport legacy from '@vitejs/plugin-legacy'\n\nplugins: [\n  vue2(),\n  legacy({\n    targets: ['defaults', 'not IE 11'],\n    additionalLegacyPolyfills: ['regenerator-runtime/runtime']\n  })\n]\n```\n\nFile v1.0.1:skill-card.md\n\n## Description:\n\nGuides developers through migrating Vue 2 projects from Webpack or Vue CLI builds to Vite, including dependency updates, configuration migration, environment-variable compatibility, testing, and optional deployment setup.\n\nThis skill is ready for commercial/non-commercial use.\n\n## Publisher:\n\n[yyyyzj](https://clawhub.ai/user/yyyyzj)\n\n### License/Terms of Use:\n\nMIT-0\n\n## Use Case:\n\nDevelopers and engineers use this skill to plan and execute Vue 2 build-tool migrations from Webpack or Vue CLI to Vite. It helps analyze existing project structure, update dependencies and configuration, migrate code patterns, troubleshoot common Vite issues, and validate builds before release.\n\n### Deployment Geography for Use:\n\nGlobal\n\n## Known Risks and Mitigations:\n\nRisk: Optional deployment guidance uses FTP and can upload production artifacts remotely.\n\nMitigation: Replace FTP with SFTP, HTTPS, or CI deployment; use least-privilege credentials from environment variables and require explicit production approval.\n\nRisk: Migration steps may delete old build and configuration files.\n\nMitigation: Run deletion steps only from the project root after confirming a clean Git state or backup, and prefer git rm so changes can be restored.\n\nRisk: Dependency and axios upgrades can change runtime behavior or leave known vulnerabilities if versions are not checked.\n\nMitigation: Review dependency conflicts manually, verify lockfile changes, and choose the current axios 1.x security patch level from official release and security sources at execution time.\n\n## Reference(s):\n\n- [Migration steps](references/migration-steps.md)\n- [Vite configuration template](references/vite-config.md)\n- [Common migration issues](references/common-issues.md)\n- [Deployment configuration](references/deploy-config.md)\n- [ClawHub skill page](https://clawhub.ai/yyyyzj/skills/vue2-vite-upgrade)\n\n## Skill Output:\n\n**Output Type(s):** [Text, Markdown, Code, Shell commands, Configuration, Guidance]\n\n**Output Format:** [Markdown guidance with inline code, shell commands, and configuration snippets]\n\n**Output Parameters:** [1D]\n\n**Other Properties Related to Output:** [Includes phased migration guidance and optional deployment instructions; review security guidance before production upload.]\n\n## Skill Version(s):\n\n1.0.1 (source: ClawHub release evidence)\n\n## Ethical Considerations:\n\nUsers should evaluate whether this skill is appropriate for their environment, review any generated or modified files before relying on them, and apply their organization's safety, security, and compliance requirements before deployment.\n\nArchive v1.0.0: 6 files, 14521 bytes\n\nFiles: references/common-issues.md (14748b), references/deploy-config.md (2375b), references/migration-steps.md (4551b), references/vite-config.md (4124b), SKILL.md (5733b), _meta.json (136b)\n\nFile v1.0.0:SKILL.md\n\n---\nname: vue2-vite-upgrade\ndescription: 将 Vue 2 + Webpack 项目升级到 Vite 构建工具。当用户需要将 Vue 2 项目从 Webpack 迁移到 Vite、优化构建速度、解决 Webpack 配置问题，或处理 Vue CLI/Webpack 与 Vite 环境变量兼容时使用此技能。触发场景包括：(1) 用户提到\"升级到 Vite\"、\"迁移到 Vite\"、\"Webpack 转 Vite\"；(2) 用户抱怨 Webpack 构建慢、热更新慢；(3) 用户想要现代化 Vue 2 项目的构建工具；(4) 用户遇到 Webpack 配置复杂难以维护的问题；(5) 用户询问如何提升 Vue 2 项目的开发体验；(6) 用户提到 process.env、VUE_APP_*、import.meta.env、VITE_* 等环境变量兼容。\n---\n\n# Vue 2 项目升级到 Vite 构建工具\n\n## 重要提示\n\n⚠️ **在开始之前，必须确认用户已经备份了代码或使用了 Git 版本控制！**\n\n## 执行原则\n\n1. **使用 TodoWrite 工具**：创建完整的任务列表，实时更新进度\n2. **分阶段执行**：按照阶段顺序执行，每个阶段完成后标记为完成\n3. **遇到问题及时沟通**：使用 AskUserQuestion 询问用户\n4. **保留业务依赖**：只删除 Webpack/Babel 相关依赖\n\n## 执行流程概览\n\n| 阶段 | 内容 |\n|------|------|\n| 阶段 1 | 项目分析 - 分析现有项目结构、依赖和配置 |\n| 阶段 2 | 文件清理与配置 - 删除 Webpack 配置，创建 Vite 配置 |\n| 阶段 3 | 代码迁移 - 修改代码以适配 Vite |\n| 阶段 4 | 测试与验证 - 安装依赖并测试运行 |\n| 阶段 5 | 部署配置（可选）- 配置自动化部署脚本 |\n\n## 参考文档\n\n- **Vite 配置模板**：见 [references/vite-config.md](references/vite-config.md)\n- **常见问题处理**：见 [references/common-issues.md](references/common-issues.md)\n- **部署配置**：见 [references/deploy-config.md](references/deploy-config.md)\n\n---\n\n## 阶段 1：项目分析\n\n使用 TodoWrite 创建任务列表，然后依次执行：\n\n1. 分析 package.json（识别业务依赖、Webpack 依赖、需升级依赖）\n2. 提取环境变量（从 config/*.env.js 转换为 .env.* 格式）\n3. 检查代理配置（记录 proxyTable）\n4. 检查特殊功能（SVG Sprite、JSX、动态路由）\n5. 单独审计 `axios`：核对官方 release/security 公告，避免继续保留已披露漏洞版本；同时排查上传、导出、401/403 拦截和裸 `axios` 调用\n\n详细步骤见 [references/migration-steps.md](references/migration-steps.md)\n\n---\n\n## 阶段 2：文件清理与配置\n\n1. 删除 Webpack 相关文件（build/、config/、.babelrc 等）\n2. 更新 package.json（添加 type: module，替换 scripts，更新依赖）\n3. 创建 vite.config.js（使用 [references/vite-config.md](references/vite-config.md) 模板）\n4. 创建环境变量文件（.env.development、.env.staging、.env.production）\n\n详细步骤见 [references/migration-steps.md](references/migration-steps.md)\n\n---\n\n## 阶段 3：代码迁移\n\n1. 修改 index.html（移动到根目录，添加 `<script type=\"module\">`）\n2. 修改 main.js（使用 render 函数，导入加 .vue 扩展名）\n3. 迁移环境变量：优先使用 import.meta.env；如项目存在并行上线或老分支合并，需要保留 process.env.VUE_APP_* 静态写法兼容层\n4. 处理静态资源（static → public，~@ → @，global → window）\n5. 修改组件导入（所有 .vue 导入加扩展名）\n6. 修复样式（/deep/ 和 >>> → ::v-deep）\n\n详细步骤见 [references/migration-steps.md](references/migration-steps.md)\n\n---\n\n## 环境变量兼容约定\n\n迁移期如果可能有 Webpack/Vue CLI 写法的需求先合入生产，不要只做 `process.env -> import.meta.env` 的单向替换。\n\n1. `.env.*` 中新增变量优先使用 `VITE_` 前缀。\n2. Vite 代码推荐使用 `import.meta.env.VITE_*`。\n3. Webpack/Vue CLI 历史代码允许继续使用 `process.env.VUE_APP_*`。\n4. 在 `vite.config.js` 中用 `loadEnv(mode, process.cwd(), '')` 读取变量，并自动把 `VITE_XXX` 映射为 `process.env.VUE_APP_XXX`。\n5. 如果 `.env.*` 已存在 `VUE_APP_XXX`，也要原样注入为 `process.env.VUE_APP_XXX`。\n6. `define` 使用精确 key，不整体替换 `process.env`，减少对第三方库和动态访问的影响。\n7. 兼容层只保证静态写法，如 `process.env.VUE_APP_BASE_API`；不承诺兼容 `process.env[key]`。\n\n具体配置示例见 [references/vite-config.md](references/vite-config.md)。\n\n---\n\n## 阶段 4：测试与验证\n\n1. 安装依赖：`pnpm install`（冲突时用 `--force`）\n2. 启动开发服务器：`pnpm run dev`\n3. 验证功能：页面显示、样式、图标、路由、API 请求\n4. 构建测试：`pnpm run build:stage && pnpm run preview`\n\n**遇到问题查看 [references/common-issues.md](references/common-issues.md)**\n\n---\n\n## 阶段 5：部署配置（可选）\n\n如需自动化部署，参考 [references/deploy-config.md](references/deploy-config.md)：\n\n1. 创建压缩脚本（export-zip.cjs）\n2. 创建 FTP 部署脚本（ftp-deploy.js）\n3. 添加部署 NPM 脚本\n4. 安装依赖：`pnpm add -D archiver ftp-deploy rimraf`\n\n---\n\n## 升级要点速查\n\n| 类别 | 修改内容 |\n|------|----------|\n| 依赖管理 | 保留业务依赖，删除 Webpack/Babel，升级 Vue 到 2.7.x；`axios` 使用当前 1.x 安全补丁版本，不保留已披露漏洞版本 |\n| 配置文件 | 创建 vite.config.js 和 .env.* 文件 |\n| 代码修改 | .vue 导入加扩展名，环境变量优先迁移到 import.meta.env，必要时兼容 process.env.VUE_APP_* |\n| 语法替换 | require → import，global → window，~@ → @ |\n| axios 改造 | 排查 `CancelToken`、`headers.post['Content-Type']`、裸 `axios` 调用、上传下载与登录态拦截 |\n\nFile v1.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn7ff3q7khffkrpd622drjz91n86k0sv\",\n  \"slug\": \"vue2-vite-upgrade\",\n  \"version\": \"1.0.0\",\n  \"publishedAt\": 1778563530693\n}\n\nFile v1.0.0:references/common-issues.md\n\n# 常见问题处理\n\n在升级过程中如果遇到以下错误，按照对应方案处理。\n\n## 目录\n\n- [问题 1：require is not defined](#问题-1require-is-not-defined)\n- [问题 2：找不到模块](#问题-2找不到模块cannot-find-module)\n- [问题 3：process is not defined](#问题-3process-is-not-defined)\n- [问题 4：global is not defined](#问题-4global-is-not-defined)\n- [问题 5：图片 require 报错](#问题-5图片-require-报错)\n- [问题 6：环境变量判断不生效](#问题-6环境变量判断不生效)\n- [问题 7：SCSS :export 不工作](#问题-7scss-export-导出变量不工作)\n- [问题 8：JSX 报错](#问题-8vue-文件中使用-jsx-报错)\n- [问题 9：HTMLCollection 没有 forEach](#问题-9htmlcollection-没有-foreach-方法)\n- [问题 10：导航守卫重定向报错](#问题-10vue-router-导航守卫重定向报错)\n- [问题 11：Element-UI 样式丢失](#问题-11element-ui-样式丢失)\n- [问题 12：SVG Sprite 图标不显示](#问题-12svg-sprite-图标不显示)\n- [问题 13：热更新不生效](#问题-13热更新-hmr-不生效)\n- [问题 14：生产构建路径错误](#问题-14生产构建后路径错误)\n- [问题 15：path 模块不可用](#问题-15nodejs-path-模块在浏览器不可用)\n- [问题 16：axios 安全漏洞](#问题-16axios-版本存在安全漏洞)\n- [问题 17：浏览器兼容性](#问题-17浏览器兼容性问题需要支持旧浏览器)\n- [问题 18：router.app.$store 为 undefined](#问题-18routerappstore-为-undefined)\n- [问题 19：chunk 体积过大](#问题-19构建后-chunk-体积过大警告)\n- [问题 20：路由懒加载](#问题-20路由组件未实现懒加载导致主包过大)\n- [问题 21：静态资源 404](#问题-21静态资源-404-错误publicdir-配置)\n- [问题 22：正则匹配 null](#问题-22正则匹配结果为-null-导致运行时报错)\n- [问题 23：动态导入冲突](#问题-23动态导入与静态导入冲突警告)\n\n---\n\n## 问题 1：require is not defined\n\n**原因：** Vite 不支持 CommonJS 的 require 语法\n\n**解决方案：**\n\n1. 将 CommonJS 依赖加入 `vite.config.js` 的 `optimizeDeps.include`\n2. 如果是代码中使用了 require，改为 import 语法\n\n---\n\n## 问题 2：找不到模块（Cannot find module）\n\n**原因：** 导入路径缺少 `.vue` 扩展名，或 Vite 默认不自动补全扩展名\n\n**解决方案：**\n\n**方案一：配置 `resolve.extensions`（推荐，批量解决）**\n\n在 `vite.config.js` 中添加：\n\n```javascript\nresolve: {\n  extensions: ['.js', '.vue', '.json', '.ts'],\n  alias: { /* ... */ }\n}\n```\n\n> 注意：Vite 官方不推荐省略 `.vue` 扩展名，配置此项后仍建议逐步补全扩展名。\n\n**方案二：手动补全扩展名**\n\n```bash\n# 搜索所有缺少 .vue 扩展名的导入\ngrep -rn \"from ['\\\"].*@/.*['\\\"]\" src --include=\"*.js\" --include=\"*.vue\" | grep -v \"\\.vue\"\n```\n\n---\n\n## 问题 3：process is not defined\n\n**原因：** 第三方库使用了 Node.js 的 process 对象\n\n**解决方案：** 在 `vite.config.js` 中添加：\n\n```javascript\nexport default defineConfig({\n  define: {\n    'process.env': {},\n    'process.platform': JSON.stringify(''),\n    'process.version': JSON.stringify('')\n  }\n})\n```\n\n---\n\n## 问题 4：global is not defined\n\n**原因：** 代码中使用了 Node.js 的 global 对象\n\n**解决方案：** 全局替换 `global.` 为 `window.`\n\n```bash\nfind src -type f \\( -name \"*.js\" -o -name \"*.vue\" \\) -exec sed -i '' 's/global\\./window./g' {} +\n```\n\n---\n\n## 问题 5：图片 require 报错\n\n**原因：** Vite 不支持 require 动态导入图片\n\n**解决方案：**\n\n```javascript\n// ❌ 错误\n<img :src=\"require('@/assets/logo.png')\" />\n\n// ✅ 方案一：使用 import\nimport logo from '@/assets/logo.png'\n// template 中使用 :src=\"logo\"\n\n// ✅ 方案二：public 目录下的资源直接用绝对路径\n<img src=\"/images/logo.png\" />\n```\n\n---\n\n## 问题 6：环境变量判断不生效\n\n**原因：** Vite 使用 `import.meta.env` 而非 `process.env`，且只暴露 `VITE_` 前缀变量\n\n**解决方案：**\n\n**情况一：新代码用了 Vite 内置 MODE 变量但不匹配**\n\n```javascript\n// ❌ 错误\nimport.meta.env.MODE === 'testing'  // 永远不匹配\n\n// ✅ 正确：使用自定义的 VITE_RUN_ENV 变量\nimport.meta.env.VITE_RUN_ENV === 'staging'\n```\n\n**情况二：老代码使用 `process.env.VUE_APP_*`，通过配置兼容**\n\n在 `vite.config.js` 中用 `define` 静态替换，无需修改业务代码：\n\n```javascript\nimport { defineConfig, loadEnv } from 'vite'\n\nexport default defineConfig(({ mode }) => {\n  const env = loadEnv(mode, process.cwd(), '')\n\n  const define = Object.entries(env).reduce((memo, [key, value]) => {\n    if (key.startsWith('VITE_')) {\n      memo[`process.env.${key.replace(/^VITE_/, 'VUE_APP_')}`] = JSON.stringify(value)\n    }\n    if (key.startsWith('VUE_APP_')) {\n      memo[`process.env.${key}`] = JSON.stringify(value)\n    }\n    return memo\n  }, {})\n\n  if (env.NODE_ENV) define['process.env.NODE_ENV'] = JSON.stringify(env.NODE_ENV)\n\n  return { define }\n})\n```\n\n> 此方案只兼容静态访问，不兼容 `process.env[dynamicKey]` 动态访问。\n\n---\n\n## 问题 7：SCSS :export 导出变量不工作\n\n**原因：** Vite 不支持 SCSS 的 :export 语法\n\n**解决方案：** 创建对应的 JS 文件导出变量\n\n```javascript\n// src/styles/variables.js\nexport default {\n  menuText: '#101011',\n  menuActiveText: '#2255FF',\n  menuBg: '#F8F9FB'\n}\n```\n\n---\n\n## 问题 8：Vue 文件中使用 JSX 报错\n\n**原因：** 缺少 JSX 插件\n\n**解决方案：**\n\n1. 安装插件：`pnpm add -D @vitejs/plugin-vue2-jsx`\n2. 在 `vite.config.js` 中添加插件\n3. 在 `<script>` 标签添加 `lang=\"jsx\"`\n\n---\n\n## 问题 9：HTMLCollection 没有 forEach 方法\n\n**原因：** Vite 环境没有 polyfill\n\n**解决方案：** 使用 Array.from 转换\n\n```javascript\n// ❌ 错误\nconst children = document.querySelector('.container').children\nchildren.forEach(item => {})\n\n// ✅ 正确\nArray.from(children).forEach(item => )\n```\n\n---\n\n## 问题 10：Vue Router 导航守卫重定向报错\n\n**原因：** 缺少 replace: true 参数\n\n**解决方案：**\n\n```javascript\n// ❌ 可能报错\nnext({ path: '/' })\n\n// ✅ 正确\nnext({ path: '/', replace: true })\n```\n\n---\n\n## 问题 11：Element-UI 样式丢失\n\n**原因：** 未正确引入 Element-UI 样式\n\n**解决方案：** 在 main.js 中确保正确引入：\n\n```javascript\nimport ElementUI from 'element-ui'\nimport 'element-ui/lib/theme-chalk/index.css'\n\nVue.use(ElementUI)\n```\n\n---\n\n## 问题 12：SVG Sprite 图标不显示\n\n**原因：** 原项目使用 `svg-sprite-loader`，需要迁移到 `vite-plugin-svg-icons`\n\n**解决方案：**\n\n1. **安装插件：**\n\n    ```bash\n    pnpm add -D vite-plugin-svg-icons\n    ```\n\n2. **在 `vite.config.js` 中配置：**\n\n    ```javascript\n    import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'\n    import path from 'path'\n\n    export default defineConfig({\n      plugins: [\n        vue2(),\n        createSvgIconsPlugin({\n          iconDirs: [path.resolve(process.cwd(), 'src/icons/svg')],\n          symbolId: 'icon-[name]'\n        })\n      ]\n    })\n    ```\n\n3. **在 `main.js` 中添加：**\n\n    ```javascript\n    import 'virtual:svg-icons-register'\n    ```\n\n---\n\n## 问题 13：热更新 HMR 不生效\n\n**原因：** 组件缺少 name 属性\n\n**解决方案：** 确保组件有 name 属性\n\n```javascript\nexport default {\n  name: 'MyComponent',  // 确保有 name\n  // ...\n}\n```\n\n---\n\n## 问题 14：生产构建后路径错误\n\n**原因：** `base` 配置不正确\n\n**解决方案：** 检查 `vite.config.js` 中的 `base` 配置\n\n```javascript\nexport default defineConfig({\n  // 部署在根目录\n  base: '/',\n  // 部署在子目录\n  base: '/admin/',\n  // 使用相对路径（推荐用于不确定部署路径的情况）\n  base: './'\n})\n```\n\n---\n\n## 问题 15：Node.js path 模块在浏览器不可用\n\n**原因：** 代码中使用了 Node.js 的 `path` 模块（如 `path.resolve`）\n\n**解决方案：**\n\n1. **安装浏览器兼容版本：**\n\n    ```bash\n    pnpm add path-browserify\n    ```\n\n2. **在 `vite.config.js` 中配置 alias：**\n\n    ```javascript\n    resolve: {\n      alias: {\n        \"@\": fileURLToPath(new URL(\"./src\", import.meta.url)),\n        vue: \"vue/dist/vue.esm.js\",\n        path: \"path-browserify\",  // 添加这行\n      },\n    },\n    ```\n\n---\n\n## 问题 16：axios 版本存在安全漏洞\n\n**原因：** 项目继续保留旧的 axios 版本，或迁移文档仍固定推荐已经落后的补丁版本。Vue 2 是否迁移到 Vite，不会改变 axios 的安全风险；需要单独按官方安全公告升级。\n\n**已知漏洞：**\n\n- CVE-2023-45857: XSRF Token 泄露风险（< 1.6.0）\n- CVE-2024-39338: 路径解析导致的 SSRF 风险（1.3.2 - 1.7.3）\n- CVE-2025-27152: 绝对 URL 可绕过 baseURL，导致 SSRF / 凭据泄露（< 1.8.2）\n- CVE-2025-58754: Node 端 `data:` URL 可触发内存型 DoS（< 1.12.0）\n- `mergeConfig` 处理 `__proto__` 时可触发 DoS（参考官方 advisory，按最新已修复补丁线处理）\n\n**解决方案：**\n\n1. **升级 axios 到当前 1.x 安全补丁版本：**\n\n    先核对官方 release 和 security advisories，再决定具体补丁号；不要把 skill 写死在某个会过时的版本。\n\n    可以直接写成“升级到当前 1.x 安全补丁版本”，由执行当日再确认具体版本。\n\n2. **说明兼容性边界：**\n\n    - 对 Vue 2 + Vite 迁移场景，axios 与 Vue 主版本没有强绑定，`axios.create()`、请求/响应拦截器、`responseType: 'blob'`、普通上传下载通常可以继续使用。\n    - 需要额外排查旧写法：`CancelToken`、`axios.all`、`axios.spread`、`headers.post['Content-Type']`、直接裸用 `axios` 绕过统一封装。\n    - 如果项目在 Node 侧会拼接外部 URL，升级后仍要校验 `baseURL` 和入参 URL，不能只依赖版本升级。\n\n3. **修正上传相关旧写法：**\n\n    ```javascript\n    // ❌ 旧写法：axios 1.x 下 headers.post 不存在，可能直接报错\n    request({\n      url: '/upload',\n      method: 'post',\n      data: formData,\n      transformRequest: [(data, headers) => {\n        delete headers.post['Content-Type'];\n        return data;\n      }],\n      headers: {\n        'Content-Type': 'multipart/form-data'\n      }\n    });\n    ```\n\n    ```javascript\n    // ✅ 推荐：直接传 FormData，让 axios / 浏览器自动处理 multipart boundary\n    request({\n      url: '/upload',\n      method: 'post',\n      data: formData\n    });\n    ```\n\n4. **优化 axios 配置文件（如果存在响应拦截器中的 FileReader 异步问题）：**\n\n    ```javascript\n    // ✅ 正确：将 FileReader 包装为 Promise\n    service.interceptors.response.use((response) => {\n      let res = response.data;\n      if (res instanceof Blob && res.type === \"application/json\") {\n        return new Promise((resolve, reject) => {\n          const file = new FileReader();\n          file.readAsText(res, \"utf-8\");\n          file.onload = () => {\n            try {\n              const jsonRes = JSON.parse(file.result);\n              resolve(jsonRes);\n            } catch (error) {\n              reject(error);\n            }\n          };\n          file.onerror = () => reject(new Error(\"文件读取失败\"));\n        });\n      }\n      return res;\n    });\n    ```\n\n---\n\n## 问题 17：浏览器兼容性问题（需要支持旧浏览器）\n\n**原因：** Vite 默认只支持现代浏览器\n\n**解决方案：**\n\n1. **安装 legacy 插件：**\n\n    ```bash\n    pnpm add -D @vitejs/plugin-legacy terser\n    ```\n\n2. **在 `vite.config.js` 中配置：**\n\n    ```javascript\n    import legacy from '@vitejs/plugin-legacy'\n    \n    export default defineConfig({\n      plugins: [\n        vue2(),\n        legacy({\n          targets: ['defaults', 'not IE 11'],\n          additionalLegacyPolyfills: ['regenerator-runtime/runtime']\n        })\n      ]\n    })\n    ```\n\n---\n\n## 问题 18：router.app.$store 为 undefined\n\n**原因：** 在 Vite 环境下，`router.app` 在初始导航时可能尚未设置\n\n**错误示例：**\n\n```text\nTypeError: Cannot read properties of undefined (reading 'dispatch')\n```\n\n**解决方案：** 直接使用导入的 store 模块\n\n```javascript\n// ❌ 错误\nrouter.app.$store.dispatch('setBreadcurmbItems', breadCrumbItems);\n\n// ✅ 正确\nimport store from '../store/index';\nstore.dispatch('setBreadcurmbItems', breadCrumbItems);\n```\n\n---\n\n## 问题 19：构建后 chunk 体积过大警告\n\n**原因：** 构建后某些 chunk 超过 1000 kB\n\n**解决方案：**\n\n1. **扩展 manualChunks 配置：**\n\n    ```javascript\n    manualChunks: {\n        'element-ui': ['element-ui'],\n        'vue-vendor': ['vue', 'vue-router', 'vuex'],\n        'utils': ['axios', 'moment', 'qs'],\n    },\n    ```\n\n2. **实现路由懒加载**（见问题 20）\n\n3. **调整警告阈值：**\n\n    ```javascript\n    build: {\n        chunkSizeWarningLimit: 1500,\n    }\n    ```\n\n---\n\n## 问题 20：路由组件未实现懒加载导致主包过大\n\n**原因：** 使用 `import.meta.glob` 时设置了 `eager: true`\n\n**解决方案：**\n\n1. **移除 eager: true：**\n\n    ```javascript\n    // ✅ 正确：懒加载模式\n    const viewModules = import.meta.glob('@/views/*/index.vue')\n    ```\n\n2. **排除静态导入的组件：**\n\n    ```javascript\n    const viewModules = import.meta.glob([\n        '@/views/*/index.vue',\n        '!@/views/login/index.vue'  // 排除 login\n    ])\n    ```\n\n---\n\n## 问题 21：静态资源 404 错误（publicDir 配置）\n\n**原因：** Vite 默认使用 `public` 作为静态资源目录\n\n**解决方案：**\n\n1. **配置 publicDir：**\n\n    ```javascript\n    export default defineConfig({\n        publicDir: 'static',\n    })\n    ```\n\n2. **修改引用路径（去掉 /static 前缀）：**\n\n    ```html\n    <!-- 修改前 -->\n    <script src=\"/static/ckeditor/ckeditor.js\"></script>\n    \n    <!-- 修改后 -->\n    <script src=\"/ckeditor/ckeditor.js\"></script>\n    ```\n\n---\n\n## 问题 22：正则匹配结果为 null 导致运行时报错\n\n**原因：** 正则匹配未对 null 结果进行检查\n\n**解决方案：** 添加空值检查\n\n```javascript\n// ✅ 正确：先检查匹配结果\nconst match = location.hostname.match(/([a-zA-Z0-9]+\\.com)/g)\nif (match) {\n    this.domain = match[0]\n}\n```\n\n---\n\n## 问题 23：动态导入与静态导入冲突警告\n\n**原因：** 同一个文件既被静态导入又被 `import.meta.glob` 动态导入\n\n**解决方案：** 在 glob 模式中排除已静态导入的组件\n\n```javascript\n// ✅ 正确：使用否定模式排除\nconst viewModules = import.meta.glob([\n    \"@/views/*/index.vue\",\n    \"!@/views/login/index.vue\"\n]);\n```\n\nFile v1.0.0:references/deploy-config.md\n\n# 部署配置\n\n## NPM 脚本\n\n```json\n\"scripts\": {\n  \"dev\": \"vite\",\n  \"start\": \"pnpm run dev\",\n  \"build\": \"vite build\",\n  \"preview\": \"vite preview\",\n  \"build:stage\": \"pnpm run build --mode staging\",\n  \"build:prod\": \"pnpm run build --mode production\",\n  \"clean\": \"rimraf dist.zip\",\n  \"zip\": \"node export-zip.cjs\",\n  \"upload:stage\": \"node ftp-deploy.js stage\",\n  \"upload:prod\": \"node ftp-deploy.js production\",\n  \"deploy:stage\": \"pnpm run build:stage && pnpm run clean && pnpm run zip && pnpm run upload:stage\",\n  \"deploy:prod\": \"pnpm run build:prod && pnpm run clean && pnpm run zip && pnpm run upload:prod\"\n}\n```\n\n---\n\n## 压缩脚本 (export-zip.cjs)\n\n```javascript\nconst fs = require('fs');\nconst archiver = require('archiver');\n\nconst homedir = __dirname;\nconst target = ['dist'];\n\nconst output = fs.createWriteStream(homedir + '/dist.zip');\nconst archive = archiver('zip', { zlib: { level: 9 } });\n\narchive.on('error', function (err) { throw err; });\n\noutput.on('close', function () {\n    console.log(`压缩完毕，大小: ${(archive.pointer() / 1024 / 1024).toFixed(1)} MB`);\n});\n\narchive.pipe(output);\nfor (let i of target) {\n    archive.directory(i, i);\n}\narchive.finalize();\n```\n\n---\n\n## FTP 部署脚本 (ftp-deploy.js)\n\n```javascript\nimport FtpDeploy from \"ftp-deploy\";\nimport { fileURLToPath } from \"node:url\";\nimport path from \"node:path\";\n\nconst ftpDeploy = new FtpDeploy();\nconst env = process.argv.splice(2)[0];\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nconst envConfig = {\n    'stage': {\n        user: 'your_test_user',\n        password: 'your_test_password',\n        host: 'your_ftp_host',\n        remoteRoot: '/your_test_path/'\n    },\n    'production': {\n        user: 'your_prod_user',\n        password: 'your_prod_password',\n        host: 'your_ftp_host',\n        remoteRoot: '/your_prod_path/'\n    }\n};\n\nif (!env || !envConfig[env]) {\n    console.log('\\x1b[31m%s\\x1b[0m', 'error: 环境参数错误');\n    process.exit(1);\n}\n\nconst config = {\n    ...envConfig[env],\n    port: 21,\n    localRoot: __dirname + \"/\",\n    include: [\"dist.zip\"],\n    deleteRemote: false,\n    forcePasv: true,\n    sftp: false,\n};\n\nftpDeploy.deploy(config)\n    .then(() => console.log('upload completed'))\n    .catch((err) => console.log(err));\n```\n\n---\n\n## 部署依赖\n\n```bash\npnpm add -D archiver ftp-deploy rimraf\n```\n\nFile v1.0.0:references/migration-steps.md\n\n# 迁移步骤详解\n\n## 目录\n\n- [1. 分析 package.json](#1-分析-packagejson)\n- [2. 提取环境变量](#2-提取环境变量)\n- [3. 删除 Webpack 文件](#3-删除-webpack-文件)\n- [4. 更新 package.json](#4-更新-packagejson)\n- [5. 创建环境变量文件](#5-创建环境变量文件)\n- [6. 修改 index.html](#6-修改-indexhtml)\n- [7. 修改 main.js](#7-修改-mainjs)\n- [8. 全局替换环境变量](#8-全局替换环境变量)\n- [9. 处理静态资源](#9-处理静态资源)\n- [10. 修改组件导入](#10-修改组件导入)\n- [11. 修复样式问题](#11-修复样式问题)\n\n---\n\n## 1. 分析 package.json\n\n读取 package.json，识别依赖类型：\n\n**保留的业务依赖：** axios, element-ui, lodash, qs, echarts, moment 等\n\n**需要删除的 devDependencies：**\n- webpack, webpack-*, babel-*\n- html-webpack-plugin, extract-text-webpack-plugin\n- css-loader, style-loader, sass-loader, vue-loader\n- node-sass（替换为 sass）\n- vue-template-compiler（Vue 2.7 不需要）\n\n**需要升级的核心依赖：**\n- vue → ^2.7.16\n- vue-router → ^3.6.5\n- vuex → ^3.6.2\n- element-ui → ^2.15.14\n- axios → 当前 1.x 安全补丁版本（执行迁移当天先核对官方 release/security，不再在 skill 中固定写死具体补丁号）\n\n**axios 迁移检查：**\n- 如果项目还在 `0.x` 或早期 `1.x`，先升级到当前 1.x 安全补丁版本，再继续做 Vite 迁移\n- 搜索是否存在 `CancelToken`、`axios.all`、`axios.spread`、`headers.post['Content-Type']`\n- 搜索是否存在裸 `import axios from 'axios'`；如果这些请求也需要统一 token、401/403、加解密逻辑，应迁回统一 request 封装\n- 对 `FormData` 上传优先直接传 `FormData`，不要继续依赖 `delete headers.post['Content-Type']` 这类旧写法\n- 重点回归上传、文件导出、401/403 跳转、刷新 token 这几类接口\n\n---\n\n## 2. 提取环境变量\n\n读取 config/dev.env.js、config/prod.env.js 等文件，转换格式：\n\n```javascript\n// Webpack 格式\nmodule.exports = { BASE_API: '\"https://api.example.com\"' }\n\n// Vite 格式 (.env.development)\nVITE_BASE_API=https://api.example.com\n```\n\n---\n\n## 3. 删除 Webpack 文件\n\n```bash\nrm -rf build/ config/\nrm -f .babelrc .postcssrc.js babel.config.js\nrm -f package-lock.json yarn.lock\n```\n\n---\n\n## 4. 更新 package.json\n\n**关键修改：**\n\n1. 添加 `\"type\": \"module\"`\n\n2. 替换 scripts：\n\n```json\n\"scripts\": {\n  \"dev\": \"vite\",\n  \"build\": \"vite build\",\n  \"preview\": \"vite preview\",\n  \"build:stage\": \"pnpm run build --mode staging\",\n  \"build:prod\": \"pnpm run build --mode production\"\n}\n```\n\n3. 新的 devDependencies：\n\n```json\n\"devDependencies\": {\n  \"@vitejs/plugin-vue2\": \"^2.3.1\",\n  \"sass\": \"^1.77.0\",\n  \"vite\": \"^4.5.5\",\n  \"vite-plugin-compression\": \"^0.5.1\"\n}\n```\n\n4. 如果使用 JSX，添加：`\"@vitejs/plugin-vue2-jsx\": \"^2.1.1\"`\n\n5. 如果使用 SVG Sprite，添加：`\"vite-plugin-svg-icons\": \"^2.0.1\"`\n\n---\n\n## 5. 创建环境变量文件\n\n创建 .env.development、.env.staging、.env.production：\n\n```bash\nVITE_RUN_ENV=development\nVITE_BASE_API=https://dev-api.example.com\n```\n\n**注意：等号后面不能有空格！**\n\n---\n\n## 6. 修改 index.html\n\n1. 移动 index.html 到项目根目录（如果不在）\n2. 修改 script 标签：\n\n```html\n<script type=\"module\" src=\"/src/main.js\"></script>\n```\n\n---\n\n## 7. 修改 main.js\n\n```javascript\n// 修改前\nimport App from \"./App\";\nnew Vue({ template: \"<App/>\", components: { App } });\n\n// 修改后\nimport App from \"./App.vue\";\nnew Vue({ render: h => h(App) });\n```\n\n---\n\n## 8. 全局替换环境变量\n\n搜索并替换：\n\n- `process.env.NODE_ENV` → `import.meta.env.VITE_RUN_ENV`\n- `process.env.VUE_APP_*` → `import.meta.env.VITE_*`\n- `process.env.BASE_API` → `import.meta.env.VITE_BASE_API`\n\n---\n\n## 9. 处理静态资源\n\n```bash\n# 重命名 static 目录为 public\nmv static public\n\n# 替换 ~@ 为 @\nfind src -type f \\( -name \"*.scss\" -o -name \"*.vue\" \\) -exec sed -i '' 's/~@/@/g' {} +\n\n# 替换 global. 为 window.\nfind src -type f \\( -name \"*.js\" -o -name \"*.vue\" \\) -exec sed -i '' 's/global\\./window./g' {} +\n```\n\n---\n\n## 10. 修改组件导入\n\n所有 .vue 文件导入必须加扩展名：\n\n```javascript\n// 修改前\nimport LoginView from \"@/views/login/index\";\n\n// 修改后\nimport LoginView from \"@/views/login/index.vue\";\n```\n\n---\n\n## 11. 修复样式问题\n\n```bash\n# 替换深度选择器\nfind src -type f -name \"*.vue\" -exec sed -i '' 's/\\/deep\\//::v-deep /g' {} +\nfind src -type f -name \"*.vue\" -exec sed -i '' 's/>>>/::v-deep /g' {} +\n```\n\nFile v1.0.0:references/vite-config.md\n\n# Vite 配置模板\n\n## 基础配置\n\n```javascript\nimport { defineConfig } from \"vite\";\nimport vue2 from \"@vitejs/plugin-vue2\";\nimport { fileURLToPath, URL } from \"node:url\";\nimport viteCompression from \"vite-plugin-compression\";\n\nexport default defineConfig({\n  base: \"/\",\n  plugins: [\n    vue2(),\n    viteCompression({\n      algorithm: \"gzip\",\n      ext: \".gz\",\n      threshold: 10240,\n      deleteOriginFile: false,\n    }),\n  ],\n  resolve: {\n    alias: {\n      \"@\": fileURLToPath(new URL(\"./src\", import.meta.url)),\n      vue: \"vue/dist/vue.esm.js\",\n    },\n  },\n  server: {\n    port: 8080,\n    open: false,\n  },\n  optimizeDeps: {\n    include: [\"vue\", \"vue-router\", \"vuex\", \"element-ui\"],\n  },\n  css: {\n    preprocessorOptions: {\n      scss: {\n        silenceDeprecations: [\"legacy-js-api\", \"import\"],\n      },\n    },\n  },\n  build: {\n    outDir: \"dist\",\n    sourcemap: false,\n    cssCodeSplit: true,\n    assetsInlineLimit: 4096,\n    chunkSizeWarningLimit: 1000,\n    rollupOptions: {\n      output: {\n        manualChunks: {\n          \"vue-vendor\": [\"vue\", \"vue-router\", \"vuex\"],\n          \"element-ui\": [\"element-ui\"],\n          \"utils-vendor\": [\"axios\", \"lodash\", \"qs\"],\n        },\n        entryFileNames: \"assets/js/[name]-[hash].js\",\n        chunkFileNames: \"assets/js/[name]-[hash].js\",\n        assetFileNames: (assetInfo) => {\n          if (assetInfo.name?.endsWith(\".css\")) {\n            return \"assets/css/[name]-[hash][extname]\";\n          }\n          if (/\\.(png|jpe?g|gif|svg|webp|ico)$/i.test(assetInfo.name || \"\")) {\n            return \"assets/images/[name]-[hash][extname]\";\n          }\n          if (/\\.(woff2?|eot|ttf|otf)$/i.test(assetInfo.name || \"\")) {\n            return \"assets/fonts/[name]-[hash][extname]\";\n          }\n          return \"assets/[name]-[hash][extname]\";\n        },\n      },\n    },\n    commonjsOptions: {\n      include: [/node_modules/],\n      transformMixedEsModules: true,\n    },\n    minify: \"esbuild\",\n    esbuildOptions: {\n      drop: [\"console\", \"debugger\"],\n    },\n  },\n});\n```\n\n---\n\n## Webpack/Vite 环境变量兼容\n\n迁移期如果仍会合入 Vue CLI/Webpack 风格代码，可以在 Vite 中自动兼容静态 `process.env.VUE_APP_*` 写法。\n\n```javascript\nimport { defineConfig, loadEnv } from \"vite\";\n\nconst createProcessEnvDefine = (env) => {\n  const define = Object.entries(env).reduce((memo, [key, value]) => {\n    if (key.startsWith(\"VITE_\")) {\n      memo[`process.env.${key.replace(/^VITE_/, \"VUE_APP_\")}`] =\n        JSON.stringify(value);\n    }\n    if (key.startsWith(\"VUE_APP_\")) {\n      memo[`process.env.${key}`] = JSON.stringify(value);\n    }\n    return memo;\n  }, {});\n\n  if (env.NODE_ENV) {\n    define[\"process.env.NODE_ENV\"] = JSON.stringify(env.NODE_ENV);\n  }\n\n  return define;\n};\n\nexport default defineConfig(({ mode }) => {\n  const env = loadEnv(mode, process.cwd(), \"\");\n\n  return {\n    define: createProcessEnvDefine(env),\n  };\n});\n```\n\n约定：`.env.*` 新增变量优先使用 `VITE_` 前缀；业务新代码优先使用 `import.meta.env.VITE_*`；老代码或并行上线代码允许使用 `process.env.VUE_APP_*`。\n\n不要整体定义 `process.env`，优先定义 `process.env.VUE_APP_XXX` 这种精确 key。该方案只兼容静态访问，不兼容 `process.env[key]` 这类动态访问。\n\n---\n\n## 代理配置\n\n```javascript\nserver: {\n  port: 8080,\n  proxy: {\n    '/api': {\n      target: 'https://api.example.com',\n      changeOrigin: true,\n      rewrite: (path) => path.replace(/^\\/api/, '')\n    }\n  }\n}\n```\n\n---\n\n## 可选插件\n\n### JSX 支持\n\n```javascript\nimport vue2Jsx from \"@vitejs/plugin-vue2-jsx\";\n\nplugins: [vue2(), vue2Jsx()]\n```\n\n### SVG Sprite\n\n```javascript\nimport { createSvgIconsPlugin } from 'vite-plugin-svg-icons'\nimport path from 'path'\n\nplugins: [\n  vue2(),\n  createSvgIconsPlugin({\n    iconDirs: [path.resolve(process.cwd(), 'src/icons/svg')],\n    symbolId: 'icon-[name]'\n  })\n]\n```\n\n### Legacy 浏览器支持\n\n```javascript\nimport legacy from '@vitejs/plugin-legacy'\n\nplugins: [\n  vue2(),\n  legacy({\n    targets: ['defaults', 'not IE 11'],\n    additionalLegacyPolyfills: ['regenerator-runtime/runtime']\n  })\n]\n```","readmeExcerpt":"Skill: Vue 2 项目 Webpack 升级到 Vite 构建工具 Owner: yyyyzj Summary: 将 Vue 2 + Webpack 项目升级到 Vite 构建工具。当用户需要将 Vue 2 项目从 Webpack 迁移到 Vite、优化构建速度、解决 Webpack 配置问题，或处理 Vue CLI/Webpack 与 Vite 环境变量兼容时使用此技能。触发场景包括：(1) 用户提到\"升级到 Vite\"、... Tags: latest:1.0.1 Version history: v1.0.1 | 2026-05-18T02:49:50.618Z | user Version 1.0.1 - 新增阶段 2 删除操作前的安全检查，包括要求在项目根目录、Git 状态干净并获得用户确认，优先使用 git rm。 - 阶段 4 安装依赖步骤细化，要求优先手动排查 peer dependency 冲突，再考虑","codeSnippets":[],"executableExamples":[{"language":"javascript","snippet":"resolve: {\n  extensions: ['.js', '.vue', '.json', '.ts'],\n  alias: { /* ... */ }\n}"},{"language":"bash","snippet":"# 搜索所有缺少 .vue 扩展名的导入\ngrep -rn \"from ['\\\"].*@/.*['\\\"]\" src --include=\"*.js\" --include=\"*.vue\" | grep -v \"\\.vue\""},{"language":"javascript","snippet":"export default defineConfig({\n  define: {\n    'process.env': {},\n    'process.platform': JSON.stringify(''),\n    'process.version': JSON.stringify('')\n  }\n})"},{"language":"bash","snippet":"find src -type f \\( -name \"*.js\" -o -name \"*.vue\" \\) -exec sed -i '' 's/global\\./window./g' {} +"},{"language":"javascript","snippet":"// ❌ 错误\n<img :src=\"require('@/assets/logo.png')\" />\n\n// ✅ 方案一：使用 import\nimport logo from '@/assets/logo.png'\n// template 中使用 :src=\"logo\"\n\n// ✅ 方案二：public 目录下的资源直接用绝对路径\n<img src=\"/images/logo.png\" />"},{"language":"javascript","snippet":"// ❌ 错误\nimport.meta.env.MODE === 'testing'  // 永远不匹配\n\n// ✅ 正确：使用自定义的 VITE_RUN_ENV 变量\nimport.meta.env.VITE_RUN_ENV === 'staging'"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: vue2-vite-upgrade\ndescription: 将 Vue 2 + Webpack 项目升级到 Vite 构建工具。当用户需要将 Vue 2 项目从 Webpack 迁移到 Vite、优化构建速度、解决 Webpack 配置问题，或处理 Vue CLI/Webpack 与 Vite 环境变量兼容时使用此技能。触发场景包括：(1) 用户提到\"升级到 Vite\"、\"迁移到 Vite\"、\"Webpack 转 Vite\"；(2) 用户抱怨 Webpack 构建慢、热更新慢；(3) 用户想要现代化 Vue 2 项目的构建工具；(4) 用户遇到 Webpack 配置复杂难以维护的问题；(5) 用户询问如何提升 Vue 2 项目的开发体验；(6) 用户提到 process.env、VUE_APP_*、import.meta.env、VITE_* 等环境变量兼容。\n---\n\n# Vue 2 项目升级到 Vite 构建工具\n\n## 重要提示\n\n⚠️ **在开始之前，必须确认用户已经备份了代码或使用了 Git 版本控制！**\n\n### 前置安全检查\n\n阶段 2 包含删除操作（`rm -rf` / `git rm`），执行前必须确认：\n\n1. 当前在项目根目录（`ls package.json` 存在）\n2. Git 状态干净或业务代码已提交/stash（`git status`）\n3. 用户已明确确认可以执行删除\n4. 删除操作优先使用 `git rm`（可通过 `git restore` 恢复），非 Git 项目才使用 `rm`\n\n## 执行原则\n\n1. **使用 TodoWrite 工具**：创建完整的任务列表，实时更新进度\n2. **分阶段执行**：按照阶段顺序执行，每个阶段完成后标记为完成\n3. **遇到问题及时沟通**：使用 AskUserQuestion 询问用户\n4. **保留业务依赖**：只删除 Webpack/Babel 相关依赖\n\n## 执行流程概览\n\n| 阶段 | 内容 |\n|------|------|\n| 阶段 1 | 项目分析 - 分析现有项目结构、依赖和配置 |\n| 阶段 2 | 文件清理与配置 - 删除 Webpack 配置，创建 Vite 配置 |\n| 阶段 3 | 代码迁移 - 修改代码以适配 Vite |\n| 阶段 4 | 测试与验证 - 安装依赖并测试运行 |\n| 阶段 5 | 部署配置（可选）- 配置自动化部署脚本 |\n\n## 参考文档\n\n- **Vite 配置模板**：见 [references/vite-config.md](references/vite-config.md)\n- **常见问题处理**：见 [references/common-issues.md](references/common-issues.md)\n- **部署配置**：见 [references/deploy-config.md](references/deploy-config.md)\n\n---\n\n## 阶段 1：项目分析\n\n使用 TodoWrite 创建任务列表，然后依次执行：\n\n1. 分析 package.json（识别业务依赖、Webpack 依赖、需升级依赖）\n2. 提取环境变量（从 config/*.env.js 转换为 .env.* 格式）\n3. 检查代理配置（记录 proxyTable）\n4. 检查特殊功能（SVG Sprite、JSX、动态路由）\n5. 单独审计 `axios`：核对官方 release/security 公告，避免继续保留已披露漏洞版本；同时排查上传、导出、401/403 拦截和裸 `axios` 调用\n\n详细步骤见 [references/migration-steps.md](references/migration-steps.md)\n\n---\n\n## 阶段 2：文件清理与配置\n\n1. 删除 Webpack 相关文件（build/、config/、.babelrc 等）\n2. 更新 package.json（添加 type: module，替换 scripts，更新依赖）\n3. 创建 vite.config.js（使用 [references/vite-config.md](references/vite-config.md) 模板）\n4. 创建环境变量文件（.env.development、.env.staging、.env.production）\n\n详细步骤见 [references/migration-steps.md](references/migration-steps.md)\n\n---\n\n## 阶段 3：代码迁移\n\n1. 修改 index.html（移动到根目录，添加 `<script type=\"module\">`）\n2. 修改 main.js（使用 render 函数，导入加 .vue 扩展名）\n3. 迁移环境变量：优先使用 import.meta.env；如项目存在并行上线或老分支合并，需要保留 process.env.VUE_APP_* 静态写法兼容层\n4. 处理静态资源（static → public，~@ → @，global → window）\n5. 修改组件导入（所有 .vue 导入加扩展名）\n6. 修复样式（/deep/ 和 >>> → ::v-deep）\n\n详细步骤见 [references/migration-steps.md](references/migration-steps.md)\n\n---\n\n## 环境变量兼容约定\n\n迁移期如果可能有 Webpack/Vue CLI 写法的需求先合入生产，不要只做 `process.env -> import.meta.env` 的单向替换。\n\n1. `.env.*` 中新增变量优先使用 `VITE_` 前缀。\n2. Vite 代码推荐使用 `import.meta.env.VITE_*`。\n3. Webpack/Vue CLI 历史代码允许继续使用 `process.env.VUE_APP_*`。\n4. 在 `vite.config.js` 中用 `loadEnv(mode, process.cwd(), '')` 读取变量，并自动把 `VITE_XXX` 映射为 `process.env.VUE_APP_XXX`。\n5. 如果 `.env.*` 已存在 `VUE_APP_XXX`，也要原样注入为 `process.env.VUE_APP_XXX`。\n6. `define` 使用精确 key，不整体替换 `process.env`，减少对第三方库和动态访问的影响。\n7. 兼容层只保证静态写法，如 `process.env.VUE_APP_BASE_API`；不承诺兼容 `process.env[key]`。\n\n具体配置示例见 [references/vite-config.md](references/vite-config.md)。\n\n---\n\n## 阶段 4：测试与验证\n\n1. 安装依赖：先执行 `pnpm install`；如"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn7ff3q7khffkrpd622drjz91n86k0sv\",\n  \"slug\": \"vue2-vite-upgrade\",\n  \"version\": \"1.0.1\",\n  \"publishedAt\": 1779072590618\n}"},{"path":"references/common-issues.md","content":"# 常见问题处理\n\n在升级过程中如果遇到以下错误，按照对应方案处理。\n\n## 目录\n\n- [问题 1：require is not defined](#问题-1require-is-not-defined)\n- [问题 2：找不到模块](#问题-2找不到模块cannot-find-module)\n- [问题 3：process is not defined](#问题-3process-is-not-defined)\n- [问题 4：global is not defined](#问题-4global-is-not-defined)\n- [问题 5：图片 require 报错](#问题-5图片-require-报错)\n- [问题 6：环境变量判断不生效](#问题-6环境变量判断不生效)\n- [问题 7：SCSS :export 不工作](#问题-7scss-export-导出变量不工作)\n- [问题 8：JSX 报错](#问题-8vue-文件中使用-jsx-报错)\n- [问题 9：HTMLCollection 没有 forEach](#问题-9htmlcollection-没有-foreach-方法)\n- [问题 10：导航守卫重定向报错](#问题-10vue-router-导航守卫重定向报错)\n- [问题 11：Element-UI 样式丢失](#问题-11element-ui-样式丢失)\n- [问题 12：SVG Sprite 图标不显示](#问题-12svg-sprite-图标不显示)\n- [问题 13：热更新不生效](#问题-13热更新-hmr-不生效)\n- [问题 14：生产构建路径错误](#问题-14生产构建后路径错误)\n- [问题 15：path 模块不可用](#问题-15nodejs-path-模块在浏览器不可用)\n- [问题 16：axios 安全漏洞](#问题-16axios-版本存在安全漏洞)\n- [问题 17：浏览器兼容性](#问题-17浏览器兼容性问题需要支持旧浏览器)\n- [问题 18：router.app.$store 为 undefined](#问题-18routerappstore-为-undefined)\n- [问题 19：chunk 体积过大](#问题-19构建后-chunk-体积过大警告)\n- [问题 20：路由懒加载](#问题-20路由组件未实现懒加载导致主包过大)\n- [问题 21：静态资源 404](#问题-21静态资源-404-错误publicdir-配置)\n- [问题 22：正则匹配 null](#问题-22正则匹配结果为-null-导致运行时报错)\n- [问题 23：动态导入冲突](#问题-23动态导入与静态导入冲突警告)\n\n---\n\n## 问题 1：require is not defined\n\n**原因：** Vite 不支持 CommonJS 的 require 语法\n\n**解决方案：**\n\n1. 将 CommonJS 依赖加入 `vite.config.js` 的 `optimizeDeps.include`\n2. 如果是代码中使用了 require，改为 import 语法\n\n---\n\n## 问题 2：找不到模块（Cannot find module）\n\n**原因：** 导入路径缺少 `.vue` 扩展名，或 Vite 默认不自动补全扩展名\n\n**解决方案：**\n\n**方案一：配置 `resolve.extensions`（推荐，批量解决）**\n\n在 `vite.config.js` 中添加：\n\n```javascript\nresolve: {\n  extensions: ['.js', '.vue', '.json', '.ts'],\n  alias: { /* ... */ }\n}\n```\n\n> 注意：Vite 官方不推荐省略 `.vue` 扩展名，配置此项后仍建议逐步补全扩展名。\n\n**方案二：手动补全扩展名**\n\n```bash\n# 搜索所有缺少 .vue 扩展名的导入\ngrep -rn \"from ['\\\"].*@/.*['\\\"]\" src --include=\"*.js\" --include=\"*.vue\" | grep -v \"\\.vue\"\n```\n\n---\n\n## 问题 3：process is not defined\n\n**原因：** 第三方库使用了 Node.js 的 process 对象\n\n**解决方案：** 在 `vite.config.js` 中添加：\n\n```javascript\nexport default defineConfig({\n  define: {\n    'process.env': {},\n    'process.platform': JSON.stringify(''),\n    'process.version': JSON.stringify('')\n  }\n})\n```\n\n---\n\n## 问题 4：global is not defined\n\n**原因：** 代码中使用了 Node.js 的 global 对象\n\n**解决方案：** 全局替换 `global.` 为 `window.`\n\n```bash\nfind src -type f \\( -name \"*.js\" -o -name \"*.vue\" \\) -exec sed -i '' 's/global\\./window./g' {} +\n```\n\n---\n\n## 问题 5：图片 require 报错\n\n**原因：** Vite 不支持 require 动态导入图片\n\n**解决方案：**\n\n```javascript\n// ❌ 错误\n<img :src=\"require('@/assets/logo.png')\" />\n\n// ✅ 方案一：使用 import\nimport logo from '@/assets/logo.png'\n// template 中使用 :src=\"logo\"\n\n// ✅ 方案二：public 目录下的资源直接用绝对路径\n<img src=\"/images/logo.png\" />\n```\n\n---\n\n## 问题 6：环境变量判断不生效\n\n**原因：** Vite 使用 `import.meta.env` 而非 `process.env`，且只暴露 `VITE_` 前缀变量\n\n**解决方案：**\n\n**情况一：新代码用了 Vite 内置 MODE 变量但不匹配**\n\n```javascript\n// ❌ 错误\nimport.meta.env.MODE === 'testing'  // 永远不匹配\n\n// ✅ 正确：使用自定义的 VITE_RUN_ENV 变量\nimport.meta.env.VITE_RUN_ENV === 'staging'\n```\n\n**情况二：老代码使用 `process.env.VUE_APP_*`，通过配置兼容**\n\n在 `vite.config.js` 中用 `define` 静态替换，无需修改业务代码：\n\n```javascript\nimport { defin"},{"path":"references/deploy-config.md","content":"# 部署配置\n\n## NPM 脚本\n\n```json\n\"scripts\": {\n  \"dev\": \"vite\",\n  \"start\": \"pnpm run dev\",\n  \"build\": \"vite build\",\n  \"preview\": \"vite preview\",\n  \"build:stage\": \"pnpm run build --mode staging\",\n  \"build:prod\": \"pnpm run build --mode production\",\n  \"clean\": \"rimraf dist.zip\",\n  \"zip\": \"node export-zip.cjs\",\n  \"upload:stage\": \"node ftp-deploy.js stage\",\n  \"upload:prod\": \"node ftp-deploy.js production\",\n  \"deploy:stage\": \"pnpm run build:stage && pnpm run clean && pnpm run zip && pnpm run upload:stage\",\n  \"deploy:prod\": \"pnpm run build:prod && pnpm run clean && pnpm run zip && pnpm run upload:prod\"\n}\n```\n\n---\n\n## 压缩脚本 (export-zip.cjs)\n\n```javascript\nconst fs = require('fs');\nconst archiver = require('archiver');\n\nconst homedir = __dirname;\nconst target = ['dist'];\n\nconst output = fs.createWriteStream(homedir + '/dist.zip');\nconst archive = archiver('zip', { zlib: { level: 9 } });\n\narchive.on('error', function (err) { throw err; });\n\noutput.on('close', function () {\n    console.log(`压缩完毕，大小: ${(archive.pointer() / 1024 / 1024).toFixed(1)} MB`);\n});\n\narchive.pipe(output);\nfor (let i of target) {\n    archive.directory(i, i);\n}\narchive.finalize();\n```\n\n---\n\n## FTP 部署脚本 (ftp-deploy.js)\n\n> ⚠️ **安全提示**：部署凭证**不要**硬编码在脚本中或使用 Git 提交。通过环境变量或 `.env` 文件（已加入 `.gitignore`）传入。生产环境部署只在显式确认后执行。\n\n```javascript\nimport FtpDeploy from \"ftp-deploy\";\nimport { fileURLToPath } from \"node:url\";\nimport path from \"node:path\";\n\nconst ftpDeploy = new FtpDeploy();\nconst env = process.argv.splice(2)[0];\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// 从环境变量读取部署凭证，未设置时报错\nfunction getEnvConfig(targetEnv) {\n    const prefix = targetEnv === 'production' ? 'PROD' : 'STAGE';\n    const user = process.env[`${prefix}_FTP_USER`];\n    const password = process.env[`${prefix}_FTP_PASSWORD`];\n    const host = process.env[`${prefix}_FTP_HOST`];\n    const remoteRoot = process.env[`${prefix}_FTP_REMOTE_ROOT`];\n    if (!user || !password || !host || !remoteRoot) {\n        console.error(`\\x1b[31mError: Missing ${prefix}_FTP_* environment variables\\x1b[0m`);\n        process.exit(1);\n    }\n    return { user, password, host, remoteRoot };\n}\n\nif (!env || !['stage', 'production'].includes(env)) {\n    console.error('\\x1b[31m%s\\x1b[0m', 'error: 环境参数错误，请使用 stage 或 production');\n    process.exit(1);\n}\n\nconst envConfig = getEnvConfig(env);\n\nconst config = {\n    ...envConfig,\n    port: 21,\n    localRoot: __dirname + \"/\",\n    include: [\"dist.zip\"],\n    deleteRemote: false,\n    forcePasv: true,\n    sftp: false,\n};\n\nftpDeploy.deploy(config)\n    .then(() => console.log('upload completed'))\n    .catch((err) => console.log(err));\n```\n\n**使用方式：**\n\n```bash\n# 临时设置（单次部署）\nSTAGE_FTP_USER=myuser STAGE_FTP_PASSWORD=mypass STAGE_FTP_HOST=ftp.example.com STAGE_FTP_REMOTE_ROOT=/staging/ pnpm run upload:stage\n\n# 或通过 .env.local 文件（需已加入 .gitignore）\n# 在 .env.local 中写入：\n#   STAGE_FTP_USER=myuser\n#   STAGE_FTP_PASSWORD=mypass\n#   STAGE_FTP_HOST=ftp.example.com\n#   STAGE_FTP_REMOTE_ROOT=/stagi"},{"path":"references/migration-steps.md","content":"# 迁移步骤详解\n\n## 目录\n\n- [1. 分析 package.json](#1-分析-packagejson)\n- [2. 提取环境变量](#2-提取环境变量)\n- [3. 删除 Webpack 文件](#3-删除-webpack-文件)\n- [4. 更新 package.json](#4-更新-packagejson)\n- [5. 创建环境变量文件](#5-创建环境变量文件)\n- [6. 修改 index.html](#6-修改-indexhtml)\n- [7. 修改 main.js](#7-修改-mainjs)\n- [8. 全局替换环境变量](#8-全局替换环境变量)\n- [9. 处理静态资源](#9-处理静态资源)\n- [10. 修改组件导入](#10-修改组件导入)\n- [11. 修复样式问题](#11-修复样式问题)\n\n---\n\n## 1. 分析 package.json\n\n读取 package.json，识别依赖类型：\n\n**保留的业务依赖：** axios, element-ui, lodash, qs, echarts, moment 等\n\n**需要删除的 devDependencies：**\n- webpack, webpack-*, babel-*\n- html-webpack-plugin, extract-text-webpack-plugin\n- css-loader, style-loader, sass-loader, vue-loader\n- node-sass（替换为 sass）\n- vue-template-compiler（Vue 2.7 不需要）\n\n**需要升级的核心依赖：**\n- vue → ^2.7.16\n- vue-router → ^3.6.5\n- vuex → ^3.6.2\n- element-ui → ^2.15.14\n- axios → 当前 1.x 安全补丁版本（执行迁移当天先核对官方 release/security，不再在 skill 中固定写死具体补丁号）\n\n**axios 迁移检查：**\n- 如果项目还在 `0.x` 或早期 `1.x`，先升级到当前 1.x 安全补丁版本，再继续做 Vite 迁移\n- 搜索是否存在 `CancelToken`、`axios.all`、`axios.spread`、`headers.post['Content-Type']`\n- 搜索是否存在裸 `import axios from 'axios'`；如果这些请求也需要统一 token、401/403、加解密逻辑，应迁回统一 request 封装\n- 对 `FormData` 上传优先直接传 `FormData`，不要继续依赖 `delete headers.post['Content-Type']` 这类旧写法\n- 重点回归上传、文件导出、401/403 跳转、刷新 token 这几类接口\n\n---\n\n## 2. 提取环境变量\n\n读取 config/dev.env.js、config/prod.env.js 等文件，转换格式：\n\n```javascript\n// Webpack 格式\nmodule.exports = { BASE_API: '\"https://api.example.com\"' }\n\n// Vite 格式 (.env.development)\nVITE_BASE_API=https://api.example.com\n```\n\n---\n\n## 3. 删除 Webpack 文件\n\n> ⚠️ **执行前确认**：以下命令会永久删除文件。确保已在正确的项目根目录执行，且代码已提交或备份。\n\n**3.1 前置安全检查**\n\n```bash\n# 确认在项目根目录\npwd\n# 确认 Git 状态干净（无未提交的破坏性变更）\ngit status --short\n# 如果存在未提交的业务代码，先提交或 stash\ngit stash push -m \"before vite migration\"\n```\n\n**3.2 使用 Git 删除（可撤销）**\n\n优先使用 `git rm` 而非 `rm`，以便通过 `git restore` 恢复：\n\n```bash\n# 删除 Webpack 构建目录\ngit rm -rf build/ config/\n# 删除 Babel/PostCSS 配置\ngit rm -f .babelrc .postcssrc.js babel.config.js\n# 删除旧锁文件（pnpm 项目不需要 npm/yarn 锁文件）\ngit rm -f package-lock.json yarn.lock 2>/dev/null || true\n```\n\n如果项目未纳入 Git 版本控制，使用以下命令（**不可撤销，执行前务必确认**）：\n\n```bash\nrm -rf build/ config/\nrm -f .babelrc .postcssrc.js babel.config.js\nrm -f package-lock.json yarn.lock\n```\n\n**3.3 删除后验证**\n\n```bash\n# 确认目标目录已不存在\nls build config .babelrc .postcssrc.js babel.config.js 2>&1 | grep -q \"No such\" && echo \"清理完成\" || echo \"警告：部分文件仍存在\"\n```\n\n---\n\n## 4. 更新 package.json\n\n**关键修改：**\n\n1. 添加 `\"type\": \"module\"`\n\n2. 替换 scripts：\n\n```json\n\"scripts\": {\n  \"dev\": \"vite\",\n  \"build\": \"vite build\",\n  \"preview\": \"vite preview\",\n  \"build:stage\": \"pnpm run build --mode staging\",\n  \"build:prod\": \"pnpm run build --mode production\"\n}\n```\n\n3. 新的 devDependencies：\n\n```json\n\"devDependencies\": {\n  \"@vitejs/plugin-vue2\": \"^2.3.1\",\n  \"sass\": \"^1.77.0\",\n  \"vite\": \"^4.5.5\",\n  \"vite-plugin-compression\": \"^0.5.1\"\n}\n```\n\n4. 如果使用 JSX，添加：`\"@vitejs/plugin-vue2-jsx\": \"^2.1.1\"`\n\n5. 如果使用 SVG Sprite，添加：`\"vite-plugin-svg-icons\": \"^2.0.1\"`\n\n---\n\n## 5. 创建环境变量文件\n\n创建 .env.development、.env.staging、.env.production：\n\n```bash\nVITE_RUN_ENV=development\nVITE_BASE_"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":null,"editorialQuality":{"score":100,"threshold":65,"status":"thin","wordCount":1254,"uniquenessScore":42,"reasons":["uniqueness-below-45"]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-10T17:57:38.468Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-10T17:57:38.468Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T21:53:44.356Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}