基于UniApp的美妆教程微信小程序开发实践
前阵子帮朋友把一个美妆教程平台从零搭到了上线技术栈选了UniApp目标平台是微信小程序。做这类内容型小程序真正让人头疼的不是UI要画得多精美而是视频内容怎么在小程序里稳定播放、图文教程怎么优雅展示、登录支付分享这些基础能力怎么跟UniApp的跨端逻辑对齐。项目从立项到提审大概花了三周中间踩了不少坑也沉淀出一套可以复用的方案。今天把这些东西整理出来如果你正准备做美妆教程、知识付费、内容社区这类微信小程序或者拿UniApp做毕业设计应该能从里面少走很多弯路。1. 项目整体设计与技术选型1.1 为什么选UniApp而不是原生小程序很多人在做微信小程序的时候第一反应都是用原生语法直接写。如果是纯展示型的落地页原生确实没毛病但对于美妆教程平台这种典型的内容型产品我建议你冷静想一下后续的扩展路径。美妆教程的用户触点大概率不止微信小程序一个公众号文章里要嵌H5版本、抖音上可能要搞一个小程序版本、后续如果要做独立App底座也得提前留好。UniApp用Vue语法写一套代码编译到微信小程序、H5、App、抖音小程序等多个端这套跨端能力对内容型产品来说价值极高。开发周期能压缩到原生方案的60%左右尤其是页面结构、组件逻辑、接口封装这些都是可以完全复用的。另一个现实原因是生态成熟度。UniApp插件市场里现成的组件很多比如富文本渲染、视频播放器、分享海报、扫码插件美妆教程平台用到的能力几乎都有现成方案不需要像原生小程序那样到处找轮子或者自己造。加上UniApp的社区活跃遇到问题搜一圈基本都能找到解决方案。1.2 功能模块拆解美妆教程平台的核心不是“好看”而是内容消费闭环。我按业务链路把功能拆成了六个模块首页推荐流瀑布流卡片展示教程封面、标题、播放量用户进来第一眼就能看到优质内容。分类浏览护肤、彩妆、发型、美甲、香水每个分类下再细分标签比如彩妆下面可以分底妆、眼妆、唇妆。教程详情图文步骤讲解 视频教学 产品清单这是内容消费的主场景。视频播放横屏/竖屏适配、清晰度切换、进度记忆、循环播放。个人中心收藏列表、观看记录、已购付费专栏、肤质偏好设置。付费专栏单课购买、打包会员接微信支付配套订单管理。数据层面我设计了六张核心表用户表、教程表、分类表、视频表、收藏表、订单表。教程表跟视频表是一对多的关系一个美妆教程可能会包含多个短视频步骤所以视频表里专门加了sort字段控制播放顺序。分类表用parent_id做树形结构方便后续扩展二级分类。1.3 核心流程梳理整个产品的核心链路是用户进入首页看到瀑布流 - 点击教程卡片进入详情页 - 看图文步骤 - 点击视频按钮播放教学视频 - 看完后收藏/分享/购买付费专题。技术层面的核心链路是前端通过uni.request请求后端接口 - 后端用JWT校验登录态 - 返回教程和视频数据 - 前端渲染列表和详情。考虑到美妆教程图片多、视频体积大我在列表接口里只返回封面图URL和视频时长详情页再拉完整信息避免首页加载过慢。同时把首页接口数据用uni.setStorageSync做了半小时缓存实测冷启动速度提升明显。后端我选了Java Spring Boot用微信小程序的code2Session换取openid再签发自定义的JWT token。选Java而不是Node或uniCloud主要是考虑到后续可能会有管理后台、数据分析、运营系统要接Java在后端生态上更稳也方便团队里其他人接手。2. UniApp项目配置与开发环境搭建2.1 manifest.json配置到底要怎么填UniApp里最容易踩坑的就是manifest.json这个文件直接决定了编译到微信小程序后的行为。分享一份我实际用下来的微信小程序最小可用配置{ name: 美妆教程平台, appid: , description: 美妆教程内容平台, versionName: 1.0.0, versionCode: 100, transformPx: false, mp-weixin: { appid: 你的小程序appid, setting: { urlCheck: false, es6: true, postcss: true, minified: true }, usingComponents: true, permission: { scope.userLocation: { desc: 用于推荐附近的美妆门店 } }, requiredPrivateInfos: [getLocation], lazyCodeLoading: requiredComponents }, vueVersion: 3 }有几个关键点要重点说。appid必须填真实的微信小程序appid这个在微信公众平台注册后就能拿到。setting里的urlCheck开发阶段可以设成false这样本地调试时不需要把接口域名加到白名单但发布前一定要改回true否则线上环境请求会被拦截。permission和requiredPrivateInfos是给定位能力用的如果你的美妆平台要做门店推荐或者同城功能这两个字段必须配否则wx.getLocation会直接报错。lazyCodeLoading建议设为requiredComponents可以显著减少小程序首包体积类似按需加载实测包体积能少20%左右。另外很多人不知道uniapp的manifest里还支持h5端的域名配置。如果你的项目H5端也要用并且涉及多个接口域名可以在h5下配置devServer的proxy或者直接维护一个环境变量文件打包时用不同的BASE_URL。这是H5端指向两个域名的常见做法比如一个域名走静态资源、一个域名走接口。2.2 从HBuilderX到微信开发者工具的完整流程很多新手卡在第一步代码写在HBuilderX里怎么在微信开发者工具里看到效果其实流程非常简单但确实有细节要注意。第一步用HBuilderX创建UniApp项目模板选“默认模板”就行如果你要Vue3就选Vue3版本Vue2的项目后续维护会比较吃力我不推荐新项目再开Vue2。第二步打开manifest.json在微信小程序配置项里填入你的appid。如果没有appid可以在微信公众平台先注册一个测试号开发阶段用测试号足够。第三步菜单栏点击“运行 - 运行到小程序模拟器 - 微信开发者工具”。前提是你的电脑上已经安装了微信开发者工具并且在微信开发者工具的“设置 - 安全设置”里打开“服务端口”。这个端口不开HBuilderX根本推不过去。第四步发行上线时点击“发行 - 小程序-微信”HBuilderX会生成一个unpackage/dist/build/mp-weixin目录然后用微信开发者工具导入这个目录上传代码再去微信公众平台提交审核。发布前记得检查三件事appid是否正确、接口域名是否加入白名单、request合法域名是否配置了https证书的完整链路。我遇到过好几次项目运行正常、一上传审核就被打回的情况基本都是域名白名单和内容安全这两块的问题。2.3 生命周期管理与页面切换的坑UniApp的页面生命周期跟Vue组件生命周期是两个体系这一点必须搞清楚。页面级生命周期主要有onLoad、onShow、onReady、onHide、onUnload它们控制的是页面从创建到销毁的完整过程而Vue的created、mounted这些是组件级生命周期页面onLoad触发时组件的created已经执行完了。美妆教程平台里我踩得最深的一个坑是tabBar页面切换时底部导航闪烁。现象很典型从首页切到分类页再切回来时首页会白屏一下底部tabBar还会闪。排查下来原因有三个一是tabBar页面在onShow里重新拉接口网络慢时页面处于空白状态二是页面栈切换动画还没结束新数据就渲染了三是懒加载组件在tab切换时被重新初始化。解决方案也很直接。首页推荐流的数据在onLoad时拉一次放到内存里onShow时不再重复请求只有当用户下拉刷新时才强制更新。同时给tabBar页面加了页面预加载处理在pages.json里配置页面参数让相邻tab页面提前初始化。这两个操作组合起来闪烁问题基本消失。另外还要提一下onBackPress这个生命周期。小程序里用户点左上角返回默认是navigateBack但在web-view子页面、自定义导航栏场景下默认行为往往会失效或者不符合预期。我在教程详情页里用onBackPress做了返回拦截先弹窗确认是否退出用户确认后再手动调用uni.navigateBack体验比默认返回好很多。2.4 多端差异与条件编译UniApp号称一套代码多端运行但实际开发中你很快就会发现不同端的差异比想象中大。最典型的就是导航栏微信小程序的胶囊按钮位置、状态栏高度、导航栏标题样式跟H5和App完全不一样。我的做法是写一个自定义导航栏组件动态计算顶部高度。微信小程序端用uni.getMenuButtonBoundingClientRect获取胶囊按钮的位置再用uni.getSystemInfoSync().statusBarHeight获取状态栏高度两者相加就是自定义导航栏的安全高度。H5端就简单了直接给一个固定高度比如44px。App端如果使用沉浸式状态栏还需要额外适配。具体代码片段// 自定义导航栏高度计算 const getNavBarHeight () { const systemInfo uni.getSystemInfoSync() const menuButton uni.getMenuButtonBoundingClientRect() if (menuButton menuButton.top) { const statusBarHeight systemInfo.statusBarHeight || 20 const navBarHeight (menuButton.top - statusBarHeight) * 2 menuButton.height return { statusBarHeight, navBarHeight } } return { statusBarHeight: systemInfo.statusBarHeight || 20, navBarHeight: 44 } }跨端差异还需要用条件编译来处理语法很简单在注释里声明平台即可!-- #ifdef MP-WEIXIN -- view classweixin-only仅微信小程序可见/view !-- #endif -- !-- #ifdef H5 -- view classh5-only仅H5可见/view !-- #endif --美妆教程平台里视频封面图在小程序端和H5端的缩放模式不一样我用了条件编译分别调objectFit参数小程序端用cover保证封面不变形H5端用fill配合自适应容器。3. 美妆教程内容核心实现3.1 视频列表与播放体验美妆教程这类内容跟游戏、影视完全不同它的核心价值在于实操步骤看得清。底妆怎么涂、眼线怎么画镜头稍微不清楚用户就划走了。所以视频播放体验必须作为第一优先级来对待。视频组件上我直接用微信小程序的video组件不推荐在小程序里套video.js之类的H5播放器性能差距太明显了。UniApp对这种写法很友好video组件在App和H5端也能用唯一要注意的是在不同端上的属性支持度有差异比如原生控件在小程序端表现就不太一样。播放器参数建议这样配置video :srccurrentVideo.url :controlstrue :autoplayautoPlay :loopfalse :objectFitobjectFit :show-center-play-btntrue :enable-progress-gesturetrue playonVideoPlay erroronVideoError endedonVideoEnded classvideo-player /video美妆教程绝大多数是竖屏视频长短不一建议在视频数据里直接带上宽高比前端用aspect-fill或者contain自适应。用户点击某个步骤的视频后自动切换视频源并续播这一步要用video组件的id控制或者直接绑定不同的src否则切视频时会出现黑屏闪烁。视频存储我选了腾讯云点播主要看中两点一是小程序端的兼容性好视频格式不用转码也能直接播放二是防盗链能力完整可以设置Key防盗链和Referer防盗链对美妆教程这种原创内容来说很重要。如果你的预算有限用对象存储直传也能凑合但一定要自己给URL加签名否则视频地址被别人扒走就是公开资源了。3.2 图文教程与mp-html富文本美妆教程和很多内容类产品一样图文混排是刚需。产品清单要用表格、护肤步骤要加序号、成分介绍要带链接用小程序原生的rich-text组件虽然能解析一部分HTML但碰到表格、自定义样式、图片懒加载就非常吃力。我最终选了mp-html这个组件UniApp生态里富文本渲染的标配方案。它支持完整的HTML标签解析表格、列表、图片、视频都能渲染还能配置图片懒加载和预览点击事件。安装方式很简单在插件市场搜索mp-html下载放到项目components目录然后在页面里注册使用mp-html :contentdetailContent linktaponLinkTap imgtaponImgTap /需要强调两点。第一后端返回的HTML内容一定要做安全过滤尤其是script标签必须去掉否则会有XSS风险。可以在后端统一用Jsoup处理前端侧mp-html本身也做了安全过滤但双保险更放心。第二图片地址建议用CDN域名且带宽高信息避免富文本渲染时页面布局跳动。实际测试下来一个包含图文步骤、产品清单表格、注意事项列表的美妆教程详情页用mp-html渲染后性能表现稳定长内容滚动也没有卡顿问题。唯一的坑是mp-html对自定义字体支持一般如果你的设计稿里用到了特殊字体建议降级成系统字体或者用图片替代标题效果。3.3 内容保护视频防盗、图片防盗与其他思路做内容平台的人最关心的一件事就是内容安全美妆教程更是如此花了不少人力拍的教程被别人一个录屏拿走搬走就亏大了。先聊视频防盗。最基础的是给视频URL加签名并且签名到期时间要短比如10分钟有效。就算用户把视频地址分享出去过几分钟就失效了能有效防止二次传播。稍微进阶一点的做法是用腾讯云点播的防盗链可以做到按URL、按时间段、按IP限制访问对平台型产品来说已经足够。还要注意视频域名不能跟接口域名混用视频走CDN域名时Referer防盗链要单独配置。再聊图片防盗。小程序里的图片默认长按会弹出保存菜单虽然可以设置show-menu-by-longpress为false来禁止但部分安卓机型仍然能通过截图、录屏等方式带走。我采取的组合方案是重要点位图加半透明水印、长图切片加载、配合层级低的木马图兜底。水印文字用“用户ID手机尾号”这种动态方案既能追溯泄露源头也能给普通用户一些心理威慑。关于防录屏我必须诚实说一句纯前端方案无法彻底阻止系统级录屏。微信小程序提供了onUserCaptureScreen事件可以监听用户截屏但录屏事件目前没有统一的官方API。能做的是在视频播放时叠加动态水印让水印跟着播放时间实时变化这样就算被录屏录出来的内容上也铺满了可追溯的水印。实测这种水印对搬运党有明显的劝退作用。另外安卓端可以用一些原生插件限制截屏但iOS端限制就非常有限传播风险只能靠水印和版权声明来兜底。3.4 音频与静音播放细节美妆教程平台里除了视频讲解还有一类内容是背景音乐、语音讲解和ASMR类的卸妆演示。这里最坑的问题就是iOS静音键手机开了静音视频或者音频播放直接没声音。用户以为自己手机坏了其实是iOS系统把声音给切了。解决办法是调用微信小程序的音频设置接口让音频可以混着静音键播放// 微信小程序端 wx.setInnerAudioOption({ mixWithOther: true, obeyMuteSwitch: false, success: () { console.log(音频设置成功静音模式下可播放) } })在UniApp中可以通过条件编译调用wx.setInnerAudioOptionApp端则用plus.audio.setAudioMode或者html5的audio设置。遇到iOS静音无声问题优先检查这个参数。另外视频播放器本身有muted属性如果用户没有主动静音页面初始化时不要设置muted否则也会出现有声变无声的问题。3.5 视频下载与内容导出限制很多内容平台都在设法限制视频下载但你猜怎么着微信小程序官方其实根本没有提供直接下载视频到本地相册的API。用户只能通过wx.downloadFile把视频下载到小程序本地缓存目录再通过wx.saveVideoToPhotosAlbum保存到相册但这两个接口有平台规则限制必须用户主动触发且明确授权。我在美妆教程平台里直接放弃了这个功能因为原创类教程的版权比用户体验更重要。分享出去的海报我倒是做了用canvas把教程封面、标题、二维码合成一张分享图用户长按保存到相册发朋友圈这是主流的裂变方式。海报模板单独用canvas画太繁琐我直接拼了一个现成组件在插件市场搜“分享海报”就能找到一堆选个star多的就行。4. 微信小程序关键能力集成4.1 登录、鉴权与用户体系美妆教程平台需要用户体系来支撑收藏、购买、记录进度所以登录是第一步。微信小程序的登录流程是前端调用wx.login获取一个临时code把code传给后端后端拿着code加AppID和AppSecret去微信的code2Session接口换取openid和session_key。openid就是用户在小程序里的唯一标识session_key用来解密用户信息。这里要提醒一下不要把AppSecret放到前端代码里一定要留在后端。你在微信公众平台看到的AppSecret泄露出去等于任何人可以冒充你的小程序做登录。Java后端的核心代码大概是这样的PostMapping(/api/auth/login) public Result login(RequestBody LoginRequest request) { String code request.getCode(); // 请求微信接口获取openid String url https://api.weixin.qq.com/sns/jscode2session?appid appId secret appSecret js_code code grant_typeauthorization_code; String result restTemplate.getForObject(url, String.class); JSONObject json JSON.parseObject(result); String openid json.getString(openid); // 查库或创建用户 User user userMapper.selectByOpenId(openid); if (user null) { user new User(); user.setOpenId(openid); userMapper.insert(user); } // 签发JWT token String token JwtUtil.createToken(user.getId()); return Result.success(token); }前端拿到token后存到uni.setStorageSync里每次请求通过拦截器统一带上Authorization头。token过期时后端返回401前端弹出提示并跳转登录页这个逻辑在每个请求里都要处理所以我封装了一个request工具类统一管理。4.2 支付接入与虚拟支付合规美妆教程平台如果卖付费专栏支付是绕不开的一环。UniApp做支付有个天然优势支付流程和参数在不同端上高度统一但你得清楚微信小程序支付和App支付的差异否则联调时会被参数搞得一头雾水。小程序端支付调起方式是wx.requestPaymentApp端是uni.requestPayment或plus.pay前端参数对齐后调用方式差异不大。核心区别在后端下单和参数维度上小程序支付走JSAPI下单需要传用户的openidApp支付走App下单传的不是openid而是用户的App登录标识。下单成功后的调起参数小程序是timeStamp、nonceStr、package、signType、paySign这些字段App端是partnerid、prepayid、package、noncecstr、timestamp、sign这些字段。简单说不同端的调起参数中package、signType和预支付ID的字段名都是不一样的后端返回给前端时要做区分。我做了一个简单的对比表维度微信小程序支付App内微信支付App内支付宝支付统一下单接口小程序专用JSAPI下单App下单接口支付宝预下单需传用户标识openid无无前端调起APIwx.requestPaymentuni.requestPaymentuni.requestPayment关键参数timeStamp/nonceStr/packagetimestamp/noncestr/partnerid/prepayidorderStr是否需后端参与签名是是是关于虚拟支付我必须给你提个醒。微信小程序对于线上虚拟内容比如付费教程、会员、课程有严格的规范苹果iOS端更是规定虚拟商品必须走苹果IAP支付分成所以如果你在微信小程序里直接卖虚拟课程有被下架的风险。我这边为了合规选择了在小程序端只做内容展示付费环节引导用户到公众号H5或者App内完成支付这样虽然损失了一部分转化率但避免了虚拟支付违规导致的封禁风险。做这类内容产品千万别为了省事踩平台红线。4.3 分享裂变与自定义分享卡片内容型产品最划算的获客方式就是老用户分享。UniApp里实现分享很简单但想做好看、转化高需要花点心思。页面开启分享功能只需要在onShareAppMessage里配置onShareAppMessage() { return { title: this.tutorial.title, path: /pages/detail/detail?id this.tutorial.id, imageUrl: this.tutorial.cover } }关键点在path参数。分享出去的链接最好带上来源标识比如fromUserId这样新用户通过分享进来时系统可以知道是谁带来的流量便于做奖励机制。美妆教程平台里我做了邀请有礼用户分享教程给好友好友注册后双方都能获得积分积分可以兑换付费课程。这个机制上线后分享率提升了30%左右相当于免费获客。分享朋友圈是onShareTimeline这个接口小程序端从基础库2.11.3开始支持UniApp也做了封装。要注意的是分享朋友圈的链接不支持自定义页面路径只能分享当前页所以分享朋友圈的海报和分享好友的卡片要分开设计。4.4 扫码与H5跳转小程序的实现美妆教程平台里扫码功能的落地场景是美妆门店的物料上印了二维码用户扫一下直接进入对应教程或者门店专属优惠页。这个用uni.scanCode就能搞定。uni.scanCode({ scanType: [qrCode], success: (res) { const result res.result if (result.startsWith(https://yourdomain.com/tutorial/)) { const tutorialId result.split(/).pop() uni.navigateTo({ url: /pages/detail/detail?id tutorialId }) } else { uni.showToast({ title: 无效的二维码, icon: none }) } } })这里要说明一下微信小程序自带扫一扫能力但如果你希望扫码后用户直接打开小程序一般有两种方案一种是用微信的URL Link在H5页面里生成一个链接用户点击后直接跳转到小程序指定页面另一种是小程序码把带参的小程序码印刷到物料上用户用微信扫一扫直接打开。如果你同时做了H5端还有一个常见需求是H5页面里点击按钮跳转到小程序。这个功能在微信内可以用微信开放标签 但非微信环境就只能用URL Link/URL Scheme。UniApp里我封了一个方法根据当前环境判断跳转方式const jumpToMiniProgram (path) { // #ifdef H5 const ua navigator.userAgent.toLowerCase() if (ua.match(/MicroMessenger/i)) { // 微信内用开放标签跳转 } else { location.href https://wxaurl.cn/ generateUrlLink(path) } // #endif }另外H5端还有一个容易忽略的需求就是页面的重新加载。UniApp的H5端页面跳转跟传统网页不一样如果只是刷新当前页面用uni.reLaunch会重新走一遍路由成本偏高如果是切换tab页用uni.switchTab如果想回到上一页并刷新用uni.navigateBack配合onShow重新拉数据。4.5 WebView页面返回处理美妆教程平台里有一部分内容是运营后台编辑的长文我把它们放在了独立H5站点上小程序内通过web-view组件加载。这种混合架构有一个绕不过去的坑web-view页面的返回行为和普通小程序页面完全不同。普通页面左上角有默认的返回箭头点击后触发navigateBackweb-view页面没有这个箭头iOS上用户在H5内往下划经常直接退出小程序体验非常糟糕。我最后采用的方案是H5页面在需要返回时通过微信小程序SDK向小程序发送消息小程序收到消息后手动navigateBack。// H5 页面内 wx.miniProgram.postMessage({ data: { type: back } })// 小程序页面内 web-view :srch5Url messageonWebviewMessage/web-view onWebviewMessage(e) { const data e.detail.data[0] if (data.type back) { uni.navigateBack() } }需要注意web-view的message事件触发时机是特定时机而不是每一条消息都能实时收到比如分享、组件销毁、页面返回时才会触发。所以更稳妥的做法是H5页面把返回动作分成两步先弹一个中间页用URL变化告诉小程序小程序通过onShow检测到URL变化再决定是否返回。这个方案复杂一些但确实能解决消息延迟的问题。如果只是承接简单的H5内容我建议直接隐藏web-view的返回按钮让用户在H5内完成浏览后再通过底部固定按钮返回小程序。5. 常见问题与排查技巧实录5.1 网络请求类问题做小程序根本避不开网络请求的坑。这里整理了实际项目中遇到的高频问题。第一个是WebSocket连接报错微信小程序 handshake failed due to invalid upgrade header: null。这个报错常见于连接wss协议时服务端返回的握手响应头里没有合法的Upgrade标识。排查方向有三个第一确认你用的确实是wss而不是ws小程序线上环境强制要求wss第二检查Nginx的WebSocket代理配置需要显式设置Upgrade和Connection头第三确认服务端是否正常返回了101状态码有些云服务网关默认会过滤掉Upgrade头导致握手失败。第二个是iOS端网络请求失败率高。小程序在iOS上的请求失败率明显高于安卓这是很多人的共同感受。常见原因包括TLS版本过低、证书链不完整、IPv6网络适配问题。我最终的解决方案是域名证书统一换成Lets Encrypt或腾讯云免费证书并开启OCSP后端强制TLS1.2以上前端给uni.request加超时和重试机制超时时间设为10秒非4xx错误自动重试一次。第三个是请求数据乱码问题通常发生在新接口或者迁移服务器之后。记住统一用UTF-8编码后端返回时显式设置Content-Type: application/json;charsetUTF-8前端解析时不要用默认ASCII。5.2 界面样式与适配问题美妆教程平台对UI要求很高但小程序在样式适配上的坑真不少。顶部导航栏高度是最常见的问题。如果你用了自定义导航栏不同机型上的状态栏高度和胶囊按钮位置都不一样用我上面提供的getNavBarHeight方法可以动态获取但要注意在onResize和onShow里重新计算否则横竖屏切换后布局会乱。有些安卓机状态栏高度还会跟随字体大小变化建议在页面onLoad时取一次值存起来避免反复调用。底部tabBar闪烁的问题前面已经说过再补充一个容易忽略的原因页面里如果用了van-tabbar、u-tabbar这类第三方组件它们默认有过渡动画和原生tabBar切换动画叠加后就会出现闪烁。解决方法是给组件加:transitionfalse或自定义动画时长。另一个高频问题是扫码不清晰。uni.scanCode在暗光、手持不稳、距离不对时识别率会明显下降。可以在扫码前先引导用户打开闪光灯、对准二维码保持20cm距离如果还是不行微信原生扫码的识别能力是固定的可以考虑接第三方扫码插件比如美洽的扫码SDK。5.3 数据缓存与本地存储问题小程序本地存储有10MB上限这个限制在美妆教程场景下很容易触达因为收藏列表和观看记录里存了很多封面图和视频URL。我的方案是本地存储只存ID和必要字段完整数据通过接口动态拉取。收藏列表存的是[{tutorialId:123, cover:..., title:...}]这样的精简结构不存视频URL。观看记录用时间戳排序超过30天的自动清理。如果你要存结构化数据且数据量较大Uniapp端可以用plus.sqlite但请注意sqlite在微信小程序端并不支持小程序端还是用setStorageSync或者云开发数据库更合理。用户肤质测试这个需求我用了radio-group单选组件数据存的是option的value而不是index这样后端扩展选项时不用改历史数据。5.4 打包、发布与审核避坑UniApp项目的打包流程分三种HBuilderX云打包、本地打包、离线打包。个人开发者或者小团队用云打包就够了登录HBuilderX账号后选择云打包会排队几分钟生成apk或者ipa。如果你要用uts插件或者接第三方原生SDK那就得走离线打包用Android Studio或Xcode工程打包这里不再展开。上架安卓应用市场时国内各个应用市场要求各有不同软著、隐私协议、权限说明、备案号这些材料缺一不可。微信小程序审核需要注意的核心点就一个内容安全。美妆教程平台里的图文、评论、用户昵称都需要接内容安全接口可以接入微信官方的内容安全API也可以接第三方服务否则一个违规内容就能让整个小程序下架。审核被驳回的高频原因还有虚拟支付违规、诱导分享、没有隐私政策弹窗、用户协议不完整。这些在提审前一定要自查一遍尤其要检查是否有诱导分享词、是否有隐藏的付费引导、是否明确公示了用户个人信息保护规则。我这边第一次提审就被打了个回票原因是付费专栏页面暗示了用户可“购买”但实际支付流程还没接完整属于虚拟支付违规后来把页面改成“订阅提醒”才过审。结尾想说的话做完美妆教程平台这个项目我最大的感受是技术本身并没有想象中那么难UniApp确实把跨端的工程量压缩了一大截真正考验人的是对微信小程序平台规则的理解。虚拟支付怎么避开红线、内容安全怎么做到位、分享裂变怎么合规地做这些隐藏约束才是决定项目能走多远的关键。最后分享一个小技巧也算是我踩过坑之后总结的像美妆教程这类图文视频混排的内容平台千万别在详情页一次性把所有视频地址都加载进来用户可能只看了前两个步骤就划走了后面的视频地址白白消耗流量。我把视频地址改成懒加载用户滚动到对应步骤时才加载对应视频源详情页首屏加载速度快了一倍用户反馈体验提升明显。这个思路在内容型小程序里基本通用供你参考。