如何在Vue.js中调用TVmaze API按类型获取并展示剧集?
Solution: Fetch & Filter TVMaze Shows by Genre in Vue.js
Since the TVMaze API doesn’t have a direct genre filter endpoint, we’ll need to fetch paginated show data and filter it client-side to get 4 shows each for Action, Drama, and Thriller genres. Here’s a step-by-step implementation using Vue.js (I’ll use Axios for simplicity, but native fetch works too):
Step 1: Set Up Dependencies (Optional)
If you aren’t already using Axios, install it via npm or yarn:
npm install axios # or yarn add axios
Step 2: Complete Vue Component Implementation
This component handles fetching, filtering, loading states, errors, and displays the results neatly:
<template> <div class="show-library"> <!-- Loading State --> <div v-if="isLoading" class="status-message">Grabbing your shows...</div> <!-- Error State --> <div v-if="error" class="status-message error">Oops! {{ error }}</div> <!-- Filtered Show Sections --> <div v-else class="genre-sections"> <section class="genre-section"> <h2>Action Picks (4)</h2> <div class="show-grid"> <div v-for="show in actionShows" :key="show.id" class="show-card"> <img :src="show.image?.medium || 'https://via.placeholder.com/200x300'" :alt="show.name"> <h3>{{ show.name }}</h3> <p>Rating: {{ show.rating.average || 'N/A' }}</p> </div> </div> </section> <section class="genre-section"> <h2>Drama Picks (4)</h2> <div class="show-grid"> <div v-for="show in dramaShows" :key="show.id" class="show-card"> <img :src="show.image?.medium || 'https://via.placeholder.com/200x300'" :alt="show.name"> <h3>{{ show.name }}</h3> <p>Rating: {{ show.rating.average || 'N/A' }}</p> </div> </div> </section> <section class="genre-section"> <h2>Thriller Picks (4)</h2> <div class="show-grid"> <div v-for="show in thrillerShows" :key="show.id" class="show-card"> <img :src="show.image?.medium || 'https://via.placeholder.com/200x300'" :alt="show.name"> <h3>{{ show.name }}</h3> <p>Rating: {{ show.rating.average || 'N/A' }}</p> </div> </div> </section> </div> </div> </template> <script> import axios from 'axios'; export default { name: 'GenreShowFilter', data() { return { actionShows: [], dramaShows: [], thrillerShows: [], isLoading: true, error: null, currentPage: 1, targetPerGenre: 4 // We want exactly 4 shows per category }; }, mounted() { // Start fetching when the component loads this.fetchAndFilterShows(); }, methods: { async fetchAndFilterShows() { try { // Fetch the current page of shows (25 per page from TVMaze) const res = await axios.get(`http://api.tvmaze.com/shows?page=${this.currentPage}`); const shows = res.data; // Filter shows into their respective genre lists (only if we haven't hit the target yet) shows.forEach(show => { if (this.actionShows.length < this.targetPerGenre && show.genres.includes('Action')) { this.actionShows.push(show); } if (this.dramaShows.length < this.targetPerGenre && show.genres.includes('Drama')) { this.dramaShows.push(show); } if (this.thrillerShows.length < this.targetPerGenre && show.genres.includes('Thriller')) { this.thrillerShows.push(show); } }); // Check if we have enough shows in all categories const hasEnoughShows = this.actionShows.length >= this.targetPerGenre && this.dramaShows.length >= this.targetPerGenre && this.thrillerShows.length >= this.targetPerGenre; if (!hasEnoughShows) { // Increment page and keep fetching this.currentPage++; this.fetchAndFilterShows(); } else { // Trim any extra shows (in case we grabbed more than 4) this.actionShows = this.actionShows.slice(0, this.targetPerGenre); this.dramaShows = this.dramaShows.slice(0, this.targetPerGenre); this.thrillerShows = this.thrillerShows.slice(0, this.targetPerGenre); this.isLoading = false; } } catch (err) { this.error = 'Failed to load shows. Please try again later.'; this.isLoading = false; console.error('Fetch error:', err); } } } }; </script> <style scoped> .show-library { max-width: 1200px; margin: 2rem auto; padding: 0 1rem; } .status-message { text-align: center; padding: 3rem; font-size: 1.2rem; } .status-message.error { color: #dc3545; } .genre-sections { display: flex; flex-direction: column; gap: 2rem; } .genre-section h2 { border-bottom: 2px solid #eee; padding-bottom: 0.5rem; } .show-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 1.5rem; margin-top: 1rem; } .show-card { border: 1px solid #eee; border-radius: 8px; padding: 1rem; text-align: center; transition: transform 0.2s; } .show-card:hover { transform: translateY(-5px); } .show-card img { width: 100%; border-radius: 4px; margin-bottom: 0.5rem; } .show-card h3 { margin: 0 0 0.5rem; font-size: 1rem; } </style>
Key Features Explained:
- Smart Pagination: We fetch pages one by one until we hit 4 shows per genre—no need to load unnecessary data once our target is met.
- Genre Matching: Each show has a
genresarray; we check if it contains our target genre before adding it to the list. - User Feedback: Clear loading and error states keep users informed while data loads.
- Fallback Images: Uses a placeholder image if a show doesn’t have a medium-sized image available.
Quick Swap to Native Fetch:
If you don’t want to use Axios, replace the fetch call with:
const res = await fetch(`http://api.tvmaze.com/shows?page=${this.currentPage}`); const shows = await res.json();
内容的提问来源于stack exchange,提问作者machupichu
相关产品推荐
相关产品推荐

