把 Eazo 生成的代码接到自己的数据上,卡点通常不在代码量,而在于先分清三档改动:只改配置值、改请求层、改数据结构。三档混着改,很容易出现「改了半天还是空列表」或者「页面白屏但接口是通的」。建议先做一次位置登记,再按成本从低到高推进,最后用一个只读数据源把整条链路走通,再处理字段不一致。
接入自有数据前,先判断每处数据来源属于哪一档:值来自配置就直接改值,来自组件内联调用就补请求层,来自字段命名差异就加映射层。判断依据是可验证的——改值后重启能看到请求目标变化,改结构后构建与页面行为会变。未确认接口结构前,不要动渲染组件,优先用只读数据源验证请求到渲染是否连通。
把生成项目里的数据来源点全部列出来
先摸清有多少处需要接自有数据,别边改边找。在项目根目录做两轮检索,注意排除依赖和构建产物目录,否则结果会被噪声淹没。
# 第一轮:找请求调用
# 在项目根目录执行
grep -rn `--exclude-dir`=node_modules `--exclude-dir`=dist `--exclude-dir`=.git \
-E 'fetch\(|axios|XMLHttpRequest|useSWR|useQuery|/api/' .
# 第二轮:找静态数据与常量
grep -rn `--exclude-dir`=node_modules `--exclude-dir`=dist `--exclude-dir`=.git \
-E 'const [A-Za-z_]*(Data|List|Items)[[:space:]]*=' .
grep -rln `--exclude-dir`=node_modules `--exclude-dir`=dist '\.json' .
需要登记的位置类型,通常集中在以下几类:
- 环境变量与配置文件:如 .env、config.js,决定接口地址和开关。
- 请求封装层:项目里已有的 fetch 或 axios 实例,所有请求一般会经过这里。
- 组件内的内联调用:直接在组件里 fetch 或写死数组,是最容易漏掉的一类。
- mock 或静态数据文件:本地模拟数据,接真实数据后需要停用或替换。
- 类型定义与字段常量:字段名、枚举、联合类型,改结构时会牵连到这里。
登记完再排序处理。建议按改动成本分档:先处理配置文件里的值,再补请求层,最后动类型和渲染。每改完一档就做一次页面验证,避免多轮改动叠加后无法定位问题。
区分配置项与硬编码逻辑
配置项的判断标准比较明确:值来自环境变量或配置文件、运行时或构建时可覆盖、有默认值兜底、同一个值在多个文件被引用。满足其中多数,改它基本只是改值。
硬编码的识别特征也相对好认:接口地址或字段名直接写在 JSX 与组件里;数组字面量直接当数据源用;业务分支写在 if 条件里且和具体值绑定;渲染时直接取深层字段,例如从响应对象一路点到某个固定属性。这类位置改值没用,必须改结构。
两类改动的验证方式不同。配置项:改完 .env 或配置文件后重启开发服务,在网络面板看到请求目标随之变化,即算生效。硬编码:改完需要跑构建和类型检查,页面行为随之变化才算通过;如果构建报错消失但页面照旧,说明改的是另一处副本,回到第一步的登记清单核对。
为请求层补一个可替换的客户端封装
把地址与请求方式集中到一个文件,组件只调用方法,不再各自拼地址。下面是一个通用骨架,具体路径和导出名按项目约定调整即可。
// src/api/client.js
const BASE_URL = import.meta.env.VITE_API_BASE_URL || '/api';
async function request(path, { method = 'GET', body, headers, signal } = {}) {
const res = await fetch(`${BASE_URL}${path}`, {
method,
headers: { 'Content-Type': 'application/json', ...headers },
body: body ? JSON.stringify(body) : undefined,
signal,
});
if (!res.ok) {
const text = await res.text().catch(() => '');
throw new Error(`请求失败 ${res.status} ${path} ${text}`);
}
const type = res.headers.get('content-type') || '';
return type.includes('application/json') ? res.json() : res.text();
}
export const api = {
list: (params = {}) => request(`/items?${new URLSearchParams(params)}`),
detail: (id) => request(`/items/${id}`),
};
环境变量的读取方式取决于构建工具:Vite 项目用 import.meta.env 加 VITE_ 前缀,Next 或 CRA 项目常用 process.env 加 NEXT_PUBLIC_ 或 REACT_APP_ 前缀。只有带约定前缀的变量才会被注入到浏览器端代码里,这一点需要结合项目实际的构建配置确认。
调用处替换的验证方法:把组件里的直接请求换成 api.list 这类方法,然后用第一步的检索命令再跑一遍,确认没有残留的 fetch( 或 axios. 调用。剩下的命中如果只出现在 client.js 内部,就是正常的。
先接一个只读数据源验证链路
挑项目里最简单的一个列表页面作为替换点,只改它的数据来源,指向自有的只读接口,先确认从请求到渲染是通的。
控制台与页面两侧的验证信号:网络面板能看到请求发出、状态码为成功、响应体是预期结构;控制台没有未捕获异常和渲染报错;页面渲染出的条目数与响应里的条目数一致。三者都满足,链路即连通。
失败时按下面的顺序排查,不要跳步:
- 请求有没有发出。没发出说明调用处没替换干净,或被条件渲染挡住。
- 地址与请求方法是否正确,BASE_URL 是否指向预期环境。
- 是否被跨域或代理规则拦下,浏览器会给出明确提示。
- 看状态码,区分鉴权、路径错误和服务端异常。
- 比对响应结构与代码假设是否一致。
- 最后才回头看组件的渲染逻辑和字段取值。
处理自有数据与生成结构的字段不一致
自有数据的字段名和生成代码假设的结构往往对不上:命名风格不同、层级不同、可空字段没返回。不要在组件里到处写兜底判断,在请求层和组件之间加一层映射,组件只认一种结构。
// src/api/normalize.js
function normalizeItem(raw) {
return {
id: raw.id ?? raw.item_id ?? raw.uuid ?? '',
title: raw.title ?? raw.name ?? '未命名',
desc: raw.desc ?? raw.description ?? '',
tags: Array.isArray(raw.tags) ? raw.tags : [],
updatedAt: raw.updated_at ?? raw.updateTime ?? null,
};
}
export function normalizeList(rawList) {
return (Array.isArray(rawList) ? rawList : [])
.map(normalizeItem)
.filter((item) => item.id !== '');
}
缺失字段的兜底展示按用途区分:文本类给空字符串或占位文案;列表类给空数组,渲染时自然走空状态;时间类为空时显示占位文字,不要直接丢给日期格式化函数,否则容易出现无效日期。关键标识缺失的条目建议过滤掉并打一条控制台警告,比渲染出无法点击的空行更容易排查。
验证方式:在返回数据里故意删掉一个字段,页面应显示兜底内容而不是白屏;把某个字段改个名字,只改映射层一处就能恢复;再跑一次类型检查,确认没有组件绕过映射层直接取原始字段。