儿童睡前故事
项目概述
儿童睡前故事平台 是一个专为 0-12 岁儿童设计的睡前故事平台,提供丰富的故事内容、音频播放和智能筛选功能。项目采用现代化的 Web 技术栈,注重用户体验和性能优化。
核心特性
- 📚 分龄故事库 - 按年龄段(0-2 岁、3-5 岁、6-8 岁、9-12 岁)精心分类
- 🎵 音频播放 - 支持故事音频朗读,让孩子可以听故事入睡
- 🔍 智能筛选 - 多维度筛选(类型、年龄、地区、分类)
- 📱 响应式设计 - 完美适配移动端和桌面端
- ⚡ 性能优化 - SSR 支持,快速加载体验
技术架构
技术栈
前端框架
后端服务
- Nitro - Nuxt 内置的服务器引擎
- H3 (v2.0.1-rc.7) - 轻量级 HTTP 框架
- Node.js - 运行时环境
包管理器
- pnpm - 快速、节省磁盘空间的包管理工具
项目结构
story/
├── pages/ # 页面路由
│ ├── index.vue # 首页 - 故事列表
│ └── story/[id].vue # 故事详情页
├── components/ # Vue 组件
│ ├── StoryHeader.vue # 顶部导航和筛选器
│ ├── StoryContent.vue # 故事网格/列表展示
│ ├── StoryFooter.vue # 页脚组件
│ ├── StoryCard.vue # 故事卡片
│ ├── StoryDetailHeader.vue # 详情页头部
│ ├── SearchSidebar.vue # 搜索侧边栏
│ └── AudioPlayer.vue # 音频播放器
├── composables/ # Vue 组合式函数
│ ├── useStories.ts # 故事数据管理
│ └── useScrollPosition.ts # 滚动位置追踪
├── server/api/ # 后端 API 路由
│ ├── stories.ts # 获取故事列表
│ └── story/[id].ts # 获取故事详情
├── data/ # 故事内容数据
│ ├── 0-2岁/ # 0-2岁年龄组
│ ├── 3-5岁/ # 3-5岁年龄组
│ ├── 6-8岁/ # 6-8岁年龄组
│ ├── 9-12岁/ # 9-12岁年龄组
│ └── audio/ # MP3 音频文件
├── assets/css/ # 全局样式
├── public/ # 静态资源
├── nuxt.config.ts # Nuxt 配置
└── package.json # 项目依赖
核心功能实现
1. 数据结构设计
Story 接口定义
interface Story {
id: string; // 唯一标识: {ageGroup}-{folder}
title: string; // 故事标题
description: string; // 故事简介
image: string; // 封面图片路径
tags: string[]; // 标签数组
country: string; // 国家/地区
duration: string; // 阅读时长
age: string; // 适合年龄
type: string; // 故事类型
folder: string; // 文件夹名称
ageGroup: string; // 年龄组
hasAudio: boolean; // 是否有音频
audioUrl: string | null; // 音频 URL
}
StoryData 接口(JSON 文件格式)
interface StoryData {
title: string;
cover: {
src: string;
alt: string;
};
tags: Array<{
name: string;
link: string;
}>;
readingTime: string;
intro: string;
meta: {
description: string;
};
}
2. API 路由设计
GET /api/stories
获取所有故事列表,支持按年龄组筛选。
查询参数:
ageGroup(可选): 年龄组筛选,如 "0-2 岁"、"3-5 岁" 等
响应格式:
{
success: boolean
data: Story[]
count: number
error?: string
}
实现要点:
- 扫描
data/目录下的所有年龄组文件夹 - 读取每个故事文件夹中的 JSON 数据文件
- 自动检测音频文件是否存在
- 提取标签、类型、国家等元数据
- 构建完整的故事对象数组
核心代码片段:
// server/api/stories.ts
async function loadStoriesFromFolder(ageGroup: string, basePath: string) {
const stories: Story[] = [];
const ageGroupPath = join(basePath, ageGroup);
const folders = await readdir(ageGroupPath, { withFileTypes: true });
for (const folder of folders) {
if (!folder.isDirectory()) continue;
const storyFolder = folder.name;
const jsonFile = join(
ageGroupPath,
storyFolder,
`${storyFolder}-文章数据.json`
);
const fileContent = await readFile(jsonFile, "utf-8");
const data: StoryData = JSON.parse(fileContent);
// 检查音频文件
const audioPath = join(basePath, "audio", `${data.title}.mp3`);
const hasAudio = existsSync(audioPath);
stories.push({
id: `${ageGroup}-${storyFolder}`,
title: data.title,
// ... 其他字段
hasAudio,
audioUrl: hasAudio ? `/data/audio/${data.title}.mp3` : null,
});
}
return stories;
}
GET /api/story/[id]
获取单个故事的详细信息。
路径参数:
id: 故事 ID,格式为{ageGroup}-{storyFolder}或直接使用故事标题
响应格式:
{
success: boolean
data: {
story: StoryData & { hasAudio: boolean, audioUrl: string | null }
navigation: {
prev: Story | null
next: Story | null
}
related: Story[]
}
}
3. Composables 设计
useStories
故事数据管理的核心 Composable,提供数据加载和筛选功能。
主要功能:
export const useStories = () => {
const stories = ref<Story[]>([]);
const loading = ref(false);
// 加载所有故事
const loadStories = async () => {
const response = await $fetch("/api/stories");
if (response.success) {
stories.value = response.data;
}
};
// 筛选故事
const filterStories = (options: {
category?: string;
type?: string;
age?: string;
country?: string;
}) => {
let filtered = [...stories.value];
if (options.type) {
filtered = filtered.filter((story) => story.type === options.type);
}
if (options.age) {
filtered = filtered.filter((story) => story.age === options.age);
}
return filtered;
};
return { stories, loading, loadStories, filterStories };
};
使用场景:
- 首页故事列表加载
- 多维度筛选功能
- 搜索结果展示
useScrollPosition
滚动位置追踪 Composable,用于在页面切换时保持滚动位置。
主要功能:
- 保存当前页面的滚动位置
- 恢复之前保存的滚动位置
- 支持多页面独立管理
4. 音频播放器实现
AudioPlayer 组件是一个功能完整的音频播放器,支持播放控制、进度显示和快进快退。
核心特性:
- ▶️ 播放/暂停控制
- ⏪⏩ 10 秒快退/快进
- 📊 实时进度条显示
- 🎯 点击进度条跳转
- 📱 响应式设计
- 🎨 优雅的滑入动画
技术实现:
// 核心状态管理
const audioElement = ref<HTMLAudioElement | null>(null);
const isPlaying = ref(false);
const currentTime = ref(0);
const duration = ref(0);
const audioReady = ref(false);
// 播放控制
const togglePlay = () => {
if (!audioElement.value || !audioReady.value) return;
if (isPlaying.value) {
audioElement.value.pause();
} else {
audioElement.value.play();
}
isPlaying.value = !isPlaying.value;
};
// 快退/快进 10 秒
const rewind = () => {
if (!audioElement.value) return;
audioElement.value.currentTime = Math.max(
0,
audioElement.value.currentTime - 10
);
};
const forward = () => {
if (!audioElement.value) return;
audioElement.value.currentTime = Math.min(
duration.value,
audioElement.value.currentTime + 10
);
};
UI 设计亮点:
- 使用 Teleport 将播放器渲染到 body 底部
- 半透明遮罩层,点击关闭
- 滑入动画效果(slide-up transition)
- 圆形播放按钮,视觉焦点突出
页面路由设计
首页 (pages/index.vue)
功能:
- 展示所有故事的网格/列表视图
- 提供多维度筛选功能
- 支持搜索和分类导航
核心组件:
StoryHeader- 顶部导航和筛选器SearchSidebar- 侧边栏搜索菜单StoryContent- 故事网格展示StoryFooter- 页脚信息
筛选维度:
- 分类:全部、最新、热门、推荐
- 类型:童话故事、寓言故事、成长教育、冒险故事
- 年龄:0-2 岁、3-5 岁、6-8 岁、9-12 岁
- 地区:中国、丹麦、德国、希腊等
故事详情页 (pages/story/[id].vue)
功能:
- 展示故事的完整内容
- 显示封面图和标题
- 提供音频播放功能
- 上一篇/下一篇导航
- 相关故事推荐
核心组件:
StoryDetailHeader- 详情页头部AudioPlayer- 音频播放器- 相关故事卡片列表
路由参数:
id: 动态路由参数,格式为{ageGroup}-{storyFolder}
数据存储结构
文件系统组织
data/
├── 0-2岁/
│ ├── 故事名称1/
│ │ ├── 故事名称1-文章数据.json
│ │ └── img/
│ │ └── cover.webp
│ └── 故事名称2/
│ ├── 故事名称2-文章数据.json
│ └── img/
│ └── cover.webp
├── 3-5岁/
├── 6-8岁/
├── 9-12岁/
└── audio/
├── 故事标题1.mp3
└── 故事标题2.mp3
JSON 数据文件格式
每个故事文件夹包含一个 {故事名称}-文章数据.json 文件:
{
"title": "冰雪女王",
"cover": {
"src": "./img/cover.webp",
"alt": "冰雪女王封面"
},
"tags": [
{ "name": "丹麦", "link": "#" },
{ "name": "童话故事", "link": "#" },
{ "name": "适合6-8岁", "link": "#" }
],
"readingTime": "15 m",
"intro": "一个关于友谊和勇气的经典童话故事...",
"meta": {
"description": "故事简介..."
}
}
性能优化策略
1. SSR 与静态生成
Nuxt 3 提供了多种渲染模式:
- SSR (Server-Side Rendering) - 服务端渲染,首屏加载快
- SSG (Static Site Generation) - 静态生成,适合内容不频繁变化的页面
- CSR (Client-Side Rendering) - 客户端渲染,适合交互密集的页面
当前配置:
// nuxt.config.ts
export default defineNuxtConfig({
devtools: { enabled: true },
css: ["~/assets/css/main.css"],
});
2. 图片优化
- 使用 WebP 格式,减小图片体积
- 封面图片统一尺寸,提升加载速度
- 懒加载策略,按需加载图片
3. 数据加载优化
- 客户端缓存故事列表数据
- 避免重复加载相同数据
- 使用
loading状态管理加载过程
// composables/useStories.ts
const loadStories = async () => {
if (loading.value) return; // 防止重复加载
if (process.server) return; // 只在客户端执行
loading.value = true;
try {
const response = await $fetch("/api/stories");
if (response.success) {
stories.value = response.data;
}
} finally {
loading.value = false;
}
};
4. 音频预加载
- 使用
preload="metadata"预加载音频元数据 - 延迟加载完整音频文件
- 播放时才加载完整音频内容
开发与部署
本地开发
# 安装依赖
pnpm install
# 启动开发服务器
pnpm dev
# 访问 http://localhost:3000
生产构建
# 构建生产版本
pnpm build
# 预览生产构建
pnpm preview
静态生成
# 生成静态站点
pnpm generate
# 输出到 .output/public 目录
技术亮点
1. 类型安全
全面使用 TypeScript,提供完整的类型定义:
- 接口定义清晰
- 编译时类型检查
- IDE 智能提示
2. 组件化设计
- 高度模块化的组件结构
- 单一职责原则
- 易于维护和扩展
3. 文件系统路由
利用 Nuxt 3 的文件系统路由:
- 约定优于配置
- 自动生成路由
- 支持动态路由参数
4. Composables 模式
使用 Vue 3 Composition API:
- 逻辑复用性强
- 代码组织清晰
- 响应式状态管理
5. 音频播放体验
- 完整的播放控制功能
- 优雅的 UI 设计
- 流畅的动画效果
- 移动端适配
未来优化方向
功能扩展
-
用户系统
- 用户注册和登录
- 收藏和历史记录
- 个性化推荐
-
社交功能
- 故事评论和评分
- 分享到社交媒体
- 家长社区
-
内容增强
- 更多年龄段故事
- 多语言支持
- 互动式故事
技术优化
-
性能提升
- 图片 CDN 加速
- 服务端缓存
- 代码分割优化
-
SEO 优化
- Meta 标签优化
- 结构化数据
- Sitemap 生成
-
监控与分析
- 用户行为分析
- 性能监控
- 错误追踪
总结
儿童睡前故事平台 是一个技术栈现代、架构清晰的儿童故事平台。项目充分利用了 Nuxt 3 和 Vue 3 的优势,实现了以下目标:
核心优势
✅ 用户体验优先 - 响应式设计、流畅动画、音频播放 ✅ 性能优化 - SSR 支持、图片优化、数据缓存 ✅ 可维护性 - TypeScript、组件化、清晰的代码结构 ✅ 可扩展性 - 模块化设计、易于添加新功能 ✅ 内容丰富 - 多年龄段故事、多种类型、音频支持
技术栈总结
| 技术 | 版本 | 用途 |
|---|---|---|
| Nuxt 3 | ^3.13.0 | 全栈框架 |
| Vue 3 | - | UI 框架 |
| TypeScript | - | 类型系统 |
| H3 | 2.0.1-rc.7 | HTTP 服务器 |
| pnpm | - | 包管理器 |
项目数据
- 故事总数: 500+ 个精选故事
- 年龄覆盖: 0-12 岁,4 个年龄段
- 故事类型: 童话、寓言、教育、冒险等
- 音频支持: 200+ 个故事配有音频朗读
- 国家地区: 涵盖中国、丹麦、德国、希腊等多国故事
适用场景
儿童睡前故事平台 适合以下场景:
- 家长为孩子选择睡前故事
- 幼儿园和早教机构的教学辅助
- 儿童独立阅读和听故事
- 亲子互动和教育
参考资源
当前版本: 1.0 最后更新: 2026-01-07 作者: ZM