
本文详解如何解决从 Rick and Morty API 获取角色数据后,item.episode.name 返回 undefined 的常见问题——根本原因在于 episode 字段仅包含 URL 字符串数组,需额外发起请求获取真实剧集对象。
本文详解如何解决从 rick and morty api 获取角色数据后,`item.episode.name` 返回 `undefined` 的常见问题——根本原因在于 `episode` 字段仅包含 url 字符串数组,需额外发起请求获取真实剧集对象。
在使用 Rick and Morty API 时,一个关键设计特点是:角色(Character)资源中的 episode 字段并非嵌套对象,而是一个字符串数组,每个元素是对应剧集的完整 API URL(例如 “https://rickandmortyapi.com/api/episode/1″)。因此,直接访问 character.episode.name 必然失败——因为 character.episode 是数组,不是对象,更不存在 .name 属性。
✅ 正确做法:链式请求获取剧集详情
你需要对每个角色的首个剧集 URL(character.episode[0])发起二次 fetch 请求,解析返回的 json 后,才能安全访问 episode.name。以下是优化后的实现方案:
方案一:promise 链式调用(兼容性好)
const main = document.querySelector("main"); function fetchAllCharacters() { return fetch("https://rickandmortyapi.com/api/character") .then(res => { if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`); return res.json(); }); } fetchAllCharacters() .then(data => { data.results.forEach(character => { // 对每个角色的第一个剧集 URL 发起请求 fetch(character.episode[0]) .then(res => { if (!res.ok) throw new Error(`Episode fetch failed: ${res.status}`); return res.json(); }) .then(episode => { // 渲染卡片,此时 episode.name 可用 const html = ` <article class="character-card"> <div class="image-container"> <img src="${character.image}" alt="${character.name}"> </div> <div class="character-info"> <div class="section"> <h2>${character.name}</h2> <span class="status">${character.status} - ${character.species}</span> </div> <div class="section"> <span class="greytext">Last known location:</span> <span>${character.location.name}</span> </div> <div class="section"> <span class="greytext">First seen in:</span> <span>${episode.name}</span> </div> </div> </article> `; main.insertAdjacentHTML("beforeend", html); }) .catch(err => { console.warn(`Failed to load episode for ${character.name}:`, err.message); // 降级显示占位文本,避免整个卡片崩溃 const html = ` <article class="character-card"> <div class="image-container"> <img src="${character.image}" alt="${character.name}"> </div> <div class="character-info"> <div class="section"> <h2>${character.name}</h2> <span class="status">${character.status} - ${character.species}</span> </div> <div class="section"> <span class="greytext">Last known location:</span> <span>${character.location.name}</span> </div> <div class="section"> <span class="greytext">First seen in:</span> <span>— Episode data unavailable —</span> </div> </div> </article> `; main.insertAdjacentHTML("beforeend", html); }); }); }) .catch(err => console.error("Failed to fetch characters:", err));
方案二:现代 async/await 写法(推荐,可读性强)
const mainEl = document.querySelector("main"); // 通用异步 fetch 封装(带错误处理) async function fetchJson(url) { const res = await fetch(url); if (!res.ok) throw new Error(`${res.status} ${res.statusText} on ${url}`); return res.json(); } // 获取所有角色 async function fetchCharacters() { const data = await fetchJson("https://rickandmortyapi.com/api/character"); return data.results; } // 渲染单个角色卡片的 HTML 模板 function renderCharacter(character, episode) { return ` <article class="character-card"> <div class="image-container"> <img src="${character.image}" alt="${character.name}"> </div> <div class="character-info"> <div class="section"> <h2>${character.name}</h2> <span class="status">${character.status} - ${character.species}</span> </div> <div class="section"> <span class="greytext">Last known location:</span> <span>${character.location.name}</span> </div> <div class="section"> <span class="greytext">First seen in:</span> <span>${episode?.name || "— Unknown —"}</span> </div> </div> </article> `; } // 主逻辑 async function renderAllCharacters() { try { const characters = await fetchCharacters(); for (const character of characters) { try { const episode = await fetchJson(character.episode[0]); mainEl.insertAdjacentHTML("beforeend", renderCharacter(character, episode)); } catch (err) { console.warn(`Skipping episode for ${character.name}:`, err.message); mainEl.insertAdjacentHTML("beforeend", renderCharacter(character, {})); } } } catch (err) { console.error("Critical error loading data:", err); mainEl.innerHTML = "<p class='error'>Failed to load character data. Please check your network.</p>"; } } // 启动应用 renderAllCharacters();
⚠️ 注意事项与最佳实践
- 永远不要假设 character.episode 非空:部分角色可能未出现在任何剧集中(episode 数组为空),使用前应校验 character.episode.length > 0;
- 避免阻塞渲染:多个 fetch 并发请求可能触发浏览器并发限制(通常为 6 个),建议用 Promise.all() 批量加载(适用于已知少量角色),或采用节流策略;
- 错误隔离:单个剧集请求失败不应导致整个页面崩溃,务必为每层 fetch 添加独立 try/catch 或 .catch();
- 性能提示:若需展示全部剧集名称,可先批量获取所有唯一 episode URL,再统一请求(减少重复),但本例中只需首播剧集,故单次请求即可;
- CSS 建议:原文 CSS 中 main { grid-template-columns: 1fr 1fr 1fr } 在小屏下易溢出,建议添加响应式断点:
@media (max-width: 768px) { main { grid-template-columns: 1fr; } }
通过以上方式,你就能准确、健壮地将角色与其首播剧集名称一同渲染,彻底告别 undefined 问题。核心原则始终如一:API 返回什么结构,就按什么结构处理;需要嵌套数据,就主动发起对应请求。