nuxtjs实战教程-使用nuxtjs两天开发一个儿童睡前故事网站

34 阅读8分钟

儿童睡前故事

项目概述

儿童睡前故事平台 是一个专为 0-12 岁儿童设计的睡前故事平台,提供丰富的故事内容、音频播放和智能筛选功能。项目采用现代化的 Web 技术栈,注重用户体验和性能优化。

核心特性

  • 📚 分龄故事库 - 按年龄段(0-2 岁、3-5 岁、6-8 岁、9-12 岁)精心分类
  • 🎵 音频播放 - 支持故事音频朗读,让孩子可以听故事入睡
  • 🔍 智能筛选 - 多维度筛选(类型、年龄、地区、分类)
  • 📱 响应式设计 - 完美适配移动端和桌面端
  • 性能优化 - SSR 支持,快速加载体验

技术架构

技术栈

前端框架

  • Nuxt 3 (v3.13.0+) - Vue 3 元框架,提供 SSR 和静态生成能力
  • Vue 3 - 渐进式 JavaScript 框架
  • TypeScript - 类型安全的开发体验

后端服务

  • 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 设计
  • 流畅的动画效果
  • 移动端适配

未来优化方向

功能扩展

  1. 用户系统

    • 用户注册和登录
    • 收藏和历史记录
    • 个性化推荐
  2. 社交功能

    • 故事评论和评分
    • 分享到社交媒体
    • 家长社区
  3. 内容增强

    • 更多年龄段故事
    • 多语言支持
    • 互动式故事

技术优化

  1. 性能提升

    • 图片 CDN 加速
    • 服务端缓存
    • 代码分割优化
  2. SEO 优化

    • Meta 标签优化
    • 结构化数据
    • Sitemap 生成
  3. 监控与分析

    • 用户行为分析
    • 性能监控
    • 错误追踪

总结

儿童睡前故事平台 是一个技术栈现代、架构清晰的儿童故事平台。项目充分利用了 Nuxt 3 和 Vue 3 的优势,实现了以下目标:

核心优势

用户体验优先 - 响应式设计、流畅动画、音频播放 ✅ 性能优化 - SSR 支持、图片优化、数据缓存 ✅ 可维护性 - TypeScript、组件化、清晰的代码结构 ✅ 可扩展性 - 模块化设计、易于添加新功能 ✅ 内容丰富 - 多年龄段故事、多种类型、音频支持

技术栈总结

技术版本用途
Nuxt 3^3.13.0全栈框架
Vue 3-UI 框架
TypeScript-类型系统
H32.0.1-rc.7HTTP 服务器
pnpm-包管理器

项目数据

  • 故事总数: 500+ 个精选故事
  • 年龄覆盖: 0-12 岁,4 个年龄段
  • 故事类型: 童话、寓言、教育、冒险等
  • 音频支持: 200+ 个故事配有音频朗读
  • 国家地区: 涵盖中国、丹麦、德国、希腊等多国故事

适用场景

儿童睡前故事平台 适合以下场景:

  • 家长为孩子选择睡前故事
  • 幼儿园和早教机构的教学辅助
  • 儿童独立阅读和听故事
  • 亲子互动和教育

参考资源


当前版本: 1.0 最后更新: 2026-01-07 作者: ZM

儿童睡前故事链接