ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于UniApp的美妆教程微信小程序开发实战与踩坑指南

基于UniApp的美妆教程微信小程序开发实战与踩坑指南 一直有朋友问我美妆类内容平台该怎么从零做尤其是一套代码要同时覆盖微信小程序和App的场景到底怎么选型。我自己做过几个美妆方向的小程序踩了不少坑也积累了一些比较成熟的做法。今天就把这套“基于微信小程序的美妆教程平台UniApp”完整拆开讲一遍从技术选型、功能设计、核心模块实现到微信平台适配和上架发布的实操细节全都捋清楚。这套方案适合准备入手做美妆教程、美妆社区、以及类似内容型小程序的开发者也适合产品经理和技术负责人拿去做方案参考。1. 项目背景与整体设计思路拆解1.1 美妆教程平台的核心需求定位美妆教程平台本质上是一个垂直领域的内容消费产品。用户进入小程序核心诉求很明确我要看“怎么护肤”“怎么化妆”“某个产品怎么用”要的是高效、直观、能跟着学的教程内容。而平台方的诉求则是内容能持续供给、用户能持续回来、内容能产生互动甚至转化。这就决定了平台的核心功能模块必须有这么几块分类化的教程内容库。护肤、彩妆、发型、美甲这些大类下面再细分比如彩妆还能分成底妆、眼妆、唇妆。内容形式以短视频为主图文为辅。用户互动体系。点赞、收藏、评论、关注这些基础互动行为直接影响内容分发和用户粘性。个人中心与行为沉淀。用户收藏过什么、看过什么、订阅了什么主题这些数据是后续做个性化推荐的基础。消息触达能力。美妆教程有一个特点内容有很强的时效性和场景感比如“换季护肤教程”“秋冬妆容教程”用户希望在有新内容时被通知这就依赖微信订阅消息。基于这些需求技术侧的核心问题就变成用什么样的技术栈才能快速实现、稳定运行同时还能兼顾未来App端、H5端的扩展1.2 为什么选 UniApp 而不是原生开发微信小程序原生开发用 WXML WXSS JS语法体系自成一派UniApp 则是基于 Vue 语法一套代码能同时编译到微信小程序、App、H5 等多个平台。我选 UniApp不是因为原生做不了而是从“内容型产品”的迭代节奏和团队成本两个角度考虑。内容型产品有个特点需求变化快运营活动多版本迭代频繁。如果只做微信小程序原生开发也能行但一旦后面要上抖音小程序、支付宝小程序或者要打包成安卓/iOS App原生那套代码基本全部作废。UniApp 通过条件编译可以在一套代码里针对不同平台做差异化处理理论上是一次开发、多端复用。从人力成本上讲UniApp 用的是 Vue 语法招 Vue 开发者的难度远低于招原生小程序开发者。我自己带过几个项目一个熟悉 Vue 的开发者上手 UniApp基本一周内就能投入业务开发这个学习成本非常低。从生态上看UniApp 的插件市场里有大量现成的组件和模板像美妆平台常见的视频列表、瀑布流、评分组件都有现成方案。项目初期能省掉很多造轮子的时间快速上线验证业务模型。当然UniApp 也不是没有代价。跨端框架在性能上会有一定损耗尤其是在复杂动画和大列表场景下微信小程序特有的一些 API 和组件UniApp 可能封装得不够及时需要自己写条件编译去做原生适配。这些后面我都会讲到对应的解决方案。1.3 整体架构与页面规划美妆教程平台的架构可以分成四层来看展示层、业务层、服务层、数据层。展示层就是用户看到的所有页面。我的规划是首页热门教程推荐、分类页按照美妆品类划分、教程详情页视频/图文播放与互动、个人中心页、登录页、搜索页。另外还有一个管理端用来做内容审核和发布管理端可以单独做成一个UniApp项目也可以只在代码里做角色权限控制。业务层主要负责处理用户行为包括微信登录、收藏/点赞/评论、订阅消息模板的发送逻辑、分享朋友圈/会话的配置。服务层我推荐使用微信云开发或者自建后端服务都可以。如果项目处于早期验证阶段直接用微信云开发能省掉服务器运维的事如果已经有一定用户规模建议用自建后端比如 Node.js 或 Java 服务配合 MySQL 存储业务数据对象存储存视频和图片Redis 做热门榜单和缓存。我这里后续的讲解会兼顾两种方案并指出各自的使用场景。数据层就是内容数据和用户数据。教程内容需要维护封面图、视频地址、教程步骤、标签、适用肤质、所需工具等结构化信息用户数据需要维护openid、昵称头像、收藏记录、浏览记录、订阅记录。页面规划上我建议采用底部TabBar导航四个主入口首页、分类、订阅消息中心、我的。TabBar的优势是用户在不同模块之间的切换成本最低符合内容消费类小程序的使用习惯。{ pages: [ { path: pages/index/index, style: { navigationBarTitleText: 美妆教程 } }, { path: pages/category/category, style: { navigationBarTitleText: 分类 } }, { path: pages/subscribe/subscribe, style: { navigationBarTitleText: 订阅 } }, { path: pages/user/user, style: { navigationBarTitleText: 我的 } }, { path: pages/detail/detail, style: { navigationBarTitleText: 教程详情 } } ], tabBar: { color: #999999, selectedColor: #E08BA6, list: [ { pagePath: pages/index/index, text: 首页 }, { pagePath: pages/category/category, text: 分类 }, { pagePath: pages/subscribe/subscribe, text: 订阅 }, { pagePath: pages/user/user, text: 我的 } ] } }这套页面结构是经过实际运营验证的整体路径非常短用户从进入小程序到看到视频最多不超过3次点击非常符合碎片化消费场景。2. 工程结构与基础环境搭建2.1 开发工具与工程创建开发 UniApp 项目主流工具是 HBuilderX当然也可以用 CLI 方式创建用 VSCode 写代码。我自己的习惯是简单项目直接用 HBuilderX因为内置了小程序模拟器、真机调试、发行打包一站式体验比较顺畅如果项目复杂度较高需要配合完整的工程化工具链ESLint、Prettier、单元测试那我建议用 CLI 方式创建这样前后端在一个仓库里管理会更方便。HBuilderX 创建 UniApp 项目的路径是文件 - 新建 - 项目 - 选择 uni-app 项目模板。创建后项目结构大致如下├── pages // 页面目录 ├── static // 静态资源 ├── components // 公共组件 ├── uni_modules // uni-app 插件模块 ├── utils // 工具函数 ├── api // 接口请求封装 ├── store // 状态管理Vuex/Pinia ├── App.vue // 应用入口文件 ├── main.js // 入口JS ├── manifest.json // 应用配置appid、权限、SDK配置 ├── pages.json // 页面路由、导航栏、TabBar配置 └── uni.scss // 全局样式变量有一件事我建议在项目初期就做完善 utils 和 api 目录。很多新手开发时所有请求都在页面里写后期维护非常痛苦。我在项目里会把所有后端接口统一封装到 api 目录每个页面只调用对应的接口函数这样后端改了接口地址只要维护一处就行。2.2 manifest.json 配置要点manifest.json 是 UniApp 项目的核心配置文件很多人忽略这里等到打包才发现一堆问题。我拆解一下关键配置项。微信小程序平台配置主要是填写小程序 appid。这里有个容易踩的坑如果你只是开发调试不填写微信开发者工具会使用测试号但测试号不支持某些能力比如订阅消息、获取手机号、登录凭证校验。所以我都是创建项目后就先填好真实的 appid避免后面功能联调时返工。{ mp-weixin: { appid: 你的小程序appid, setting: { urlCheck: false, es6: true, minified: true }, usingComponents: true, permission: { scope.userLocation: { desc: 用于推荐附近的美妆门店 } }, requiredPrivateInfos: [] } }这里提醒一下如果小程序里不需要定位权限就不要申请scope.userLocation。微信官方对隐私权限审核比较严格申请了但用不上很容易被驳回而且还会在用户首次打开时弹出授权框非常影响体验。App 端的配置这里就不展开了重点提一下如果你后面要打包安卓和 iOS 的安装包manifest 里需要配置图标、启动图、App SDK 的 appid 等参数。建议在项目初期就把美妆品牌的 Logo 和启动图准备好省得到上架阶段手忙脚乱。2.3 页面路由与分包策略pages.json 里除了配置页面路径还有一个容易被忽视的能力分包加载。微信小程序单个包体积限制是 2MB主包不能超过 2MB。美妆教程平台里面视频封面图多、组件库体积也不小如果不做分包处理很可能在发布时提示“主包体积超限”。我建议的分包策略把管理后台、用户协议/隐私政策、积分商城等功能放到分包里这些功能访问频率低但占据了大量页面和组件代码。主包里只保留首页、教程列表、详情、个人中心这些高频页面。{ subPackages: [ { root: packageAdmin, name: admin, pages: [ { path: pages/audit/audit, style: { navigationBarTitleText: 内容审核 } }, { path: pages/upload/upload, style: { navigationBarTitleText: 教程发布 } } ] } ] }分包配置要注意分包的页面不能直接通过 wx.navigateTo 跳转需要正确写全路径同时分包内的页面不能依赖主包外的自定义组件或 JS 文件否则编译时会报错。我通常是先把工具函数和公共请求封装放主包里分包只放页面和页面私有组件。2.4 状态管理与接口请求封装状态管理在美妆平台里主要管理用户登录态、收藏状态、订阅偏好这些全局信息。UniApp 支持 VuexVue2和 PiniaVue3。新建项目如果是 Vue3 版本直接上 PiniaVue2 项目用 Vuex 也够用。我个人比较推荐 Vue3 Pinia写起来简洁而且自动支持响应式。下面是一个简单的用户状态示例import { defineStore } from pinia export const useUserStore defineStore(user, { state: () ({ token: , userInfo: null }), getters: { isLogin: (state) !!state.token }, actions: { setUserInfo(info) { this.userInfo info }, logout() { this.token this.userInfo null } } })接口请求封装需要注意一个点UniApp 的uni.request和浏览器 axios 不同它默认不会携带 cookie所以登录态的维护需要主动把 token 放到 header 里。我在封装时会在拦截器里统一处理const BASE_URL https://api.example.com export function request(options) { return new Promise((resolve, reject) { uni.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, header: { Content-Type: application/json, Authorization: uni.getStorageSync(token) ? Bearer ${uni.getStorageSync(token)} : }, success: (res) { if (res.statusCode 200) { resolve(res.data) } else if (res.statusCode 401) { uni.navigateTo({ url: /pages/login/login }) reject(res) } else { uni.showToast({ title: res.data.message || 请求失败, icon: none }) reject(res) } }, fail: (err) { uni.showToast({ title: 网络异常, icon: none }) reject(err) } }) }) }这个封装逻辑可以说是整套小程序的基础。后面登录态、收藏、订阅等所有业务接口都共用这一套逻辑。注意 401 的处理很多项目忽略了这个导致用户 token 过期后接口返回报错但页面没有跳转登录体验很糟糕。3. 核心功能模块实现与实操细节3.1 微信登录与 code 换取 token 流程微信小程序的登录机制核心就是 wx.login 获取临时 code然后把 code 发送给后端后端拿着 code 向微信接口换取 openid 和 session_key或云开发环境中的 openid拿到 openid 之后做用户的注册或登录再返回给前端一个自定义登录态 token。后端接口用示例伪代码展示一下我用的是 Node.js 环境const axios require(axios); async function loginHandler(code) { const appid 你的appid; const secret 你的appsecret; const url https://api.weixin.qq.com/sns/jscode2session?appid${appid}secret${secret}js_code${code}grant_typeauthorization_code; const res await axios.get(url); const { openid, session_key } res.data; // 在数据库查找 openid如果不存在则创建新用户 let user await findUserByOpenid(openid); if (!user) { user await createUser({ openid, nickname: 微信用户, avatar: }); } const token generateToken({ openid, userId: user.id }); return { token, user }; }前端 UniApp 里调用 uni.login 拿到 code然后请求后端接口uni.login({ provider: weixin, success: async (loginRes) { const res await request({ url: /auth/login, method: POST, data: { code: loginRes.code } }); uni.setStorageSync(token, res.data.token); uni.setStorageSync(userInfo, res.data.user); } });有一个细节要特别注意真实环境中jscode2session接口需要在小程序的后台配置服务器域名并且在开发调试阶段要勾选开发者工具里的“不校验合法域名”选项。但等到正式上线必须把接口域名加入到小程序的 request 合法域名白名单里并且域名需要支持 HTTPS否则真机上是请求不通的。3.2 教程内容展示与信息流设计美妆教程平台的首页信息流是核心。我推荐用双层流式设计上层是分类标签横向滚动比如“全部”“护肤入门”“进阶彩妆”“拔草避雷”下层是视频卡片瀑布流或单列大卡。单列大卡比较适合视频教程每屏只展示一个视频封面 标题 作者 点赞数用户上滑加载更多。这种模式的好处是视觉聚焦用户不会被杂乱的信息干扰。瀑布流双列适合图文教程和短视频封面能在有限屏幕里展示更多内容提高浏览效率。美妆教程普遍封面图质量较高双列瀑布流在视觉上也会有“逛杂志”的感觉。我建议首页用单列大卡因为首次加载性能压力更小视频自动播放的实现也简单分类页则用双列瀑布流因为分类页用户目的性更加明确希望在最短时间浏览更多内容。列表数据的后端接口设计要考虑分页方式。推荐使用“页码 每页数量”的方式也可以根据滚动位置使用偏移量分页。前端监听 onReachBottom 事件来加载下一页onReachBottom() { if (this.hasMore !this.loading) { this.page 1; this.getTutorialList(); } }加载状态的管理非常关键很多初级开发者没有做“是否正在加载”的判断用户快速滚动时连续触发多次请求导致数据重复或列表错乱。这个loading标志位是信息流稳定性的核心。3.3 视频播放方案选型美妆教程的重头戏是视频。微信小程序里的视频播放有两个主要方案video组件播放网络视频或使用同层渲染能力实现更复杂的效果。如果你用的是云开发视频文件上传到云存储后可以直接获得一个临时链接或永久链接在video组件里引入即可。如果是自建后端视频推荐放在腾讯云 COS 或阿里云 OSS 上开启 CDN 加速。美妆教程的场景下用户对画质的敏感度其实取决于网络环境我的做法是默认播放 720P网络较差时自动切换到 480P这个可以用 video 组件的enable-progress-gesture和自定义清晰度切换实现。关于自动播放要说明的是微信小程序出于用户流量和体验考虑不允许页面加载后自动播放带声音的视频。你可以在 onShow 事件里判断视频是否进入可视范围然后调用视频组件的 play 方法但同时需要使用muted属性开启静音播放。这是目前主流信息流产品的通用做法进入屏幕时静音播放用户点击后才有声音。由于热搜词里有用户提到ios 微信小程序渲染机制特殊uni-datetime-picker 放在 scroll-view 里会有问题我就专门说一下这个点。iOS 小程序对原生组件和同层渲染的支持一直有一些兼容性问题uni-datetime-picker这类弹层组件在 scroll-view 内时可能出现层级被遮挡、组件渲染不完全的情况。解决方法有两个第一种把弹层组件挂在scroll-view外面通过 absolute 定位来跟对应的表单控件对齐。第二种使用v-if控制组件渲染时机仅在点击选择时再渲染选择器组件。根据我实际经验第二种方案在 iOS 上的稳定性更好只是实现上要多写一点触发逻辑。3.4 自定义分享好友与朋友圈美妆教程天然具备社交分享属性用户看到一个“新手必学日常妆”教程顺手就分享给闺蜜了。这部分的体验如果做得顺畅能带来非常大的自然流量。微信小程序的自定义分享需要在页面里定义onShareAppMessage方法export default { onShareAppMessage() { return { title: this.tutorial.title, path: /pages/detail/detail?id${this.tutorial.id}, imageUrl: this.tutorial.cover } } }这里有几个细节容易被忽略path必须写完整路径并且如果目标页面需要参数参数要拼接进去。只写页面路径不带参数用户打开分享卡片时就没有教程ID页面会白屏。imageUrl建议使用 5:4 比例的封面图。微信官方推荐的比例接近这个如果比例差异太大分享卡片图可能会被裁剪封面上的文字会被截断。分享时如果需要附带渠道来源信息可以在 path 里追加inviter用户ID这样方便统计每个分享渠道带来的转化。关于朋友圈分享微信小程序支持通过wx.showShareMenu({ menus: [shareAppMessage, shareTimeline] })开启分享到朋友圈功能但用户只能看到单页模式的分享卡片不能像会话分享那样展示完整卡片封面。这项能力对美妆平台来说依然有用因为朋友圈的传播价值很高。我在项目里的做法是在 App.vue 的 onLaunch 里统一开启分享菜单同时全局配置 onShareAppMessage确保每个页面都能分享。script export default { onLaunch() { wx.showShareMenu({ menus: [shareAppMessage, shareTimeline] }) } } /script3.5 订阅消息与用户召回订阅消息就是用户订阅后平台可以向用户发送一条模板消息。对美妆平台来说订阅消息的使用场景很清晰用户订阅了某个教程系列的主题当该主题有新内容上线时给用户发送一条“您订阅的[秋冬眼妆教程]已更新”的通知。小程序订阅消息分两种一次性订阅消息和长期订阅消息。长期订阅消息目前主要面向政务、医疗、教育这些民生领域普通商业小程序基本申请不到。所以美妆平台只能使用一次性订阅消息。一次订阅的含义是用户每次主动触发订阅动作向用户弹出一个授权框用户允许后你获得一次下发消息的机会。你可以引导用户在访问详情页时授权订阅“更新提醒”也可以引导用户在收藏时同时订阅。前端触发订阅的代码uni.requestSubscribeMessage({ tmplIds: [模板消息ID], success(res) { // res[模板消息ID] accept 表示用户同意 }, fail(err) { // 用户拒绝或不支持 } })后端下发订阅消息调用微信接口subscribeMessage.send需要准备的数据有用户的 openid、模板ID、跳转页面路径、模板里每个字段的取值。{ touser: 用户的openid, template_id: 模板ID, page: pages/detail/detail?id123, data: { thing1: { value: 秋冬眼妆教程 }, time2: { value: 2025-09-20 10:00 } } }注意模板消息每个字段都有长度限制thing类字段最多 20 个字符中文 20 字超了会下发失败。内容标题控制在 20 字以内这个细节在产品文案阶段就要想到否则上线后运营会频繁来抱怨消息发不出去。3.6 评论、收藏与点赞的实时反馈美妆教程平台必须有互动机制。点赞、收藏、评论这三个操作看起来简单但实现起来有几个容易踩的坑。先说点赞和收藏。这两个操作要求高实时性用户点了按钮就要立刻有反馈不能等接口返回后才变亮。我的做法是先本地更新 UI 状态乐观更新再发请求如果接口请求失败再回滚 UI 状态并提示用户。之后要考虑数据一致性。列表页显示的是“点赞总数”如果每次点赞都重新从后端拉总数会造成接口压力大。我采用的方案是后端在点赞接口里返回最新的点赞数前端本地更新展示值而不是重新拉取列表。评论功能要注意两个细节评论内容的敏感词过滤。美妆平台用户评论里经常涉及品牌名、功效词比如“美白”“治愈”有些词在微信审核规则里属于夸大宣传或医疗用语。如果不过滤被用户投诉或触发机审就很麻烦。建议后端接微信官方的内容安全检测接口msgSecCheck在评论提交时做一次内容审核。评论的排序策略。我建议“热评在前最新评论在后”。热评的计算可以简单用点赞数 * 0.7 评论时间权重不至于让老评论永远霸榜也避免新评论完全没人看到。3.7 搜索与标签系统美妆教程平台的内容越来越多搜索能力会变成一个核心竞争力。用户会搜“敏感肌”“平价眼影”“奶油肌底妆”这类关键词。搜索功能不一定要上 Elasticsearch前期用 MySQL 的 LIKE 查询加索引都能撑住几万条内容级别。但如果教程内容超过十万条建议提前规划接入专业搜索服务。标签系统是整个搜索和推荐系统的基石。我建议在上传教程时强制内容创作者填写至少 3 个标签品类标签彩妆/护肤/美甲、适用肤质标签、价位标签平价/中端/高端。教程详情页把这些标签展示出来点击标签可以进入同一个标签的内容列表页。这样实现的目的是让用户以一种低成本的方式找到同类内容。比如用户看了一个“干皮粉底液教程”点击标签“干皮”就看到了所有干皮适用的美妆教程。这种通过标签串联内容的方式是美妆平台最常用的内容组织策略之一。3.8 教程详情页与在线文档预览教程详情页除了视频还要承载图文步骤讲解、产品清单、所需工具等结构化信息。你可以用富文本编辑好图文内容后端存储 HTML 片段前端用 rich-text 组件渲染。有些教程可能用到了 PDF 附件比如完整的护肤清单、产品对比表用户需要在小程序里预览 PDF。微信小程序原生没有提供 pdf 预览能力我推荐两个方案使用web-view加载一个支持 PDF 预览的 H5 页面由服务端生成 PDF 预览地址。使用第三方 PDF 解析插件把 PDF 内容渲染成 Canvas 或转成图片分页展示。第一种方案实现最简单但渲染效果依赖第三方平台第二种方案体验更好但需要引入较重的依赖。我实际项目里用的是第一种因为平台初期 PDF 附件数量很少用 web-view 能快速解决问题。等用户量上来、PDF 内容增多之后我再考虑换成 canvas 渲染方式。4. 微信小程序平台适配与踩坑实录UniApp 跨端开发最考验人的不是业务代码本身而是微信小程序平台的各种“个性”。很多功能在 H5 端跑得好好的一到微信小程序就出问题。我把这几年积累的典型问题和解决方案整理出来。4.1 iOS 渲染机制导致的弹层遮挡问题开头提到过的 uni-datetime-picker 在 scroll-view 里的问题就是典型的 iOS 同层渲染限制。iOS 上小程序的原生组件和普通 DOM 渲染层级处理一直是老大难video、map、canvas 这种原生组件的层级天然高于普通页面元素。解决弹层类组件覆盖问题的通用思路尽量避免在 scroll-view 内直接使用弹层组件把弹层移到页面根节点。使用 cover-view 和 cover-image 来覆盖原生组件但 cover-view 的样式支持有限只能做简单布局。用v-if控制弹层组件的创建和销毁避免渲染时机问题。我之前做过一个“教程筛选弹层”筛选条件里有视频预览封面图弹层在 iOS 上始终被视频遮挡。后来我把弹层改成在page根节点渲染配合z-index拉高问题就解决了。4.2 轮播图安卓黑边问题用户搜索里有人提到“uniapp轮播图安卓有黑边”。这个问题看起来很诡异其实原因很简单轮播图默认是左侧对齐而 Android 端某些浏览器内核会把swiper组件里非全屏宽的图片渲染出黑边尤其当图片和容器宽度不一致时。我的解决方案给 swiper 组件设置明确的宽度和高度或者在图片外层加overflow: hidden把超出部分裁掉。另外最好给封面图设定统一尺寸比如 750rpx 宽、480rpx 高。如果后台传上来的图片比例不固定前端做modeaspectFill处理能有效避免黑边。view classbanner-wrap swiper indicator-dots autoplay circular swiper-item v-foritem in banners :keyitem.id image :srcitem.image modeaspectFill classbanner-img/image /swiper-item /swiper /view.banner-wrap { width: 750rpx; height: 400rpx; overflow: hidden; } .banner-img { width: 750rpx; height: 400rpx; }4.3 web-view 页面返回问题UniApp 中嵌入 web-view 加载 H5 页面返回行为和常规页面不一样。常规页面返回直接调用uni.navigateBack()就行但 web-view 里面是一个完整的 H5 页面栈用户在小程序 shell 里点返回可能直接退出整个 web-view而不是回退到 H5 的上一页。我的解决方案是在 web-view 加载的 H5 页面里通过 JS Bridge 监听浏览器的历史记录。给 H5 页面植入一段返回拦截代码当用户点击返回时先判断history.length如果大于 1调用wx.miniProgram.navigateBack({ delta: 1 })回退 H5 历史如果等于 1再调用wx.miniProgram.navigateBack()退出 web-view 层级。这段逻辑看起来简单但很多项目在混合开发时忽略了用户进入一个多层级的富文本教程页面比如从首页走进文章列表、再进详情点返回直接回到了首页中间层级全丢了体验非常差。4.4 权限弹窗与实时监听热搜词里有一条“uniapp能不能实时监听权限申请框的出现和消失”这在美妆平台里是一个很实际的需求。比如用户上传自定义头像、发布教程时用相机拍照、选择相册图片都需要请求相机或相册权限。开发者希望知道授权弹窗什么时候出现、用户什么时候做出选择以便在页面上做相应引导。这里说明一个事实微信小程序官方目前没有提供权限申请框出现/消失的实时监听 API只能通过uni.getSetting查询用户当前权限状态以及uni.authorize触发授权弹窗后的回调事件来判断用户选择结果。一个可行的方案是在调用相机或相册 API 之前先调用uni.getSetting确认权限状态。如果已经授权直接调用对应 API如果尚未授权调用uni.authorize手动弹出权限框然后在成功回调里继续操作在失败回调里引导用户去wx.openSetting手动打开权限。async function checkPermission(scope) { const setting await uni.getSetting(); if (setting.authSetting[scope]) { return true; } if (!setting.authSetting[scope]) { try { await uni.authorize({ scope }); return true; } catch (e) { uni.showModal({ title: 提示, content: 需要授权才能继续使用该功能, success: (res) { if (res.confirm) uni.openSetting(); } }); return false; } } return false; }华为等安卓手机上这个流程会涉及到麦克风权限问题热搜词里有人问“小米打包App之后为啥没有麦克风权限”这是 UniApp 打包原生 App 时的常见坑。原因是manifest.json 的网络权限勾选要同步到原生清单如果你没在 App 模块权限配置里勾选麦克风权限原生应用就没有声明这个权限用户安装以后自然无法使用录音功能。这里提醒一句小程序端本身不涉及这个问题App 打包才需要检查模块配置。4.5 顶部导航栏高度适配与安全区不同厂商的手机尤其是刘海屏、挖孔屏顶部导航栏高度不一样。UniApp 默认使用小程序的胶囊按钮作为右上角而胶囊按钮与手机状态栏之间的距离在不同机型上并不一致。如果你自定义了导航栏navigationStyle: custom实现一个自定义的返回按钮那么你就需要动态获取状态栏高度。const menuButton uni.getMenuButtonBoundingClientRect(); const systemInfo uni.getSystemInfoSync(); const statusBarHeight systemInfo.statusBarHeight; const navBarHeight menuButton.height (menuButton.top - statusBarHeight) * 2;这段代码是我做自定义导航栏时常用的。拿到statusBarHeight和胶囊按钮的位置信息后就能计算出导航栏的准确高度保证自定义导航栏的内容在 iOS 和安卓、刘海屏和非刘海屏上都能垂直居中。4.6 基础库版本兼容热搜词里有个“基础库版本从哪设置”小程序的基础库版本其实就是微信客户端内置的小程序运行环境版本。开发者可以在微信公众平台的“设置 - 基础库最低版本”里设置一个最低版本只有基础库版本高于等于该版本的用户才能访问你的小程序也可以在微信开发者工具的详情面板看到当前模拟器使用的基础库版本并切换。需要说明的是你没办法强制用户使用某个基础库因为这是由微信客户端版本决定的。你只能通过wx.getSystemInfoSync拿到用户的 SDKVersion然后根据不同的版本去做功能开关。实践中我尽量不直接使用太高版本的基础库 API避免用户微信版本过低导致白屏。如果要用新 API会先用条件判断做降级处理。UniApp 大部分 API 已经做了兼容处理但涉及微信原生能力时还是需要自己判断。5. 打包、发布与开发提效5.1 HBuilderX 发行微信小程序的超详细步骤使用 HBuilderX 发行微信小程序流程如下在 HBuilderX 顶部菜单选择“运行 - 运行到小程序模拟器 - 微信开发者工具”。如果本地电脑安装了微信开发者工具且开启了服务端口HBuilderX 会自动拉起微信开发者工具并打开编译后的项目。如果要正式发布选择“发行 - 小程序-微信”HBuilderX 会执行编译并在项目的 unpackage/dist/build/mp-weixin 目录下生成微信小程序代码。打开微信开发者工具选择“导入项目”目录指向 mp-weixin 目录appid 选择你自己的小程序 appid完成导入。在微信开发者工具里先预览或真机调试确认没有明显问题然后点击“上传”按钮把代码上传到微信公众平台。在微信公众平台后台选择“版本管理 - 开发版本”找到刚上传的版本提交审核。这里面容易忽略的步骤是HBuilderX 在发行前要检查 manifest.json 中是否填写了正确的 appid 和上传证书否则微信开发者工具导入后会提示 appid 不匹配。另外uni.request请求的域名必须先在小程序后台配置 request 合法域名。开发阶段可以在微信开发者工具里勾选“不校验合法域名”但真机预览会强制校验所以正式联调时必须把域名配置好否则真机上所有接口都会请求失败。5.2 离线打包与原生插件UniApp 打包 App 分为云打包和本地离线打包。云打包是直接把代码上传到 DCloud 提供的云端服务器完成打包适合快速测试离线打包是把 UniApp 编译后的资源集成到 Android Studio 或 Xcode 工程中需要自己配置原生工程适合要集成自定义原生插件的场景。关于原生插件如热搜词提到的 uts 插件UniApp 从 3.9 版本开始支持 UTS 方式编写原生插件即用 TypeScript 语法直接调用 Android/iOS 原生 API。如果美妆平台需要在原生的视频处理、美颜滤镜、相册选图等能力上做深度定制或者小程序端无法覆盖某些App独有功能UTS 插件是值得学习的方向。不过我要提醒一句UTS 插件目前的生态成熟度还在上升期遇到兼容性问题时要准备好备选方案。我一般建议能通过 DCloud 插件市场找到现成稳定方案的比如支付、分享、登录优先用现成插件把精力放在业务上。5.3 调试与抓包技巧小程序开发避免不了调试和抓包。微信开发者工具自带的 Network 面板能看到请求和返回但有一些场景需要在手机上真机调试此时推荐老牌抓包工具 Charles 或 Fiddler。手机抓包要做的配置是手机和电脑连接同一个局域网手机代理指向电脑 IP并安装代理证书。微信小程序真机运行后部分接口请求就能在 Charles 里看到明文数据。但这里要注意微信小程序正式环境体验版/正式版要求域名备案、HTTPS 证书有效抓包工具可以解析 HTTPS但前提是手机信任了代理证书。如果用户手机上没装证书是抓不到 HTTPS 请求内容的。热搜词里还有“微信小程序反编译”和“uniapp 逆向”。这里我不展开讲具体原理但想提醒做内容平台的开发者你的小程序代码包是可以在一定程度上被还原的。所以不要把前端代码里放敏感信息比如后端接口的密钥、云开发的私有配置、第三方服务的 appsecret。前端只是展示层核心逻辑和敏感计算必须放在后端。这也是给美妆平台这种注重内容版权的产品的一个安全底线教程视频地址可以加密动态生成但不要直接在代码里写死永久链接。5.4 多端差异与条件编译UniApp 虽然是“一套代码多端运行”但每个平台的差异点是真实存在的。我的做法是勤用条件编译把平台相关代码隔离清楚。条件编译的语法简单以 #ifdef 开头例如“#ifdef MP-WEIXIN”和“#endif”之间的代码只在微信小程序平台生效。// #ifdef MP-WEIXIN console.log(这段只在微信小程序端运行); // #endif视频自动播放策略、分享文案配置、订阅消息能力在不同端的表现都不一致这些都建议通过条件编译优雅处理。不要试图写一套万金油代码解决所有平台问题各个平台的能力边界本来就不一样。5.5 低代码开发与工程化方向热搜词里有“uniapp低代码开发”。目前 DCloud 也提供了页面模板和图形化配置能力例如 uni-admin、uniCloud 等产品让管理后台和云函数的搭建门槛大幅降低。美妆教程平台的管理后台内容上传、审核、数据统计如果用传统方式开发需要单独做一个 Web 系统非常耗时。用 uniCloud uni-admin 可以快速搭建一套后台服务实现教程数据的增删改查和内容审核流程。对于个人开发者或小团队我强烈推荐 UniApp uniCloud 的全栈方案。前端代码、云函数、数据库都在同一个生态里部署简化到极致美妆平台的初期版本一两个人完全能撑起来。等到用户量大了再逐步迁移到自建后端也不迟。6. 常见问题排查速查表最后把开发过程中最常遇到的问题整理成一个速查表。我在做美妆教程平台时遇到的这些问题基本覆盖了新手到中级开发者最容易卡住的节点。问题现象可能原因解决方案真机上请求全部失败开发者工具正常request 合法域名未配置或未勾选不校验域名登录微信公众平台配置域名域名需备案且支持 HTTPS视频播放黑屏但音频正常视频编码格式不支持或封面图比例问题转码为 H.264 AAC 的 MP4 文件封面统一 aspectFill订阅消息发送失败模板字段超长或模板ID不是用户订阅时的那个ID检查字段长度限制确保下发时使用正确的模板ID自定义分享后打开空白页path 参数拼接错误或目标页面不存在检查 path 是否完整参数是否以 ? 开头且编码正确轮播图 Android 黑边图片比例与容器不一致使用 aspectFill 并给容器加 overflow hiddeniOS 弹层被视频遮挡原生组件同层渲染限制弹层移出 scroll-view必要时使用 cover-view下拉刷新跟页面滚动冲突picker 或 swiper 手势冲突改用自定义下拉刷新方案合理控制 touch 事件code 换 token 一直失败appid 和 secret 配置错误或 code 已过期检查 appid 与 secret 是否匹配code 一次性有效注意有效性窗口App 打包后缺少麦克风权限manifest.json 未勾选相关权限在 manifest 的 App 模块权限配置中勾选麦克风分包加载后页面跳转失败路径未使用完整分包路径确保跳转时使用 “/packageAdmin/pages/...” 完整路径内容审核被拒类目、资质或用户协议缺失提前在小程序后台选择正确类目完善隐私保护指引排查问题的核心思路是先分端再定位。同一个问题在 H5 端正常、微信小程序端出错那多半和平台 API 差异有关开发者工具正常、真机出错那和权限、域名、打包配置有关。把问题按客户端环境分开排查范围能缩小一大半。7. 平台运营相关的一些额外建议关于美妆教程平台单纯做完技术还不够和微信平台规则打交道也是日常。有两个地方在开发阶段就要想清楚否则上线后会比较被动。第一个是内容安全。美妆教程天然涉及产品功效描述如果用户上传的教程文案里有“三天美白”“立刻祛斑”这类违反广告法或医疗声称的话被平台审核拦截是小事被投诉下架甚至封号才是大问题。后端务必接入微信的内容安全接口图片也要过imgSecCheck。第二个是用户隐私协议。小程序需要明确告知用户你收集了什么信息、怎么使用信息。尤其在涉及手机号、定位、相册权限时微信后台有专门的隐私协议填写入口前端调用相关接口前也会弹出隐私授权提示。这个要在开发完成前准备好不然审核阶段会被打回。我见过太多项目功能全部开发完结果因为小程序类目和资质问题审核卡了两周。所以项目启动第一周就应该去微信公众平台确认美妆教程类目需要什么资质比如涉及化妆品的需要相关资质文件同时把用户协议、隐私政策文档准备起来。这些工作不和代码开发冲突可以并行推进。8. 关于 Monorepo 的歧义澄清最后想专门聊一下 Monorepo因为这个概念在工程化团队里越来越流行但它和我讲的美妆教程平台项目其实不完全是一回事。Monorepo 是一种代码仓库管理策略把多个项目的代码放在同一个仓库里统一管理配合 pnpm、npm workspaces 等工具做依赖管理。这种策略最适合的场景是你有多个相互依赖的包或服务比如一个数据层包被后台、小程序、客户端等多个应用共用。美妆教程平台如果在早期阶段只有一个前端项目和一个后端项目完全没必要上 Monorepo分开两个仓库反而更简单。只有当项目发展壮大到需要维护多个内部公共包比如美妆算法推荐服务、内容审核服务、多个客户端时才值得引入。过早引入 Monorepo 会增加构建配置和流水线的复杂度对小团队来说反而拖后腿。这里顺便想说一句Mermaid 图表是很多技术文档常用的流程可视化工具但如果你要在公众号、知乎这类平台发布文章Mermaid 的兼容性并不好读者端大概率无法渲染。我写博客时从来不用 Mermaid一律用 markdown 表格、截图或者直接用文字描述流程保证读者在任何平台都能看到完整内容。这也是做技术分享的一个实际经验好用和通用之间优先保通用。回到 UniApp 这个项目本身我最后的技术建议是先把微信小程序端跑通、上线、验证业务再考虑扩展到 App 和其他平台。跨端是未来的一步棋不要一开始就背上跨端包袱把所有平台都搞一遍。微信小程序的美妆内容生态还很大尽早跑起来比追求完美的工程化架构重要得多。
返回列表