首页
Preview

SpringBoot+Uniapp实战开发全新仿抖音短视频App | 更新完结

t018ffd551f87f58243 - 副本.jpg

一、 项目架构与技术选型

在开始之前,我们要明确这个项目的核心逻辑:移动端负责展示与交互,后端负责内容分发与业务逻辑

  • 后端:SpringBoot + MyBatis-Plus + Redis + MinIO(对象存储)
    • 理由:SpringBoot 生态成熟,处理高并发请求(如视频流、点赞)稳定;Redis 用于缓存热门视频和做分布式锁;MinIO 自建私有云存储,比 OSS 成本更低,适合副业项目起步。
  • 前端:Uniapp (Vue3) + TypeScript
    • 理由:一套代码,多端发布(iOS、Android、H5、小程序),性价比极高。

二、 后端核心实现

1. 数据库设计

我们需要核心的几张表:视频表、用户表、点赞表。

CREATE TABLE `tb_video` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) DEFAULT NULL COMMENT '发布者ID',
  `video_url` varchar(255) NOT NULL COMMENT '视频地址',
  `cover_url` varchar(255) DEFAULT NULL COMMENT '封面地址',
  `title` varchar(50) DEFAULT NULL COMMENT '视频标题',
  `like_count` int(11) DEFAULT '0' COMMENT '点赞数',
  `is_deleted` tinyint(1) DEFAULT '0',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2. 视频流接口(核心 Feed 流)

仿抖音的核心是“滑动刷新”,我们需要一个高效的分页查询接口。这里使用 MyBatis-Plus 简化开发。 Entity 实体类 (Video.java):

@Data
@TableName("tb_video")
public class Video {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long userId;
    private String videoUrl;
    private String coverUrl;
    private String title;
    private Integer likeCount;
    // getter/setter 省略,使用 Lombok @Data
}

Service 业务层 (VideoService.java): 这里演示基础的分页查询。在生产环境中,通常结合 Redis 做推荐算法(如基于权重的 Feed 流)。

@Service
public class VideoService {
    @Autowired
    private VideoMapper videoMapper;
    /**
     * 分页获取视频列表(仿抖音首页Feed流)
     */
    public IPage<Video> getVideoFeed(long current, long size) {
        Page<Video> page = new Page<>(current, size);
        // 按照创建时间倒序排列
        QueryWrapper<Video> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("create_time");
        return videoMapper.selectPage(page, queryWrapper);
    }
}

Controller 控制层 (VideoController.java):

@RestController
@RequestMapping("/api/video")
public class VideoController {
    @Autowired
    private VideoService videoService;
    @GetMapping("/feed")
    public Result getFeed(@RequestParam(defaultValue = "1") long current,
                          @RequestParam(defaultValue = "5") long size) {
        IPage<Video> videoPage = videoService.getVideoFeed(current, size);
        return Result.success(videoPage);
    }
}

3. 文件上传接口 (MinIO)

视频上传是短视频应用最消耗资源的部分。

@PostMapping("/upload")
public Result uploadVideo(MultipartFile file) {
    try {
        // 1. 生成唯一文件名
        String fileName = UUID.randomUUID().toString() + ".mp4";
        // 2. 调用 MinIO 工具类上传(需自行配置 MinIOClient)
        String url = MinIOUtil.upload(file.getInputStream(), fileName);
        // 3. 返回访问地址
        return Result.success(url);
    } catch (Exception e) {
        e.printStackTrace();
        return Result.error("上传失败");
    }
}

三、 前端核心实现

1. 视频滑页组件

这是仿抖音的灵魂,使用 swiper 组件实现垂直滑动。

