项目:PEXELS 壁纸应用 | 版本:v1.1 | 生成日期:2026-10-06 | 依据:源码分析
本项目没有配置中心,全部配置分散在根目录的少量文件里,且每个文件只服务一个生命周期阶段。先给全景清单,后续各节逐文件逐项展开:
| 文件 | 作用阶段 | 核心内容 |
|---|---|---|
.env.example | 开发期模板 | 4 个变量的说明与占位(唯一入库的 env 文件) |
.env.development | pnpm dev | 真实 Key ⚠️(不入库) |
.env.production | pnpm build | 真实 Key ⚠️(不入库) |
.gitignore / .npmrc / .prettierrc | 全程 | 忽略规则 / pnpm 行为 / 格式化 |
vite.config.ts | dev + build | 插件、px-to-viewport、别名、代理、分包 |
tsconfig.json / tsconfig.node.json | type-check | 严格编译选项 / 配置文件自身 |
package.json | 全程 | scripts 与依赖(解析见《00-项目总览》第 3 节) |
与配置相关的类型声明在 src/env.d.ts(ImportMetaEnv),运行时消费点在 src/api/pexels.ts——这三者构成 Key 的完整配置链路,见第 3 节。
逐行语义(原文引用见仓库):
| 变量 | 模板值 | 语义 | 被谁读取 |
|---|---|---|---|
VITE_PEXELS_API_KEY | '' | Pexels API Key,直连 api.pexels.com/v1 时经 Authorization 头透传(无 Bearer 前缀) | src/api/pexels.ts 第 13 行 |
VITE_PEXELS_PROXY_BASE | '' | 反向代理地址;配置后请求改走代理、Key 由服务端注入、前端产物零 Key | src/api/pexels.ts 第 24 行 |
文件头注释有三层信息:复制用法(复制为 .env.development/.env.production 并填真实值)、安全警告("VITE_ 前缀的变量会被打包进前端产物,任何值都不具备真正的保密性,生产环境建议使用 VITE_PEXELS_PROXY_BASE")、历史记录(VITE_BASE_API 与 VITE_APP_SECRET 已随账号体系移除,模板中已删但类型声明残留,见《13-技术债务与演进建议》D-03)。
两文件当前各含两项:VITE_BASE_API(开发 /api、生产 /prod-api,无任何代码消费,历史遗留赋值)与 VITE_PEXELS_API_KEY(填有同一个真实 Key,即《08-安全与权限设计》第 2 节的 P0 问题 🔴——本篇不重复处置步骤,只强调与配置工作相关的两点:轮换后两文件需同步替换;切代理方案后 Key 应从两文件中完全移除并代之以 VITE_PEXELS_PROXY_BASE)。两文件均被 .gitignore 的 .env.* 规则忽略(!.env.example 例外放行),文件头注释写明"含 API Key 等敏感信息,禁止入库"。
加载规则的三个实用细节:Vite 按"模式名匹配文件名"加载(dev 命令加载 .env.development,build 默认 production 模式加载 .env.production,.env 无后缀文件两者都加载——本仓库未使用);pnpm preview 不加载任何代理逻辑、只是静态伺服 dist(《06-部署运行》第 2 节);env 变更后必须重启 dev server,因为 src/api/pexels.ts 的 API_KEY/PROXY_BASE 是模块首次求值时固化的常量(热更新不会重新求值)。
按部署形态从弱到强给三档:档一(本地自用):Key 只放 .env.development,走 dev 内置代理,不提交、不共享文件,接受"本地明文"的残余风险;档二(公网直连部署):接受 Key 进产物的现实,配合 Pexels 侧的配额监控与随时轮换预案——不推荐但可行;档三(生产推荐):VITE_PEXELS_PROXY_BASE 指向服务端反向代理,新 Key 只存在于代理服务器的环境变量(如 systemd EnvironmentFile 或容器 secret),前端仓库与产物零 Key,代理契约(路径转发、服务端注入 Authorization、透传 4xx)见《03-接口设计》第 4 节。任何档位下都遵守四条铁律:不硬编码进源码(src/utils/pexels.ts 注释原话"禁止硬编码")、不进版本库、不进聊天记录与工单、泄露即轮换。
从"写进 env"到"发出请求头"的完整链路只有五步,全部集中在两个文件:
src/env.d.ts 的 ImportMetaEnv 给 VITE_PEXELS_API_KEY? 与 VITE_PEXELS_PROXY_BASE? 类型(可选 string),编辑器据此补全;src/api/pexels.ts 第 23~25 行,PROXY_BASE = 显式配置 || (DEV ? '/api/pexels' : '')——显式代理任何环境生效;否则 dev 自动落 Vite 内置代理(规避 localhost 直连被 Cloudflare 拦截),prod 落官方直连;client = API_KEY || PROXY_BASE ? new PexelsClient(API_KEY, PROXY_BASE || undefined) : null——两者皆空则不实例化,全部接口走降级分支且零网络请求;src/utils/pexels.ts 构造器——直连模式无 Key 抛错,代理模式允许空 Key;PexelsClient.request 中 if (this.apiKey) headers.Authorization = this.apiKey——代理模式下前端不发此头(Key 由服务端注入)。这条链路的工程含义:《11-扩展开发指南》SOP-D 的"来源切换"只需替换第 2、3 步;Key 轮换只影响第 1 步的值与重启;排查"请求到底走的哪条通道"看第 2 步的计算结果(Network 面板里 /api/pexels 前缀 = dev 代理,api.pexels.com = 直连,自定义域名 = 显式代理)。
VUE_VENDOR = ['vue', '@vue', 'vue-router', 'pinia']——注释给出两个教训:@vue 必须单独列出(Vue 3 运行时拆在 @vue/runtime-core 等子包,只匹配 /node_modules/vue/ 会让整个运行时落进 vendor-misc 并被入口预加载);分组名不能写成 '@vue/'(下方模板已带尾斜杠,多写会拼成双斜杠匹配不到)。UTILS/ANIMATION/MEDIA_VENDOR 分别收 @vueuse/core、gsap、file-saver。resolveVendorChunk 按 id.includes('/node_modules/<pkg>/') 逐组匹配,未命中落 vendor-misc。这段常量与 build.rollupOptions.output.manualChunks 配合,产物的实际分组效果见《10-性能设计与优化》第 5 节的实测表。
postcssPxToViewport 的七个参数逐个解释:unitToConvert: 'px' 只转裸 px;viewportWidth: 375 设计稿宽(1px → 100/375 vw);unitPrecision: 5;propList: ['*'] 全属性;minPixelValue: 1 1px 边框不转(防细线丢失);mediaQuery: false 媒体查询内 px 不转(桌面断点 xl: 的像素布局因此不受影响);include: [/\.vue/] 只转 .vue 文件——Tailwind 产物来自 styles/index.css、不匹配 include,其 rem 由根字号驱动,任意值 px(w-[15px])因此成为精确尺寸逃生通道。配置位置的教训(注释原文):必须显式配在 vite.config.ts 而非 postcss.config.js——Vite 8(rolldown)dev 下后者对 .vue 样式块加载不可靠,会造成 dev/prod 不一致(《06-部署运行》FAQ 表)。
@ → src 与 tsconfig 的 paths 保持一致(两边都要改,只改一边会出现"构建能过、类型报错"或反之)。server.proxy 把 /api/pexels 转发到 https://api.pexels.com/v1:changeOrigin: true 改写 Host 头、rewrite 去掉前缀——这是《03-接口设计》第 4 节"代理模式服务端契约"的参考实现,也解释了 src/api/pexels.ts 中 PROXY_BASE 的 dev 默认值为何是 /api/pexels。该代理仅 dev 存在,生产需自建(档三方案)。
四个开关的动机:sourcemap: false 不把源码结构暴露到线上(兼安全考量,《08-安全与权限设计》S-08);reportCompressedSize: false 跳过 gzip 体积计算缩短构建耗时;chunkSizeWarningLimit: 300 是分包退化的唯一报警信号,注释明令不许为消除告警而调大——正确动作是去 4.1 的分组补登记;manualChunks 把 node_modules 全部经 resolveVendorChunk 分组(业务代码按路由自动分包)。base 未设置——产物以 / 为根,子路径部署需先补此配置(《06-部署运行》4.2 的 ⚠️ 待确认项)。
主 tsconfig 的关键项按"为什么开"归类(检查效力分析见《09-测试与质量保障》第 2.1 节):正确性组——strict: true(全家族)、noUncheckedIndexedAccess: true(裸下标返回 T|undefined,本项目最值钱的开关)、noFallthroughCasesInSwitch: true(changeTheme 的 switch)、noUnusedLocals/noUnusedParameters: true(死代码不存活);工具链对齐组——moduleResolution: 'Bundler' + module: 'ESNext' + isolatedModules: true(与 Vite 转译模型一致)、target: 'ES2020' + lib: ['ES2020','DOM','DOM.Iterable']、types: ['vite/client'](ImportMeta.env 类型来源);工程便利组——baseUrl + paths(@ 别名)、jsx: 'preserve'、resolveJsonModule、skipLibCheck: true(跳过依赖类型检查提速)、allowJs: true + checkJs: false(JS 参与编译不查类型——遗留盲区之一);include 覆盖 src 下 ts/d.ts/tsx/vue/js 五类。tsconfig.node.json 用 composite: true 被主配置 reference,只管 vite.config.*,types: ['node'] 提供Node 类型——vite.config.ts 里的 path/url 导入靠它通过检查。
.npmrc 只有一行 optional=false,注释解释了完整因果链:禁用全部可选依赖 → vite 的 optional 依赖 sass 及其 optional 依赖 @parcel/watcher 不被安装 → 规避 pnpm 11 的 [ERR_PNPM_IGNORED_BUILDS] 构建脚本报错,且无需 pnpm-workspace.yaml。副作用提示:未来若真要用 sass 需移除该行并接受 pnpm 的构建脚本审批流程。.prettierrc 四项(semi:false、singleQuote:true、trailingComma:'none'、printWidth:100)加 prettier-plugin-tailwindcss 类名排序;无钩子集成,格式化手动执行(《09-测试与质量保障》第 3 节)。.gitignore 的关键三条:dist(产物不入库——因此工作区的 dist 属本地快照,部署需重新构建)、.env 与 .env.*(仅 !.env.example 放行)、.codebuddy/(Agent 工具数据)。
pnpm build 的产物结构(实测 dist/):index.html(含第三方统计 script 与四条 modulepreload)、favicon.ico、assets/ 下约 25 个带内容 hash 的 js/css 文件。产物视角的三个事实:纯静态——无服务端进程,任意静态服务器/CDN/对象存储可托管;hash 命名——assets 可开永久缓存(Cache-Control: public, immutable),index.html 需短缓存或不缓存;预加载清单由构建器自动生成——当前入口预加载 rolldown-runtime、vendor-misc、vendor-vue、vendor-utils 四个 chunk(《10-性能设计与优化》第 5 节的解读)。
部署配置的编写要点在《06-部署运行》4.2 已有完整示例与三种形态的选型论证,本篇从配置视角补三条增量细节。其一,缓存头是"配置里最影响体验的一行":SPA 的 index.html 若被 CDN 长缓存,发版后用户会长期停在旧版本——且旧 index.html 引用的旧 hash chunk 一旦被清出缓存就会出现加载失败;正确组合是 HTML 用 no-cache(或 max-age=0 + must-revalidate)强制回源校验、assets 用 immutable 长缓存,两者缺一不可。其二,回退规则的覆盖面要包含所有"可直达路由":/pins/*、/videos/*、/collections/* 三段前缀都必须落回 index.html,try_files $uri $uri/ /index.html 的通配写法天然覆盖;但对象存储的"错误页回退"配置往往只支持单一 404 页,配置后务必实际刷新一条详情路由验证。其三,压缩配置的取舍:Vite 产物是未压缩 JS(reportCompressedSize 关闭只影响构建报告、不影响产物本身),gzip/brotli 应在服务器层开(nginx 的 gzip 或 Caddy 默认),不必预压缩产物——除非用对象存储且其网关不支持动态压缩,才考虑构建期出 .gz/.br 双份。
部署三要素(SPA 回退、根路径、直连需真实域名)与三种形态的选型论证见《06-部署运行》第 4.2 节;与配置直接相关的最后一条提醒:部署前重新构建而非复制现存的 dist/——现存的这份内嵌旧 Key(D-01),且其 env 快照可能已过期。
以速查表收尾,给出常见配置变更的标准动作与易错点:
| 变更 | 动作 | 易错点 |
|---|---|---|
| 换/轮换 Pexels Key | 改两个 env 文件 → 重启 dev → pnpm build 重出产物 | 忘重启(模块常量固化);忘重建 dist |
| 切代理方案 | env 填 VITE_PEXELS_PROXY_BASE,移除 Key;代理实现四点契约 | 代理吞 429 导致熔断失效(《03-接口设计》第 4 节) |
| 新增环境变量 | env 赋值 + src/env.d.ts 补 ImportMetaEnv 声明 + 消费点用 import.meta.env.X | 忘了 VITE_ 前缀(不暴露给客户端);声明与实际不同步 |
| 改分包分组 | 只动 vite.config.ts 的分组常量与 resolveVendorChunk | 写成 '@vue/' 双斜杠;为消告警调大阈值 |
| 改 px-to-viewport | 只动 css.postcss,勿迁 postcss.config.js | dev/prod 不一致回归 |
| 子路径部署 | 补 base,复查统计脚本路径与 px 转换 | 改动点分散,首次部署不建议(《06-部署运行》4.2) |
| 升级 Node/pnpm | 核对 .npmrc 的 optional=false 是否仍必要 | pnpm 版本差异引发的构建脚本报错 |
配置层的全部内容讲完了,收尾给一条配置漂移的防线上:本项目所有配置文件都没有 schema 校验与 CI 把关,配置错误的表现形态往往是"静默劣化"而非报错——px-to-viewport 移位后 dev 看不出异常、@vue 分组写错后构建成功但首屏变大、base 缺失只影响子路径场景。因此配置变更的验收永远不能只看"构建是否成功",而要跑一遍对应的行为验证:改 px 配置后比对 dev 与 prod 的间距;改分包后看 dist 文件清单与首屏 preload;改部署配置后刷新详情路由。把《06-部署运行》第 4.2 节的发布校验清单与本篇第 8 节的动作表配套使用,配置漂移就能被拦在上线前。它印证了《00-项目总览》的一个判断:这个项目的配置面刻意保持极小——两个真正被消费的环境变量、一个配置文件承担全部构建决策——配置越少,环境的确定性越高;唯一的例外恰恰是最敏感的那一项(真实 Key 落盘),它不是"配置多"的问题而是"配置卫生"的问题,处置路径见第 2.3 节与《08-安全与权限设计》。