本地开发、构建与运行
本页说明 MyIP 从本地依赖准备、双进程开发到前端构建和构建后运行的完整路径,重点解释不同启动入口、API 代理以及静态资源行为。
目的与范围
面向需要修改代码、验证构建或复现运行问题的开发者。本页覆盖包管理器与 Node.js 要求、项目脚本、Vite 配置、前端静态服务器和本地排错,不展开 IP 查询、DNS、认证等业务 API 的实现,也不替代容器部署、监控或国际化专题。
证据来自 package.json、vite.config.js 和 frontend-server.js。本页没有核验后端内部初始化、外部服务凭据要求和测试用例内容,因此不承诺“无需任何配置即可启用全部功能”。
概述:先选择正确的运行模式
| 目标 | 入口 | 前端服务 | 后端启动方式 |
|---|---|---|---|
| 修改代码并联调 | pnpm run dev | Vite 开发服务器 | nodemon,预加载 Sentry instrumentation |
| 生成前端产物 | pnpm run build | 不启动服务器 | 不启动后端 |
| 预览 Vite 产物 | pnpm run preview | vite preview | 脚本不启动后端 |
| 运行已构建应用 | pnpm run start | Node.js + Express 静态服务器 | Node.js,预加载 Sentry instrumentation |
| 单独运行一端 | pnpm run start-frontend / pnpm run start-backend | 按所选脚本启动 | 按所选脚本启动 |
| 测试后构建 | pnpm run check | 测试成功后运行构建 | 不作为服务启动入口 |
这些入口是同一工程的不同执行路径,而不是彼此等价的命令。特别是 start 不包含构建,preview 也不等于完整的前后端启动。脚本定义见 package.json。
架构与进程边界
Sources: package.json、vite.config.js、frontend-server.js
开发时由 Vite 接收页面请求;构建后则由 Express 从 dist 提供文件。两种模式都设置了 /api 到本机后端的代理,使页面资源与 API 可以通过同一个前端入口访问。后端是独立进程,并不嵌入 Vite 或前端 Express 应用。
dev 和 start 使用 concurrently 启动两端,但脚本中没有后端就绪检查或启动顺序门控,不能把“前端已可访问”等同于“后端已准备好”。
环境准备与首次启动
工具链要求
项目元数据明确声明:
1 "type": "module",
2 "packageManager": "pnpm@12.4.2",
3 "engines": {
4 "node": "^24.15.0 || >=26.0.0"
5 },Source: package.json
- 使用项目指定的 pnpm 版本,避免把 npm 或其他包管理器的生命周期行为直接套用到该项目。
- Node.js 范围不是简单的“24 以上”:允许
24.15.0起的 24.x,或 26.0.0 及以上;25.x 不在声明范围内。 - 工程以 ES module 运行。新增 Node.js 入口时应与该模块模式保持一致。
- Vite、nodemon、Vue 编译插件等位于开发依赖中。本地开发和本地构建都需要安装开发依赖,不能只准备生产依赖。依赖划分见 package.json。
建议操作顺序
- 在仓库根目录准备符合声明范围的 Node.js 和
pnpm@12.4.2。 - 安装项目依赖;本地可使用
pnpm install。这是操作建议,不是新增的仓库脚本。 - 按需设置环境变量;Vite 配置和前端服务器都会调用
dotenv.config()。 - 运行
pnpm run dev,使用 Vite 实际打印的地址访问。配置请求的前端端口默认是18966,开发服务器监听0.0.0.0。 - 同时观察前后端进程输出,再验证需要的业务功能。代理目标端口默认是
11966,但本页未读取后端监听实现,不能据此推断后端初始化一定成功。
端口与环境加载依据:vite.config.js、vite.config.js、frontend-server.js。
开发模式的真实启动入口
"dev": "concurrently \"vite\" \"nodemon --import ./sentry-instrument.js backend-server.js\"",Source: package.json
该脚本并行启动 Vite 与 nodemon。后端通过 --import 预加载 instrumentation,而不是等业务入口运行后再加载。其具体监控初始化和环境开关未在本页核验。脚本没有显式设置 nodemon 的监听目录、扩展名或忽略规则,因此这里不列出未经核实的自动重启范围。
Vite 开发入口配置如下:
1 server: {
2 host: '0.0.0.0',
3 port: frontEndPort,
4 proxy: {
5 '/api': `http://localhost:${backEndPort}`
6 },
7 allowedHosts: ['dev.ipcheck.ing', 'test.ipcheck.ing'],
8 }Source: vite.config.js
allowedHosts 是 Vite 配置中的显式允许项,不应将它理解为整个生产服务器的域名策略。使用自定义开发域名时应检查该配置及 Vite 的主机校验行为;开发服务绑定所有网卡,也应注意本机防火墙和共享网络的可达性。
构建、检查与预览
构建链路
build 执行 vite build。项目另外定义 postbuild:先检查 scripts/purge-index-cache.js 是否存在;存在则用当前 Node.js 可执行文件同步运行它,继承标准输入输出,并使用子进程状态退出,状态缺失时按 1 处理;不存在则跳过。该脚本的实际缓存服务、凭据和清理对象未读取,不能仅凭名称保证缓存已刷新。
check 使用 && 串联测试和构建,测试失败时不会继续构建:
"test": "node --import ./tests/setup.js --test tests/*.test.js",
"check": "pnpm run test && pnpm run build",
"preview": "vite preview",Source: package.json
测试入口采用 Node.js 内置测试运行器,预加载测试初始化模块,再执行 tests/*.test.js。这只能证明测试执行方式,不能证明某个功能已有测试覆盖,也不能证明测试在当前环境通过。
Vite 编译行为
以下配置会直接影响本地修改与构建结果:
| 配置 / 插件 | 实际行为与开发影响 |
|---|---|
| Vue 插件 | 将 pwa-install 识别为自定义元素,避免按普通 Vue 组件解析 |
| Tailwind CSS 插件 | 参与 Vite 样式处理链 |
@ 路径别名 | 映射到 /frontend |
siteUrlHtmlPlugin() | 读取 VITE_SITE_URL,去除首尾空白及结尾斜杠;为空时删除特定 HTML 标记块,否则替换 __SITE_URL__ |
localeStripPlugin() | 在内置 JSON 处理之前,把 frontend/locales 下 JSON 交给 stripPack 处理;设计目标是让未翻译空字符串触发运行时回退 |
localePreloadPlugin() | 从构建 bundle 找到语言包 chunk,并向 HTML 注入预加载脚本;没有匹配 chunk 时不注入 |
CodeInspectorPlugin | 指定 Vite bundler,隐藏 DOM 路径属性,并将复制行为设为文件路径 |
| Sentry Vite 插件 | 条件启用且位于插件链末尾,以处理最终输出 chunk |
依据:vite.config.js、vite.config.js。
语言包预加载优先使用保存的偏好,然后匹配 ?hl= 或浏览器语言,最后使用 en;预加载语言变体、基础语言和英语组成的回退链。其目的在源码注释中明确:让语言包下载与主 bundle 并行,减少应用挂载前的串行网络等待。开发模式没有构建 bundle,不应期待相同的 HTML 注入结果。详见 vite.config.js。
分包、资源命名与告警
manualChunks(id) 先统一路径分隔符,再优先按第三方包分组,随后按源码路径分组;不命中时返回 undefined,交给打包器继续决定。第三方分组包含 vendor、chart、speedtest、browser-detect,源码分组包含 utils-getips 和 utils-auth。
普通资源采用 assets/[name]-[hash][extname];字体在命中 .woff / .woff2 判断时采用 fonts/[name][extname];JS chunk 采用带 hash 的名称,utils- 开头的 chunk 额外放入 assets/utils/。这些命名与运行服务器的长缓存策略相关。
配置将 chunkSizeWarningLimit 设为 1000,并只过滤来自 @vueuse/core 的 INVALID_ANNOTATION 告警,其他告警仍交给默认处理器。文件还声明了 splitChunks 字段,但本页没有通过构建验证其在当前 Vite 版本中的生效情况,不能把这些值当作已验证的产物大小保证。
依据:vite.config.js、vite.config.js、vite.config.js。
预览与完整运行的区别
pnpm run preview 只调用 vite preview。已读取配置没有单独的 preview 设置,因此本页不把开发模式的监听地址、端口和代理行为当作已验证的预览契约。
需要验证仓库自带 Express 静态服务器及其代理、缓存和 SPA 路由行为时,应先完成构建,再使用 pnpm run start。不要把 dev 和 start 同时当作同一套默认端口上的服务运行。
构建后运行:请求如何经过前端服务器
启动与 API 代理
构建后运行脚本直接启动两个 Node.js 进程,不会自动生成新产物:
"start-backend": "node --import ./sentry-instrument.js backend-server.js",
"start-frontend": "node frontend-server.js",
"start": "concurrently \"node frontend-server.js\" \"node --import ./sentry-instrument.js backend-server.js\"",Source: package.json
前端服务器以自身文件位置确定 dist 路径,然后按顺序注册 API 代理、静态文件服务和 SPA 回退。API 代理先注册,避免被后面的页面回退吞掉:
1frontendApp.use('/api', createProxyMiddleware({
2 target: `http://localhost:${backEndPort}/api`,
3 changeOrigin: true
4}));Source: frontend-server.js
这里的代理目标显式带有 /api,与 Vite 配置中只指定后端 origin 的写法不同。修改时应尊重各自中间件挂载语义,不要为了形式一致直接互换。前端服务最后调用 listen(frontEndPort, callback) 并记录就绪日志;该调用没有显式传入 host,不能把日志中的 localhost 当作只监听回环地址的保证。见 frontend-server.js。
请求处理流程
Source: frontend-server.js
这套顺序区分了真实资源与客户端路由:页面深链接没有对应文件时,可以返回应用入口;丢失的 JS/CSS 文件则不应得到 HTML 响应,否则浏览器会把资源缺失表现为脚本解析错误。
实际回退条件如下:
1frontendApp.use((req, res, next) => {
2 if (req.method !== 'GET' || !req.accepts('html')) return next();
3 if (req.path.split('/').pop().includes('.')) return next();
4 res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
5 res.sendFile(path.join(distDir, 'index.html'));
6});Source: frontend-server.js
注意实现检查的是“路径最后一段包含点号”,并非严格识别文件扩展名。因此客户端路由最后一段如果含点,也会跳过 SPA 回退。非 GET 请求同样不会回退。
静态缓存与更新验证
setStaticHeaders(res, filePath) 将文件路径转为相对 dist 的路径,再按以下优先级设置响应头:
| 资源类别 | 缓存策略 | 本地验证含义 |
|---|---|---|
assets/、fonts/ | public,365 天,immutable | 带 hash 资源可通过新 URL 更新;稳定字体文件名需谨慎替换 |
favicons/ | public,30 天 | 同名替换可能继续命中旧缓存 |
| PNG/JPG/JPEG/WebP/SVG/ICO | public,7 天 | 判断不区分扩展名大小写;源码注释要求更新此类图片时重命名 |
HTML、manifest.webmanifest、根级 SEO 文件 | 浏览器 max-age=0,共享缓存 s-maxage=86400,must-revalidate | 浏览器缓存与代理/CDN 缓存要分开检查 |
| 其他静态文件 | public,1 小时 | 不属于上述分类时的默认策略 |
| SPA 回退页面 | no-store, no-cache, must-revalidate | 与静态层命中的 /、/index.html 不同 |
根级 SEO 集合包含 llms.txt、llms-full.txt、sitemap.xml、robots.txt。缓存策略依据 frontend-server.js。缓存刷新与浏览器实际请求 URL 应一起验证,不能仅以构建退出成功判断页面一定更新。
配置选项
下表仅列出已读取入口直接使用的环境变量,不是整个应用的完整环境配置清单。
| 选项 | 类型 | 默认值 / 条件 | 作用阶段 |
|---|---|---|---|
FRONTEND_PORT | 字符串,按十进制解析为整数 | 18966 | Vite 开发端口、Express 前端监听端口 |
BACKEND_PORT | 字符串,按十进制解析为整数 | 11966 | Vite 和 Express 的 API 代理目标端口;后端监听实现未在本页核验 |
VITE_SITE_URL | 字符串 | 空字符串 | HTML 转换时使用;为空则删除站点 URL 条件块 |
SENTRY_AUTH_TOKEN | 字符串 | 未设置时不启用上传 | 构建期 source map 上传凭据 |
SENTRY_ENVIRONMENT | 字符串 | 未设置或空时按 production 判断 | 必须恰为 production 才满足上传环境条件 |
SENTRY_ORG | 字符串 | 未提供代码级默认值 | Sentry 插件组织参数 |
SENTRY_PROJECT_FRONTEND | 字符串 | 未提供代码级默认值 | Sentry 插件前端项目参数 |
依据:vite.config.js、vite.config.js、vite.config.js、frontend-server.js。
Sentry 构建开关的边界
上传启用条件同时要求 token 非空以及环境判断为 production:
const sentryUploadEnabled = !!process.env.SENTRY_AUTH_TOKEN
&& (process.env.SENTRY_ENVIRONMENT || 'production') === 'production';Source: vite.config.js
启用时生成 hidden source map,插件配置为上传后删除 dist/**/*.map,并忽略没有可映射源码的 Rolldown runtime chunk;未启用时完全不生成 source map。这段判断没有检查 NODE_ENV 或 Vite mode:本地构建如果带有 token 且未设置 SENTRY_ENVIRONMENT,同样满足上传条件。上传与删除在失败场景中的最终效果取决于插件执行结果,应检查日志和实际产物,而不是只看配置意图。
排错、边界与并发运行
| 现象 / 风险 | 源码支持的检查方向 |
|---|---|
页面可打开但 /api 不可用 | 两端是独立并行进程;检查后端输出与代理目标端口,不要只看前端就绪日志 |
修改源码后 start 页面仍旧 | start 不构建,静态服务器读取 dist;重新构建并检查浏览器及共享缓存 |
同时运行 dev 和 start | 二者默认请求同一个前端端口,并各自启动后端;避免重复启动整套进程 |
| 自定义开发域名被拒绝 | 检查 Vite allowedHosts;不要误认为生产 Express 自动继承此配置 |
| 深链接刷新失败 | 检查 GET、HTML Accept、路径末段点号和 dist/index.html 是否存在 |
| 丢失的 JS 文件没有返回首页 | 这是点号检查的预期行为,用来避免将 HTML 作为脚本返回 |
| 构建后出现额外缓存清理输出或非零退出 | 检查 postbuild 子进程;该阶段与 Vite 编译不是同一操作 |
| 非法端口配置 | 入口直接使用 parseInt,没有显式范围校验或友好错误包装,应自行保证整数有效 |
| 本地构建尝试上传 source map | 检查 token 和 SENTRY_ENVIRONMENT;默认环境判断为 production |
前端入口未定义自有代理重试策略、就绪探针、优雅退出处理或统一错误中间件,本页不推断依赖库的默认超时与异常响应。进程启动脚本也没有声明 cluster 或 worker 数量;这里的“并发”指前后端两个命令并行运行,不代表多实例部署支持。依据:package.json、frontend-server.js。
扩展与验证建议
- 更改代理路径时同步核对两种模式。 开发代理在 Vite 中,构建后代理在 Express 中,修改其中一处不会自动改变另一处。
- 新增构建插件时保留顺序约束。 locale JSON 清理使用
enforce: 'pre';站点 URL 转换在 HTML 的pre阶段;语言包注入使用post阶段;Sentry 插件按注释放在末尾。 - 更改资源命名时检查缓存策略。 分包和文件名由 Vite 决定,而长缓存由前端服务器按路径分类设置;引入稳定文件名后不能仍假设所有资源都通过 hash 自动失效。
- 修改后执行
pnpm run check,再按目标模式验证。check只证明命令链的检查结果,不能替代 API 联调、深链接访问或缓存行为验证。本文未实际运行这些命令,不报告测试通过或构建成功。
以上扩展入口分别见 vite.config.js 与 frontend-server.js。
相关链接
- 关于可执行任务和工具链版本,参见 package.json 的 scripts 与 engines。
- 关于构建插件与产物规则,参见 vite.config.js 的插件和 build 配置。
- 关于页面深链接与缓存,参见 frontend-server.js 的静态服务和 SPA 回退。
容器部署、外部服务接入、监控运行期配置和业务 API 行为应查阅对应专题;当前上下文未提供这些 Wiki 页的实际路径,因此不构造未经确认的页面链接。