如何在SvelteKit中从父+page.svelte向子页面传递数据?
我用最新版SvelteKit开发,文件采用+page.svelte、+page.js、+page.server.js格式。在nowPlaying目录下的页面拉取正在上映的电影列表,点击电影海报时会把电影信息存入pkg对象,现在想把这些数据传递到跳转后的nowPlayingMovieDetails页面并展示。
文件结构:
+ src | +-- routes | +-- nowPlaying | +-- +page.server.js +-- +page.svelte | +-- nowPlayingMovieDetails | +-- +page.js +-- +page.svelte
当前nowPlaying/+page.svelte核心代码:
<script> let pkg = { movieID: "", movieName: "", movieOverview: "", movieReleaseDate: "", moviePosterPath: "", } function acquireMovieInfo(id, name, overview, release, poster) { pkg = { movieID: id, movieName: name, movieOverview: overview, movieReleaseDate: release, moviePosterPath: poster, } console.log(pkg); } </script> <div> <ul> {#each movies_nowPlaying as movieNowPlaying} <li> <a href="/nowPlayingMovieDetails" data-movie-id={movieNowPlaying.id} on:click={() => acquireMovieInfo(movieNowPlaying.id, movieNowPlaying.original_title, movieNowPlaying.overview, movieNowPlaying.release_date, movieNowPlaying.poster_path)}><img src="http://image.tmdb.org/t/p/w500/{movieNowPlaying.poster_path}" alt="{movieNowPlaying.title} movie poster"></a> </li> {/each} </ul> </div>
推荐两种方案,按需选择:
方案一:通过URL参数传递电影ID(优先选,支持页面刷新)
这是最符合SvelteKit路由规范的做法,即使用户刷新详情页也能正常获取数据,稳定性更高。
1. 修改跳转链接,带上电影ID
把nowPlaying/+page.svelte里的<a>标签路径改成带ID的格式(注意路径要对应子目录结构,之前的/nowPlayingMovieDetails是错误的),同时可以去掉acquireMovieInfo的调用(不需要再存pkg):
<!-- nowPlaying/+page.svelte --> <a href="/nowPlaying/nowPlayingMovieDetails/{movieNowPlaying.id}"> <img src="http://image.tmdb.org/t/p/w500/{movieNowPlaying.poster_path}" alt="{movieNowPlaying.title} movie poster"> </a>
2. 把详情页改成动态路由
将nowPlaying/nowPlayingMovieDetails目录重命名为[movieId],这样SvelteKit就能捕获URL里的ID参数:
+ src | +-- routes | +-- nowPlaying | +-- +page.server.js +-- +page.svelte | +-- [movieId] <!-- 原nowPlayingMovieDetails改名为这个 --> | +-- +page.server.js <!-- 新增文件,用来请求详情 --> +-- +page.svelte
3. 在详情页的+page.server.js中请求电影数据
通过params获取URL里的电影ID,调用TMDB接口拉取详情:
// nowPlaying/[movieId]/+page.server.js export async function load({ params }) { const movieId = params.movieId; // 替换成你的TMDB API密钥 const res = await fetch(`https://api.themoviedb.org/3/movie/${movieId}?api_key=你的API密钥&language=zh-CN`); const movieDetails = await res.json(); return { movieDetails }; }
4. 在详情页展示数据
直接使用data对象里的电影详情:
<!-- nowPlaying/[movieId]/+page.svelte --> <script> export let data; const { movieDetails } = data; </script> <div class="movie-details"> <h1>{movieDetails.title}</h1> <img src={`http://image.tmdb.org/t/p/w500/${movieDetails.poster_path}`} alt={movieDetails.title}> <p>{movieDetails.overview}</p> <p>上映日期:{movieDetails.release_date}</p> </div>
方案二:通过page.state传递完整数据(无需重新请求,但刷新会丢失)
如果不想重复调用TMDB接口,可以用SvelteKit的page.state直接传递已有的pkg数据,但这种方式下刷新详情页会丢失数据,适合对刷新无要求的场景。
1. 用goto方法跳转并传递数据
在nowPlaying/+page.svelte中导入goto,修改点击逻辑,阻止默认跳转并通过state传递数据:
<!-- nowPlaying/+page.svelte --> <script> import { goto } from '$app/navigation'; let pkg = { /* 原定义不变 */ }; function acquireMovieInfo(id, name, overview, release, poster) { pkg = { movieID: id, movieName: name, movieOverview: overview, movieReleaseDate: release, moviePosterPath: poster, }; // 带state跳转 goto('/nowPlaying/nowPlayingMovieDetails', { state: { movieData: pkg } }); } </script> <!-- 修改a标签,去掉href,阻止默认跳转行为 --> <a href="" on:click|preventDefault={() => acquireMovieInfo(movieNowPlaying.id, movieNowPlaying.original_title, movieNowPlaying.overview, movieNowPlaying.release_date, movieNowPlaying.poster_path)}> <img src="http://image.tmdb.org/t/p/w500/{movieNowPlaying.poster_path}" alt="{movieNowPlaying.title} movie poster"> </a>
2. 在详情页的+page.js中获取state数据
从navigation对象里取出传递的电影数据:
// nowPlaying/nowPlayingMovieDetails/+page.js export async function load({ navigation }) { if (navigation?.state?.movieData) { return { movieData: navigation.state.movieData }; } // 刷新页面时的 fallback 处理,比如返回空或跳回列表页 return { movieData: null }; }
3. 在详情页展示数据
判断数据是否存在,然后渲染:
<!-- nowPlaying/nowPlayingMovieDetails/+page.svelte --> <script> export let data; const { movieData } = data; </script> {#if movieData} <div class="movie-details"> <h1>{movieData.movieName}</h1> <img src={`http://image.tmdb.org/t/p/w500/${movieData.moviePosterPath}`} alt={movieData.movieName}> <p>{movieData.movieOverview}</p> <p>上映日期:{movieData.movieReleaseDate}</p> </div> {:else} <p>无法获取电影信息,请返回列表页重新选择</p> {/if}
内容的提问来源于stack exchange,提问作者illusive_yeti