<template>
  <view class="container">
    <swiper 
      class="swiper-box" 
      :vertical="true" 
      :circular="true" 
      @change="onSwiperChange"
    >
      <swiper-item v-for="(item, index) in videoList" :key="item.id">
        <view class="video-item">
          <!-- 视频播放器 -->
          <video 
            :id="'video-' + index" 
            :src="item.videoUrl" 
            :controls="false" 
            :loop="true"
            :autoplay="index === currentIndex"
            objectFit="contain"
            class="player-video"
            @play="handlePlay"
          ></video>
          
          <!-- 侧边互动栏(点赞、评论) -->
          <view class="sidebar">
            <view class="avatar">
              <image :src="item.userAvatar" mode="aspectFill"></image>
            </view>
            <view class="action-btn" @click="toggleLike(item)">
              <text class="iconfont">{{ item.isLiked ? '❤️' : '🤍' }}</text>
              <text class="count">{{ item.likeCount }}</text>
            </view>
          </view>
          <!-- 底部信息 -->
          <view class="bottom-info">
            <text class="username">@{{ item.userName }}</text>
            <text class="desc">{{ item.title }}</text>
          </view>
        </view>
      </swiper-item>
    </swiper>
  </view>
</template>
<script setup lang="ts">
import { ref, onMounted, nextTick } from 'vue';
import { getVideoFeed } from '@/api/video'; // 假设封装了API请求
const videoList = ref([]);
const currentIndex = ref(0);
onMounted(async () => {
  await loadVideos();
  // 自动播放第一个视频(部分平台需用户交互后才能播放)
  playVideo(0);
});
const loadVideos = async () => {
  const res = await getVideoFeed(1, 5); // 调用后端接口
  videoList.value = res.records;
};
const onSwiperChange = (e) => {
  // 停止上一个视频
  pauseVideo(currentIndex.value);
  // 更新当前索引
  currentIndex.value = e.detail.current;
  // 播放当前视频
  playVideo(currentIndex.value);
};
const playVideo = (index) => {
  const videoContext = uni.createVideoContext(`video-${index}`);
  videoContext.play();
};
const pauseVideo = (index) => {
  const videoContext = uni.createVideoContext(`video-${index}`);
  videoContext.pause();
};
const toggleLike = (item) => {
  item.isLiked = !item.isLiked;
  item.likeCount += item.isLiked ? 1 : -1;
  // 此处调用后端接口更新数据库
};
</script>
<style>
.container { height: 100vh; width: 100vw; background: #000; }
.swiper-box { height: 100%; width: 100%; }
.video-item { height: 100%; width: 100%; position: relative; }
.player-video { height: 100%; width: 100%; }
/* 侧边栏样式 */
.sidebar { position: absolute; right: 10px; bottom: 100px; display: flex; flex-direction: column; align-items: center; }
.avatar { width: 50px; height: 50px; border-radius: 50%; border: 2px solid #fff; overflow: hidden; margin-bottom: 20px; }
.action-btn { display: flex; flex-direction: column; align-items: center; margin-bottom: 20px; color: #fff; }
/* 底部信息样式 */
.bottom-info { position: absolute; left: 10px; bottom: 20px; width: 80%; color: #fff; }
.username { font-weight: bold; font-size: 16px; margin-bottom: 5px; display: block; }
.desc { font-size: 14px; opacity: 0.9; }
</style>

四、 避坑指南与上线优化

结合项目经验,这里有几个关键点需要注意:

  1. 视频加载优化:不要一次性加载所有视频,使用“预加载”机制。在用户滑动到 index+1 时,在后台静默加载下一个视频资源。
  2. 视频格式转码:用户上传的视频格式五花八门,后端必须使用 FFmpeg 进行统一转码(转为 H.264 编码的 MP4),否则在 Android 和 iOS 上会出现兼容性问题。
  3. CDN 加速:如果你想真正做到“可上线”,视频资源必须走 CDN。MinIO 只是存储,必须结合 CDN 回源配置,否则用户播放会卡顿。
  4. Uniapp 原生插件:对于视频的高级特效(滤镜、贴纸),Uniapp 默认的 <video> 标签能力有限,可能需要购买或引入原生插件(如 DCloud 市场的短视频 SDK)。 这套方案跑通后,你不仅拥有了一个可演示的 App,更掌握了一条完整的“短视频后端+跨端前端”技术链。对于做副业来说,这既可以作为外包项目的案例,也可以作为你自己打造独立产品的起点。

版权声明:本文内容由TeHub注册用户自发贡献,版权归原作者所有,TeHub社区不拥有其著作权,亦不承担相应法律责任。 如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

点赞(0)
收藏(0)
n0PBvtQpx9
暂无描述

评论(0)

添加评论