1. 为什么 dist 包在 VS Code 里直接打开会白屏
前端项目打包之后,源码会被压缩、拆分、按需加载,最终产物统一丢进dist目录。很多人拿到dist的第一反应是双击index.html,结果页面一片空白,控制台报一堆Failed to load resource或者net::ERR_FILE_NOT_FOUND。这不是代码写错了,而是浏览器用file://协议加载本地文件时,对模块化脚本、绝对路径资源、fetch 请求都有严格限制。
具体来说,file://协议下会出现三类典型问题。第一类是 ES Module 的 CORS 限制,浏览器不允许从file://加载type="module"的脚本,直接报跨域错误。第二类是绝对路径失效,打包工具通常把资源路径写成/assets/xxx.js,在file://下这个/指向的是磁盘根目录,而不是你的项目目录。第三类是接口请求,前端代码里写的/stage-api/user/list在file://下没有 host,请求根本发不出去。
所以正确的做法是起一个本地 HTTP 服务,把dist目录当作网站根目录托管起来。VS Code 里的 live-server 插件就是干这个的,它能在你保存文件时自动刷新浏览器,还能配置代理解决接口跨域。这套链路跑通之后,你调试的就是真实的打包产物,而不是开发环境的源码,能提前发现很多只在生产构建里才暴露的问题。
这篇文章面向的是需要本地联调 dist 包的前端同学,尤其是那些后端接口在另一台机器、需要跨域代理的场景。我会从 live-server 的安装讲起,给出settings.json的完整可复制配置,再一步步验证代理是否生效,最后把常见的报错对照着排查一遍。你跟着做,基本能一次性把 dist 调试链路跑通。
需要说明的是,live-server 解决的是静态资源托管和请求转发,它本身不产生接口数据。如果你的接口需要鉴权、需要模型能力,那还得有一个能签发 Key 的服务端。后面我会讲到用 TaoToken 这类平台来补上接口这一环,让本地调试的请求真正有响应。
2. live-server 插件安装与 dist 目录的正确打开方式
2.1 安装 live-server 插件
打开 VS Code,左侧活动栏点扩展图标,搜索框输入live-server。排在第一位的通常是 Ritwick Dey 发布的那个,图标是一个带闪电的圆圈,安装量最高。点 Install,几秒钟就装好了。装完之后你不需要重启 VS Code,插件会自动激活。
这里有个细节要注意:网上有些教程让你装Live Server和Live Preview两个插件,其实没必要。Live Preview是微软官方出的,功能更偏向于内置预览窗口,配置项和 live-server 不兼容。我们这篇只用一个,避免settings.json里的配置互相打架。
2.2 用 VS Code 单独打开 dist 目录
这一步是很多人踩坑的地方。不要在你的源码工程根目录直接点 Go Live,因为那样托管的根目录是工程根,dist只是它的子目录,访问路径会变成http://localhost:5555/dist/index.html,资源路径全乱。
正确做法是:VS Code 菜单栏File→Open Folder,选中你的dist目录本身。打开之后,左侧资源管理器里应该直接看到index.html、assets文件夹、favicon.ico这些。确认根目录下就是index.html,而不是还要再点一层。
如果你用的是 monorepo,dist可能在packages/web/dist,那就打开到dist这一层为止。判断标准很简单:index.html必须是你打开目录的直接子文件。
2.3 点击 Go Live 启动服务
打开dist目录后,看 VS Code 右下角状态栏,会有一个Go Live的按钮。点它,live-server 会立刻启动一个 HTTP 服务,默认端口 5500(注意,不是 excerpt 里说的 5555,5555 是需要在配置里手动改的)。浏览器会自动打开http://127.0.0.1:5500,你的 dist 页面就出来了。
如果右下角没看到Go Live,检查两件事:一是当前打开的是文件夹而不是单个文件,二是 live-server 插件确实装好了。有时候状态栏图标被折叠了,点一下状态栏最左边的...展开就能看到。
启动之后,控制台如果还有资源 404,先别急着改代理,先确认index.html里的资源路径是不是以/开头。如果是相对路径./assets/xxx.js,那在根目录托管下也能正常加载。这一步过了,说明静态资源托管没问题,接下来才轮到跨域。
3. settings.json 完整配置:端口、代理与跨域一次配好
3.1 打开 settings.json 的正确位置
VS Code 的配置分两层:用户级(全局)和工作区级(当前项目)。live-server 的代理配置建议放在工作区级,因为不同项目的接口地址不一样。操作方式:在dist目录下新建.vscode文件夹,里面建一个settings.json。或者用快捷键Ctrl+Shift+P(Mac 是Cmd+Shift+P)打开命令面板,输入Preferences: Open Workspace Settings (JSON),VS Code 会自动帮你创建这个文件。
放工作区级的好处是,这份配置可以跟着项目走,团队里其他人拉下来就能用,不用每个人手动配一遍。
3.2 可复制的完整配置片段
下面这份配置可以直接粘进.vscode/settings.json,路径和字段名都按 live-server 插件的实际读取规则来写:
{ "liveServer.settings.host": "localhost", "liveServer.settings.port": 5555, "liveServer.settings.wait": 1000, "liveServer.settings.CustomBrowser": "chrome", "liveServer.settings.ChromeDebuggingAttachment": false, "liveServer.settings.https": false, "liveServer.settings.proxy": { "enable": true, "baseUri": "/stage-api", "proxyUri": "http://192.168.17.11/stage-api" }, "liveServer.settings.ignoreFiles": [ ".vscode/**", "**/*.scss", "**/*.sass", "**/*.ts" ] }逐项说明一下。host设成localhost,如果你想让同局域网的其他设备访问,可以改成0.0.0.0。port设成 5555,和 excerpt 里保持一致,避免和常见的 3000、8080 冲突。wait是文件变更后的刷新延迟,单位毫秒,1000 表示等 1 秒再刷新,防止连续保存时反复刷新。CustomBrowser指定用 Chrome 打开,方便调试。
重点是proxy这一段。enable必须为true,否则后面两项不生效。baseUri是前端代码里请求的路径前缀,比如你代码里写fetch('/stage-api/user/list'),那这里就填/stage-api。proxyUri是真实后端地址,live-server 会把/stage-api开头的请求转发到这个地址。
3.3 代理路径的匹配规则
这里有个容易搞混的点:baseUri和proxyUri的路径拼接关系。假设baseUri是/stage-api,proxyUri是http://192.168.17.11/stage-api,那么前端请求/stage-api/user/list时,实际转发到的是http://192.168.17.11/stage-api/user/list。也就是说,baseUri会被替换成proxyUri,后面的路径原样保留。
如果你的后端接口没有/stage-api这个前缀,比如真实地址是http://192.168.17.11/user/list,那proxyUri就写http://192.168.17.11,前端请求/stage-api/user/list会被转发成http://192.168.17.11/user/list。这个替换逻辑一定要对着你的接口文档确认清楚,配错了就是 404。
3.4 如果接口需要鉴权 Key
有些接口不是裸奔的,需要带 Token 或者 API Key。live-server 的代理本身不支持注入请求头,这时候有两种做法。一种是在前端代码里统一加 header,另一种是让后端网关做鉴权。如果你本地调试的是模型类接口,需要 Key 才能返回数据,可以到 TaoToken 的 API Keys 页面(https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite)申请一个,然后在请求里带上。注意 Key 不要硬编码进前端源码,调试阶段可以用环境变量或者浏览器插件临时注入。
配置改完之后,必须重启 live-server 才生效。点一下状态栏的Port: 5555让它停掉,再点Go Live重新启动。改配置不重启,代理是不生效的,这是最常见的「我明明配了怎么还跨域」的原因。
4. 验证代理是否生效:从请求到响应的完整链路
4.1 用浏览器 Network 面板确认转发
服务重启后,打开http://localhost:5555,按 F12 打开开发者工具,切到 Network 面板。在页面上触发一次接口请求,找到那条请求,看它的 Request URL。如果显示的是http://localhost:5555/stage-api/user/list,说明请求发给了本地服务,这是对的。再看 Status Code,如果是 200,说明代理转发成功,后端返回了数据。
如果 Status Code 是 404,点开这条请求看 Response,通常是后端没有对应的路径。这时候回去检查proxyUri的拼接是否正确。如果 Status 是 502 或者ERR_CONNECTION_REFUSED,说明 live-server 连不上proxyUri指向的地址,检查后端服务是否启动、IP 和端口是否可达。
4.2 用 curl 直接验证代理端点
除了看浏览器,还可以用命令行直接验证。打开终端,执行:
curl -i http://localhost:5555/stage-api/user/list如果返回HTTP/1.1 200 OK和 JSON 数据,说明代理链路完全通了。如果返回HTTP/1.1 404 Not Found,把同样的路径直接打到后端地址试试:
curl -i http://192.168.17.11/stage-api/user/list如果后端直接访问也是 404,那就是接口路径本身的问题,跟代理无关。如果后端直接访问是 200,但通过 live-server 是 404,那就是baseUri和proxyUri的拼接规则没对上。
4.3 验证模型接口的返回
如果你调试的是模型对话类接口,代理通了之后应该能看到流式返回。用 curl 测试时加-N参数禁用缓冲:
curl -N -X POST http://localhost:5555/stage-api/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_KEY" \ -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"你好"}],"stream":true}'如果能看到一行行data: {...}往外吐,说明流式代理也正常。这里YOUR_KEY换成你在 TaoToken 控制台(https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite)拿到的 Key。注意model字段要填平台支持的模型 ID,填错了会返回model not found。
4.4 确认热更新是否工作
live-server 的核心卖点之一是保存即刷新。你可以在dist目录下随便改一个 CSS 文件里的颜色值,保存,浏览器应该会自动刷新并显示新颜色。如果没刷新,检查wait是不是设得太长,或者ignoreFiles里是不是把 CSS 排除掉了。默认配置下,dist里的静态文件变更都会触发刷新。
不过要注意,dist是打包产物,你改dist里的文件只是临时验证,真正的修改应该回到源码工程重新 build。live-server 在这里的作用是让你快速验证打包结果,而不是替代构建流程。
5. 常见报错对照排查:401、proxy failed、choices 读取失败
5.1 401 Unauthorized
这是鉴权失败,不是跨域问题。跨域问题的报错关键词是CORS、Access-Control-Allow-Origin,而 401 说明请求已经到达后端,只是凭证不对。排查顺序:先看请求头里有没有Authorization,再看 Key 是不是过期或者复制时多了空格。如果你用的是 TaoToken 的 Key,到 API Keys 页面确认一下这个 Key 的状态是否正常。
还有一种情况是代理把 header 吃掉了。live-server 的代理默认会转发大部分 header,但如果你在settings.json里配了额外的headers字段覆盖,可能会把Authorization冲掉。检查配置里有没有多余的 header 设置。
5.2 local proxy failed / ECONNREFUSED
这个报错说明 live-server 无法连接到proxyUri。可能原因有三个:后端服务没启动、IP 写错了、端口被防火墙挡了。先用ping确认 IP 可达,再用telnet 192.168.17.11 80确认端口通。如果后端在本机,proxyUri写http://127.0.0.1:8080比写局域网 IP 更稳。
还有一种隐蔽情况:proxyUri结尾多了斜杠。比如写成http://192.168.17.11/stage-api/,而baseUri是/stage-api,拼接后可能变成/stage-api//user/list,双斜杠有些后端会 404。统一去掉结尾斜杠。
5.3 reading 'choices' 报错
这个报错通常出现在模型接口调试中,完整信息类似Cannot read properties of undefined (reading 'choices')。意思是前端代码期望响应体里有choices字段,但实际返回的结构不是标准格式。原因可能是:接口返回了错误对象(比如{"error": {"message": "..."}}),但前端没做错误分支,直接去读choices就崩了。
排查方法:在 Network 面板里看这条请求的 Response 原文。如果是错误对象,先解决错误;如果返回的是流式数据,前端解析方式要和流式格式匹配,不能按普通 JSON 解析。用 TaoToken 的模型对话页面(https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat&utm_campaign=rewrite)可以先手动验证一下模型和 Key 是否可用,排除掉服务端问题。
5.4 OAuth / 登录态相关报错
如果你的 dist 包依赖登录态,本地调试时可能会遇到 OAuth 回调地址不匹配的问题。因为 OAuth 提供商通常校验redirect_uri,而localhost:5555可能不在白名单里。解决办法是在 OAuth 应用配置里把http://localhost:5555/callback加进允许列表。如果改不了,就用测试环境的账号体系,或者让后端提供一个免登录的调试 Token。
5.5 配置不生效的通用排查
如果改完settings.json什么都没变化,按这个顺序查:第一,确认文件在.vscode/settings.json而不是用户级 settings;第二,确认 JSON 语法正确,多余的逗号会导致整个文件被忽略;第三,确认 live-server 已重启;第四,看 VS Code 的 Output 面板,选择 live-server 通道,里面会打印实际的代理配置和转发日志,这是最直接的证据。
6. 把本地调试链路接到真实接口上
dist 包跑起来、代理配好、请求能通,这条链路本身已经完整了。但很多时候你调试的接口需要真实数据,尤其是模型类、AI 类接口,本地 mock 很难模拟出真实返回。这时候需要一个稳定的接口来源。
TaoToken 在这里的角色是提供兼容 OpenAI 格式的接口端点。你不需要改前端代码的请求结构,只需要把proxyUri指向它的 API 地址,或者在请求头里带上它签发的 Key。接入文档(https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite)里有完整的端点说明和参数示例,对着改proxyUri就行。
如果你调试的是 Claude Code 这类编码 Agent 的本地链路,配置方式略有不同,需要设置 Base URL、Key 和 Model ID 三件套。Base URL 填https://taotoken.net/api,Key 用控制台签发的,Model ID 按文档里支持的填。这三项在 Claude Code 的配置文件里对应不同的字段名,具体可以看 ClaudeCodeAnthropic 的接入说明(https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude_code&utm_campaign=rewrite)。
对于需要长期跑编码任务、Agent 调用的场景,反复手动配 Key 比较麻烦,可以用 Coding Plan(https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite)来管理额度,省去每次调试都要换 Key 的步骤。
回到 live-server 本身,最后提醒一个实操细节:dist目录每次重新 build 都会被覆盖,但.vscode/settings.json如果放在dist里面,build 时可能被清掉。稳妥的做法是把.vscode放在dist的上一级,然后每次打开dist时手动把配置复制过去,或者用符号链接。我自己的习惯是在源码工程里维护一份settings.json模板,build 脚本里加一行把它复制到dist/.vscode/,这样每次打包完配置都在,点 Go Live 就能直接跑。