微短剧已经卷成红海,但从“看到需求”到“小程序上线破万日活”,其实还有一条被很多人低估的快速通道。我今年用 uni-app 从零搭了一款微短剧小程序,前后只花了 7 天,上线第二周日活就破万。这篇文章不是复盘会上的漂亮 PPT,就是这段时间踩过的真实步骤、选型逻辑和一堆在文档里查不到的坑,适合准备入局的开发者、内容运营者,以及那些正犹豫要不要用 uni-app 快速验证业务的人。
这类项目最大的特点不是技术难度高,而是链路长:要解决内容从哪来、用户怎么看、怎么分享裂变、怎么过审上线,还要在小程序包体、视频体验和审批合规之间反复横跳。我用一套很务实的方案把这些问题全部串了起来,下面按设计和实操顺序逐一拆开。
1. 项目设计与技术选型:为什么是“uni-app + 微短剧小程序”
1.1 微短剧小程序到底在解决什么需求
微短剧本质上提供的是“极低门槛的即时娱乐”。用户不用下载 App,看到朋友圈或群里的海报扫码就进,一集 1 到 2 分钟,坐地铁、排队的碎片时间就能刷完。这种场景和小程序的“用完即走”天然匹配。
但单纯的 H5 页面做不到被微信搜索、被分享卡片带出小程序的完整链路,也没有小程序原生的登录、订阅和消息触达能力。所以微短剧平台最好的形态不是 App,也不是 H5,而是微信小程序。
我当初立项时定的目标非常明确:一周内上线,验证三个核心假设——用户是否愿意为短剧付费、分享裂变能否带来新增、跨端框架能否支撑后续快速迭代。这三个假设直接决定了技术选型,而不是反过来先纠结用什么框架。
1.2 技术选型:uni-app 到底赢在哪
先说结论:单从微信生态看,原生小程序开发也能完成这件事;但一旦考虑“以后可能还要上支付宝小程序、抖音小程序”,原生开发就要写三套。微短剧项目最怕的不是功能复杂,而是业务方向需要经常调头,这时候多端复用的价值会无限放大。
uni-app 赢在四个具体方面。
第一,Vue 语法开发效率高。团队里只要熟 Vue,基本不用额外学习成本,模板、组件化、路由都沿用既有习惯,比原生 WXML 顺手很多。第二,HBuilderX 的调试链路短。写完代码直接运行到微信开发者工具,真机预览、上传代码都在一个 IDE 里完成,省去了大量环境配置。第三,条件编译能精细控制平台差异。这个项目里我在微信端用了自定义导航栏、视频组件等原生能力,在其他端退回普通页面样式,一套代码各取所需。第四,uniCloud 可以同时承担后端。上线初期我没有单独买服务器,用户登录、剧集列表、观看记录这类轻接口全部用云函数实现,既免运维又支持扩容,等日活真正上来再迁到独立后端也不迟。
如果一定要说缺点,那就是 uni-app 对微信原生组件的封装有自己的一套逻辑,遇到原生视频 component 的层级问题时,需要通过 uni.createVideoContext 和 cover-view 手动补位,这个后面单独讲。
1.3 整体架构与数据流
项目整体结构分四层:展示层、业务层、数据层和内容存储层。
展示层就是小程序内的所有页面,包括首页信息流、短剧详情页、播放页、个人中心、搜索页等。业务层负责登录态、会员状态、观看进度、分享参数解析等逻辑。数据层通过 uni.request 访问云函数接口,所有接口返回统一格式:{ code, data, message }。内容存储层用对象存储 + CDN 存放短剧视频和封面图,播放地址经过签名,过期自动失效。
页面跳转我做了严格规划,核心原则是“列表页不直接播放视频,播放页永远独立成页”。首页信息流只展示封面、标题、集数和短剧标签,用户点击后先进入短剧详情页,再点击播放才进入播放页。这样既能避免在小程序页面栈里同时存在多个播放器实例,也能让用户在看剧前先完成“收藏短剧”“查看简介”等关键动作,对后续留存和会员转化都有帮助。
2. 核心功能拆解与实现要点
2.1 首页信息流:重缓存、轻渲染
首页信息流是所有用户看到的第一屏,性能直接决定首周留存。我没有选择在首页直接埋视频流,而是采用了“封面大卡 + 文字标签”的混合信息流,每条数据包含封面图、剧名、主演、更新状态、热度值。
数据加载做了两层优化。第一层是云函数侧按分页返回,每次只出 10 条,滚动到底部通过 onReachBottom 触发加载更多。第二层是客户端缓存,用uni.setStorageSync把上一批数据缓存 10 分钟,用户再次打开时先渲染缓存,再静默请求刷新。这个策略非常管用,很多用户是分享裂变带来的,首次打开时如果网络慢,至少能看到上一批内容而不是白屏。
关键代码我封装成了一个模块,核心思路如下:
const getDramaList = async (page) => { const cacheKey = `drama_list_${page}`; const cacheData = uni.getStorageSync(cacheKey); if (cacheData && cacheData.expireTime > Date.now()) { return cacheData.data; } const res = await uni.request({ url: `${BASE_URL}/drama/list?page=${page}`, method: 'GET' }); if (res.data.code === 0) { uni.setStorageSync(cacheKey, { expireTime: Date.now() + 10 * 60 * 1000, data: res.data.data }); return res.data.data; } return cacheData ? cacheData.data : []; };有一个容易被忽略的细节:缓存的 key 必须带分页参数,很多新人只缓存第一页,结果下拉刷新时第二页、第三页全部回源,弱网环境下体验非常差。
2.2 播放体验与页面栈管理
播放页是微短剧小程序的生命线,没有之一。我在这里踩过最大的坑就是视频组件和普通组件的层级关系,微信小程序里的 video 是原生组件,层级天然高于普通 view 和 button,直接导致我做的弹幕、下一集按钮、返回按钮被视频盖住,点击完全失效。
解决方案是使用cover-view和cover-image来覆盖在 video 之上。下一集按钮、进度展示、收藏按钮这些高频交互,全部用 cover-view 实现。还有一点要特别注意:cover-view 内部不支持复杂样式,flex 布局偶尔会失效,所以我尽量用绝对定位控制位置,减少嵌套。
另一个关键问题是播放器实例数量。我在设计页面栈时强制规定:一次只存在一个播放器实例。用户从播放页返回短剧详情页时,必须在 onHide 里暂停播放,离开页面时销毁实例。否则用户连续看完三集后,页面栈里还挂着三个视频实例,安卓机上直接卡到掉帧。
onLoad() { this.videoContext = uni.createVideoContext('player', this); }, onShow() { // 恢复播放进度 const pos = this.getPlayPos(this.dramaId, this.episodeId); if (pos > 0 && this.videoContext) { this.videoContext.seek(pos); } }, onHide() { // 离开页面立即暂停并记录进度 if (this.videoContext) { this.videoContext.pause(); this.savePlayPos(this.dramaId, this.episodeId, this.currentTime); } }iOS 和安卓在自动播放策略上还有差异。iOS 下如果视频不是静音状态,自动播放经常被系统拦截。我的应对方式是首集进入播放页时先不自动播放,展示封面和播放按钮,让用户明确点击后才开始播放。这样反而让用户有“主动开始追剧”的心理暗示,播放完成率比强制自动播放还要高。
2.3 用户系统、会员与观看记录
用户系统是短剧平台商业化的基础。我用uni.login获取 code,传给云函数换取 openid,再通过 openid 关联用户表。手机号授权按钮必须放在用户主动触发的位置,不能一进页面就弹。
会员体系我没有做复杂的订阅逻辑,第一版只实现了三种状态:免费用户、已购买整剧用户、单集付费用户。付费逻辑放在云函数里,客户端不做任何价格判断,防止被绕过。用户购买成功后云函数直接更新会员状态,客户端拉取用户信息时同步。
观看记录我用了一张数据表,字段包括 dramaId、episodeId、playPos、updateTime。用户进入播放页时先读本地缓存,同时异步请求服务端记录做合并。这里有一个关键取舍:服务端记录只在用户离开播放页或主动收藏时上报一次,避免那种每播 5 秒打一次接口的死亡请求量。
2.4 缓存、动态标题与导航栏适配
微信小程序的标题和导航栏经常被轻视,但实际细节非常多。微短剧每个页面都对应不同剧名,我在短剧详情页和播放页的 onLoad 里动态设置标题:“正在追剧:《${dramaTitle}》”,用的就是uni.setNavigationBarTitle。不仅让用户知道自己在哪个剧里,也让分享出去的卡片标题更有吸引力。
自定义导航栏高度计算也是老生常谈但必踩的坑。我直接用uni.getMenuButtonBoundingClientRect()拿到右上角胶囊的位置,再结合uni.getSystemInfoSync()里的状态栏高度,算出导航栏的总高度,然后给页面顶部预留出对应占位。这里要注意:不同机型的胶囊高度和状态栏高度都不一样,不能写死数值。
缓存策略方面,剧集列表、首页信息流、搜索关键词这些热数据全部做了过期缓存,会员状态和用户信息只缓存 5 分钟,确保用户付费后 APP 能较快看到效果。比较敏感的视频播放地址不做本地缓存,防止签名过期后播放失败。
3. 七天上线全记录:从 HBuilderX 新建项目到提审通过
3.1 第 1 到 2 天:基础设施、接口约定和页面骨架
第一天我没有急着写界面,而是先把 HBuilderX 项目创建好,配好微信小程序 appid 和基础目录结构,项目分主包和分包:主包只放首页、登录页、个人中心和公共组件,短剧详情页和播放页全部放分包。这样做的直接原因是微信小程序主包有 2MB 限制,视频封面图稍微多一点就会超,分包可以避开这个问题。
同一天我定义了前后端接口规范。所有接口统一请求头带 token,后端返回统一结构,错误码从 1001 开始递增。这条规范看着简单,但后面所有同事同时开发时几乎没有联调矛盾。
第二天开始搭建首页和个人中心原型。我用 flex 布局做封面瀑布流,没有引入重型 UI 组件库,因为微短剧页面追求的是沉浸感和视觉冲击,通用组件库的样式反而要反反复改。骨架屏我用的是最简单的 image 占位图,首屏加载时显示灰色背景块,数据回来后替换。
3.2 第 3 到 5 天:播放器、登录链路和云函数联调
第三天到第五天是整个开发过程中最紧张的阶段。播放器功能涉及视频源接入、cover-view 覆盖层、播放进度记录、剧集切换四个模块。我先用一段测试视频打通了播放链路,再接入真实短剧内容源,确保 CDN 的签名地址可以被小程序正常解析。
登录链路放在第四天。我用云函数实现wx.login的 code 交换逻辑,成功后在客户端存储 token,并把用户基础信息写入数据库。手机号授权按钮也在这天接入,但只在用户点击“开通会员”时才弹出,避免打扰普通浏览用户。
第五天主要做联调,把首页信息流、短剧详情、搜索、收藏四个模块串起来。搜素功能第一版只做了剧名关键词匹配,支持联想词展示。这个阶段最容易翻车的是分页数据,首页每次加载 10 条,播放记录最多显示最近 50 条,这些参数如果不是前后端统一写死,就会出现重复数据和漏数据。
3.3 第 6 到 7 天:合规自查、提审和上线
小程序类目审核是整个项目里最不可控的环节。微短剧涉及视频内容,必须选对类目,我提前准备了三样东西:营业执照、文娱类目相关资质说明、内容安全承诺函。如果你个人开发者身份不好申请视频类目,可以考虑借助已有资质的公司主体,或者先以“视频播放器”类目过审,后续再申请更准确的内容类目。
第六天我专门做了内容安全自查。逐条检查所有短剧的海报、简介、剧名,确认没有敏感词汇和打擦边球的内容,同时给短剧内容列表加上了“内容介绍”和“适龄提示”字段。这一块做不严谨,审核阶段可能直接被下架。
第七天上午提交审核,下午就被打回了一次,原因是用户协议里的隐私政策没有明确写出“收集设备信息用于统计”。我补上隐私政策说明后重新提审,当晚通过。上线那一刻真实感受不是兴奋,反而是庆幸没有在资质和文案上再翻车。
4. 上线前后绕不开的坑与排查记录
4.1 测试版过期和扫码失效
项目进行到第六天时,团队里几个运营同学用微信扫码体验开发版,结果有人报错“开发版小程序已过期,请在开发者工具重新扫码”。这个报错出现在我用开发者工具上传了新版本但团队其他成员没有重新打开开发者工具的情况下。
实际原因很简单:微信开发者工具生成的预览版和开发版二维码都有有效期,过期后必须重新在工具里点“预览”或“上传”并扫码。团队成员只是重新扫了旧的二维码,当然过不去。解决办法是约定每次上传新版本后,在工作群里同步最新的预览二维码,并让成员在微信里删除旧的小程序入口再扫码。
顺带说一句,小程序正式版也会涉及年审问题。有些同学开发完就忘了一年后还要年审,结果线上小程序突然被暂停服务,整个用户群瞬间炸锅。我在项目管理日历里设好了到期前两个月的提醒,这种事必须前置。
4.2 视频组件遮挡页面的问题
前面提到的 cover-view 只是一半,另一半是容器布局。播放页底部我放了“下一集”“换一集”两个按钮,直接用 cover-view 写在 video 内部会随着视频尺寸适配很差。我的做法是给 video 外面再包一层固定高度的容器,cover-view 全部放在容器内部,用绝对定位对齐到底部。
还有一个小技巧:当播放器处于全屏状态时,cover-view 的定位基准会发生变化。全屏后要立刻监听bindfullscreenchange事件,重新计算按钮位置,否则你在全屏时点“下一集”,按钮实际点击区域还在半屏的位置,用户会以为触控失灵。
4.3 缓存、弱网和接口状态码处理
上线后我发现一个很有意思的数据:每天有将近 15% 的播放请求在接口层报错,但用户并没有大量流失。原因是播放地址做了签名,签名过期后在弱网环境下的失败概率会明显上升。
处理方法从两个方向同时做。第一,播放地址失效时自动重新请求新地址,并在视频 error 事件里触发重试,最多重试 2 次。第二,用户播放记录和下一集信息提前缓存,弱网时先展示缓存数据,后台慢慢拉取新数据,而不是一进页面就白屏转圈。
这里要特别提醒:微信小程序的uni.request成功回调不代表业务成功,一定要统一检查res.data.code是否为 0。我看到很多新手只检查 HTTP 状态码,结果接口逻辑报错了还当成成功处理,白屏了都不知道是数据问题。
4.4 上线初期的常见问题速查表
| 问题现象 | 可能的根因 | 我的处理方式 |
|---|---|---|
| 视频播放页白屏 | 视频地址签名过期 / CDN 防盗链校验失败 | 请求新播放地址并重新绑定 video src |
| 首页刷新后重复数据 | 分页参数未统一 | 前后端统一 page 从 1 开始,size 固定 10 |
| 播放进度自动回到开头 | onHide 未记录进度 | 离开页面前主动调 videoContext.pause 并存储进度 |
| 分享卡片显示默认图标 | 未设置 onShareAppMessage 的 imageUrl | 用海报图替代默认截图,提升点击率 |
| 安卓低端机播放卡顿 | 单页面残留多个播放器实例 | 页面销毁时统一关闭播放器并释放资源 |
| 用户反馈按钮“失灵” | cover-view 被视频覆盖或层级错乱 | 全屏切换时重新计算按钮定位 |
5. 从上线到日活破万,我做了哪些增长动作
5.1 分享裂变链路:海报和小程序码
小程序天然适合裂变,但前提是你把分享链路做顺。我在每个短剧详情页都放了一个“分享给好友”按钮,点击后生成一张专属海报,海报包含短剧封面、推荐语和带参数的小程序码。
生成海报的技术方案我用的是 canvas 绘制。先在离屏 canvas 上画背景图和文字,再调用uni.canvasToTempFilePath导出图片。这里有个坑:canvas 绘制图片必须等图片加载完成,否则画出来是空白。我用了一个Promise.all等待所有封面图和二维码都加载完毕,再一次性绘制,稳定后基本不会再翻车。
带参数的小程序码很关键。每个分享者对应一个唯一 scene 参数,新用户通过这个码进入小程序后,我就能分析“这个用户是从哪个短剧、哪个分享者带来的”。上线初期我把 80% 的运营精力都放在打磨这条链路上,因为微短剧的爆发式增长基本都靠这个机制驱动。
5.2 内容运营:更新节奏和追剧提醒
日活破万的核心不是单纯拉新,而是让用户“今天看完,明天还来”。微短剧如果没有追剧节奏,用户很容易看完一批就走。我在内容侧做了两个机制。
第一是固定更新节奏:每天中午 12 点和晚上 8 点各更新一次片单,首页顶部展示“今日更新”入口。第二是追剧提醒:用户看到“最新一集可看”但没看时,可以通过订阅消息提醒第二天来看。小程序订阅消息需要用户主动点击授权,我在播放页加了一个“追更提醒”按钮,点击次数很可观。
实际操作中,我发现晚上 8 点到 10 点是观看高峰,所以重点剧目都放在这个时段更新,用户打开小程序后正好有新内容,强留存效果非常明显。
5.3 数据指标和迭代节奏
上线一周后我每天看的数据主要有五个:日活、次日留存、人均观看集数、单集播放完成率、分享率。其中最重要的不是日活,而是单集播放完成率。如果完成率低,说明内容或带货不行,再多的流量都会浪费。
第一周结束时,次日留存从 18% 提升到了 32%,单集播放完成率稳定在 60% 以上。而日活破万的直接触发点,是一天晚上某个群里有人分享了“我正在追《XXX》”的卡片,那个场景参数带来了一整波近乎病毒式的扩散,当天新增用户超过 8000。
我复盘时发现,破万并非偶然。前六天我把内容页、播放页、分享链路和缓存都打磨得足够稳,当流量突然爆发时,系统没有崩,视频加载没有卡,分享链路没有断,用户进来后顺利走完了“看剧 - 追剧 - 分享”的完整闭环。如果缓冲链路没做好,那一波流量可能瞬间就流失了。
最后分享一点个人体会
这个项目让我最深的感触是:微短剧小程序的技术门槛其实不高,高的是把内容、工具和增长这三条线同时理顺的能力。uni-app 帮我把跨端和工程化的问题兜住了,让我能把精力集中在播放体验和分享裂变这些真正影响留存的地方。
如果你也想做类似的项目,我的建议是先别急着上完整会员系统和复杂推荐算法,第一版能把“用户看到剧 - 顺利播放 - 愿意分享 - 明天再来”这条链路跑通,就已经赢过市面上绝大多数同类小程序了。
还有一个小技巧分享给大家:小程序上线后一定不要停止代码迭代,第一周我每天都发一个版本,每版只修一个核心体验痛点,比如调首页加载速度、优化播放页按钮位置、提升分享海报清晰度。高频迭代配合数据反馈,才是小团队做爆款内容产品的正确姿势。