ARTICLE DETAIL

资讯详情

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

小程序开发入门:从H5到原生,掌握生命周期、分包与登录

小程序开发入门:从H5到原生,掌握生命周期、分包与登录 这几年来微信、支付宝、百度、字节都陆续做起了自己的小程序平台而“小程序开发”也几乎成了软件外包市场里被问得最多的一块。我经常收到私信上来就问“小程序好学吗”或者“我网页能做得很熟小程序是不是直接写就行”。这种问题背后通常藏着一个很深的误解以为小程序无非就是套了一层壳的 H5。今天我就把这块掰开揉碎从基础概念到工程落地把正规做小程序开发之前必须先搞清楚的事讲一遍。内容适合准备入门的全栈开发者、接外包的独立开发者以及公司内部正准备从零搭建小程序团队的人。1. 先搞懂小程序到底是什么它和 H5 的本质差别在哪里先说结论小程序不是一个网页它是一个运行在宿主App比如微信内部、由宿主提供基础能力的沙箱应用。代码运行时渲染层由 WebView 承担但逻辑层跑在独立的 JavaScript 引擎里两层之间通过底层桥接通道来通信。这种“双线程模型”是理解小程序一切诡异行为的总钥匙。很多人上手小程序时第一反应是“这不就是浏览器环境吗”然后直接在 Page 里写window、document结果页面白屏。因为逻辑层里根本不存在浏览器对象你的页面结构由 WXML 描述样式由 WXSS 提供交互更新也不是直接操作 DOM而是通过setData把数据从逻辑层推送到渲染层。这个推送过程是有代价的每次 setData 都是一次序列化和跨线程通信数据量越大、调用越频繁页面就越卡。所以第一条基础知识就是调整心态放弃“网页思维”。小程序没有 DOM 树给你随便改也没有浏览器那么多现成能力。相机、蓝牙、定位、WiFi、扫码这些硬件相关能力全部要通 wx 开头的 API而且大部分都需要用户授权后才能使用。这和网页里拿到摄像头权限的逻辑完全不同授权时机、失败回调、重新打开授权页的方式都得按小程序的规范来做。另一个容易忽视的差别是包体分发逻辑。小程序不像网页那样部署在服务端所有代码需要先打包上传到平台服务器用户点击时再按平台规则下载到本机运行。普通小程序包体上限是 2MB超过的话就得用到分包机制把首屏不需要的页面拆分到“分包”里按需加载。这一点在选型阶段就要想清楚因为如果你从一开始就决定做几百个页面的“超级应用”又不想认真管理分包那大概率会在提审时被逼回来做技术重构。再说说宿主差异。微信小程序、支付宝小程序、抖音小程序看似 API 名称都差不多但底层能力和审核规则完全不一样。比如微信有wx.login形成的完整账号体系支付宝有自己的my.getAuthCode如果你计划多端投放要么分别适配要么直接用 uni-app、Taro 这类跨端框架写一套代码让框架做底层翻译。这里没有绝对的标准答案但有一条经验如果团队里没有原生小程序经验丰富的人只是冲着“多端”去选跨端框架后面遇到疑难杂症基本会卡死因为框架帮你解决的是 80% 的常规需求剩下 20% 的边界情况你必须能读懂各平台原生规范去“降级处理”。2. 技术选型和工程结构原生、uni-app、Taro 怎么选才不后悔很多教程上来就让你装一个脚手架开始写页面但我建议你先花半天做技术选型。选错技术栈的代价会在项目第三个月开始成倍放大特别是当你要接蓝牙、地图、直播这类重度原生依赖的功能时。目前主流的小程序开发方案有三条路原生小程序、uni-app、Taro。我做了一个简单对比可以帮你快速判断维度原生微信小程序uni-appTaro开发语言WXML/WXSS/JSVue 语法React 语法多端支持仅微信微信/支付宝/抖音/H5/App等微信/H5/React Native 等上手的认知负担需要理解新的生命周期懂 Vue 几乎无缝懂 React 需要适应 Taro 状态管理复杂原生能力最直接有完整官方文档部分能力需要写条件编译部分能力需要依赖插件踩坑维修成本低中等框架自身也是坑中等大型项目需专门管理编译配置典型适用场景仅做微信端、重交互项目多端快速交付、外包项目多端且团队本身就是 React 技术栈以最常见的uniapp 微信小程序打包 source size 2612kb exceed max limit 2mb这个报错为例。很多人用 uni-app 的 HBuilderX 一键打包发布时突然被通知代码包超过 2MB第一反应是删页面、压图片折腾半天还是超。实际上 uni-app 打包的时候会把 Vue 运行时、公共组件、所有页面路由全部塞进一个包里如果不做分包超过 2MB 是很正常的事。正确做法是在pages.json里开启subPackages分包配置把非首屏模块拆出来。这个操作在原生小程序里同样恶心但原生因为页面结构更轻问题出现得晚得多。再说工程结构。不管用什么框架最终编译出来的微信小程序都是这样一套东西project.config.json // 项目配置appid、编译设置等 app.js // 小程序入口逻辑 app.json // 全局配置 app.wxss // 全局样式 pages/ ├── index/ │ ├── index.wxml │ ├── index.wxss │ ├── index.js │ └── index.json └── list/ ├── list.wxml ├── list.wxss ├── list.js └── list.jsonapp.json里有两个关键字段最容易出错pages数组的第一项就是首页window用来配置全局导航栏样式。很多新人把页面文件写好了忘了注册到pages里编译直接报“page not found”这类低级错误几乎每天都有人问。另外每个页面的.json文件不是摆设页面自己的标题、导航栏背景色、是否下拉刷新都写在这里全局配置与页面配置是覆盖关系。关于 HBuilderX 还是微信开发者工具我的建议是你要是用 uni-app就老老实实用 HBuilderX 编译到微信小程序运行调试别真在微信开发者工具里改代码。因为 uni-app 会把代码编译成中间产物微信开发者工具里看到的那些 js 文件是转译后的你改了也不会生效回头一刷新全白搭。遇到报错优先去源码层排查再回到微信开发者工具看堆栈信息。还有一点需要特别注意跨端框架并不能帮你屏蔽所有平台差异。比如 Taro 写多端时要注意高德小程序和微信小程序的差异uni-app 写地图组件时也要处理每个平台的图层规则。我的习惯是同一个需求在微信端能达到 80 分就够了先把业务跑通再回头优化其他端。想一开始全端统一完美大概率全端都做不好。3. 生命周期与页面栈那些诡异的 bug十有八九出在这里小程序的生命周期一点都不难但它是无数线上事故的源头。很多新手只认识onLoad觉得页面加载完就算万事大吉结果遇到“页面堆栈混乱”“返回上一页后数据没刷新”“切后台再回来白屏”这类问题根本无从下手。先说 App 级别。小程序冷启动时依次执行onLaunch、onShow切入后台时执行onHide再回来时又会执行onShow。这里第一个坑全局变量最好别只放在app.js里裸声明因为小程序的全局变量很容易被系统回收在低端安卓机上切个后台回来全局数据可能就没了。稳的做法是把关键数据同步到本地存储wx.setStorageSync启动时再读回来。再说 Page 生命周期。常见顺序是onLoad - onShow - onReady - onHide - onUnload。前后台切换时页面会触发onShow/onHide而不是重新加载。我做外卖项目时经常碰到那种需求用户从商品页点开支付支付完回到小程序页面要自动刷新订单状态。不少新人把这个逻辑写进onLoad结果回到页面时根本没触发正确做法是放在onShow里并且要用一个本地状态对比避免每次切后台回来都全量刷新。热搜里有一条“微信小程序如何监听用户离开小程序”答案其实就是监听App.onHide或者页面onHide。但有一点很关键用户从小程序跳转去打开 H5、打开客服、甚至调起支付页面都会触发这个隐藏事件所以你并不能把它等同于“用户退出小程序”。想让用户真正离开后进入某个流程还得结合具体业务场景比如在onHide里记录时间戳下一次onShow时判断是否超过阈值。页面栈也是基础中的基础。用wx.navigateTo跳转页面时新页面入栈用wx.redirectTo时当前页面被替换用wx.navigateBack返回时携带delta决定返回几层。页面栈深度上限是十层超出之后navigateTo会直接失败。很多大促页为了引导用户不断进入子页面连返回键都懒得设计最后莫名其妙跳转失败其实就是栈满。正确思路是有层级太深的场景改用wx.reLaunch关闭所有页面重新打开目标页。还能看到像“加载更多”这样的经典页面逻辑这里有两个基础知识一是滚动翻页要用onReachBottom它由页面滚动到底部触发如果页面高度不够撑满视口它根本不会触发。二是做列表时要避免整页 setData。给每条列表项单独设置一个组件或者用“分包数据推送”的方式一次传一屏的量而不是每次把整个数组重新传一遍。评论区经常有人说“我加载五十条数据页面就卡成狗”原因基本就在这。4. 组件、样式和常见坑导航栏、swiper、rpx每一个都能让你加班组件和小程序基础库的覆盖率现在已经相当高了但有些表面看起来“很基础”的知识点实现起来却全是刺。这里挑几个高频问题讲讲。第一个是顶部导航栏高度。很多人做自定义导航栏结果标题在刘海屏上顶到状态栏切到安卓又偏下。原因是导航栏高度不是一个固定值。状态栏高度可以通过系统信息里的statusBarHeight拿到右上角胶囊按钮的位置和尺寸可以通过wx.getMenuButtonBoundingClientRect拿到。两者相加就是你自定义导航栏应该撑起的高度。胶囊底部会留一段间距所以习惯上把导航栏高度算成const menu wx.getMenuButtonBoundingClientRect(); const navBarHeight (menu.top - statusBarHeight) * 2 menu.height;这个公式的核心逻辑是胶囊按钮相对状态栏的偏移量乘以二再加胶囊本身高度得到的区域刚好能容纳一个和胶囊垂直居中的标题栏。用这个高度去布局大部分机型的视觉效果都比较正常。实测下来老安卓机的menu.bottom可能跟计算值有偏差但整体验收是能过的。第二个是swiper图片空白。热搜词里那条“小程序 swiper 图片尺寸导致会有空白”我太有共鸣了。新手的写法通常是swiper autoplay swiper-item wx:for{{banners}} image src{{item}} modewidthFix/image /swiper-item /swiper问题在于swiper的默认高度是 150px如果你给图片设widthFix图片高度可能会撑开但swiper-item的容器高度没有跟着撑开于是下面留白。而且swiper只有在数据层更新后才会重新计算高度直接 CSS 自适应是不会生效的。正确做法不要指望轮播图自动适应图片高度而是给swiper设一个固定高度按设计稿的宽高比算出来或者用官方推荐的rect模式让图片裁剪。最省事的是后端返回 banner 图时带一个宽高比字段前端按照比例计算容器高度这样无论什么屏幕都不会空白。第三个是动态设置标题。小程序页面标题默认是.json里的navigationBarTitleText但很多场景需要接口返回后再设置比如做一个公告详情页标题就是公告标题。这时候你需要用wx.setNavigationBarTitle({ title })但必须在页面onReady之后调用才有效某些基础库版本在onLoad里调可能无效。如果你用 uni-app对应的 API 是uni.setNavigationBarTitle逻辑一样时序问题也一样。还要说一下单位rpx。小程序规定屏幕宽度为 750rpx不管真机宽度是 320px 还是 390px750rpx 永远等于满屏宽。所以设计稿里 750 宽的图在代码里直接用 750rpx 就能还原。这个单位的坑在于边框和圆角1px在部分设备上比1rpx清晰但如果你全站用 rpx 追求统一边框会出现粗细不均。我通常的做法是布局用 rpx边框和阴影用 px必要时用media做小范围适配。其他组件也有各自的讲究。比如单选框原生radio的样式奇丑无比真正常用是radio-group配合自定义样式蓝牙定位需要先wx.openBluetoothAdapter再做设备扫描而且离开页面要closeBLEConnection否则会一直占用蓝牙通道天地图接入小程序时要走 web-view 或者换成对应小程序地图插件不能直接用网页版的 JS SDK。5. 网络请求和调试抓包别等用户骂了才学会查接口小程序开发里最让人抓狂的往往不是业务逻辑而是“真机正常模拟器挂”“线上报错本地无法复现”。这种情况下一套靠谱的抓包手段比任何 debug 工具都重要。先理清小程序网络的几个硬规则请求地址必须走 HTTPS而且要在小程序后台把域名配置到“request 合法域名”里否则真机上直接报错。开发时可以在“详情-本地设置”勾选“不校验合法域名”但发布前必须配置。如果不加域名校验用户手机上打开开发版会白屏或所有请求被拦截。如果你的项目用了 Charles 抓包微信小程序步骤基本都是固定的电脑端设好 HTTP 代理手机连同一个局域网后把代理指过来然后给手机安装并信任 Charles 的 HTTPS 证书。针对微信小程序抓包有个容易忽略的点微信小程序在安卓 7 以上默认不信任用户 CA 证书即使你安装了证书也可能抓不到 HTTPS 流量。这时可以给 Charles 开启 SSL Proxying并加入微信小程序的域名如果还不行就要专门去研究安卓系统证书安装的方式。苹果相对简单安装描述文件后在 设置-通用-关于本机-证书信任设置 里打开完全信任即可。PC 端轻量方案还有 ProxyPin原理大同小异。抓包能解决什么问题举一个真实案例。用户反馈列表页偶尔能拉到数据、偶尔空白。打开 Charles 一看发现接口返回的 JSON 里list字段有时是数组、有时是{ items: [] }后端把两种结构混着返回了。前端就算写了容错逻辑也会因为“解析成功但 list 为空”而没有报错只有抓包才能快速定位是数据格式不一致。再比如小程序签名校验。很多团队的接口会要求前端传一个sign由时间戳、参数值按照约定算法生成后端验签。抓到请求后就能核对签名算法定位是时间戳问题、参数顺序问题还是编码问题。有一点要提醒抓包能看到的流量其实是加密后的内容粒度小程序本身的 setData 数据在逻辑层内部抓包是看不到的你能看到的只有网络请求的数据和部分 WebView 的访问情况。所以抓包主要用来做接口联调、请求头校验、排查和后端的数据不一致。调试小程序还有一个冷门但好用的思路直接用微信开发者工具自带的“调试器”面板去跑wx.request的返回放在success回调里console.log。不过真机上的性能问题比如渲染慢、加载卡是模拟器复现不了的这时候就需要真机调试加抓包组合拳。6. 登录、用户体系和前后端会话code 换 openid 的完整流程不能搞反小程序的登录跟传统账号密码登录差别很大。几乎所有平台的小程序都没有“密码”概念以微信为例核心流程是前端调用wx.login()拿到临时登录凭证code。前端把code通过wx.request发给自己的后端。后端拿着code、小程序的appid和appsecret调微信接口jscode2session。微信返回openid用户唯一标识、session_key会话密钥用于解密敏感数据以及unionid如果绑定了开放平台账号。这个流程里最容易犯的错是你以为拿到了openid就能当用户 ID 用实际上openid是同一个小程序下唯一的不同小程序对同一个用户来说openid是不同的。如果你想做公众号、小程序、App 之间的用户打通需要用unionid来关联。所以涉及到“网页端同步小程序微信登录”往往会引入开放平台把多个应用绑定到同一个 UnionID 体系下。还有一个常见误区网上很多老代码让你直接调wx.getUserInfo拿头像昵称但微信已经调整了政策现在必须先引导用户点击授权按钮再调用wx.getUserProfile基础库 2.10.4 之后版本来获取头像昵称。而且这个接口现在也改了很多很多项目干脆放弃头像昵称授权直接让用户用微信提供的“头像昵称填写能力”快速选择。session_key是个好东西但它只在后端使用绝不能下发到前端。微信小程序从 2018 年开始就收紧了session_key的安全任何试图把session_key传到前端的行为都等于把用户数据的大门打开。拿手机号来说现在小程序通常用button open-typegetPhoneNumber获取手机号接口返回的是加密的code后端同样用code去微信接口换取手机号信息整个链路中心思想就是临时凭证只在后端和微信之间流转前端只负责把凭证传给后端。再补充一个反直觉的知识点小程序的wx.login可以在任何时候触发但code的有效期很短大约五分钟并且一次只能用一次。登录态维护需要自己设计后端在拿到openid和session_key后生成自己的token返回给前端前端把token存起来每次请求戴着这个token跟自家后端会话。而不是每次都wx.login拿新 code 换取用户信息那样会让后端压力巨大安全性也差。如果你要做一个社区团购、酒馆、陪玩类的小程序用户的登录态设计基本大同小异。但这类项目往往还涉及支付。小程序的微信支付要走wx.requestPayment支付参数不能在前端生成必须由后端下单后返回paySign等参数这跟登录体系的“凭证不过前端”原则是一以贯之的。7. 性能、体积与硬件能力2MB 之外的隐形天花板刚才说过主包加分包的整体体积限制大约是 20MB但每个包不能超过 2MB不同平台有细微差别。这是悬在每个开发者头上的达摩克利斯之剑。很多人第一次发布踩到source size 2612kb exceed max limit 2mb的时候都会苦着脸去删页面。正确的做法不是删页面而是管理资源。图片、字体、视频这些静态资源都应该放 CDN代码包只留逻辑代码和静态的小图标。如果你用 uni-app 打包记得把运行时不必要的内容剔除掉HBuilderX的“重新编译”有时候会带上大量 dev 模式代码发布时一定要选择“发行-小程序-微信”而不是“运行”运行模式产出的是开发包体积天然偏大。分包加载的好处不只是突破体积限制。用户进入小程序会先下载主包如果主包里没有其他分包的代码首屏启动速度会快很多。常见的分包策略是首页、核心列表页、结算支付流程放进主包直播、社区、活动页全部拆出去。比如做一个“校园食堂订餐”小程序菜单、购物车、结算在主包历史订单、积分商城放分包第一个包可能只有五六百 KB用户秒开。再说 threejs 在小程序里的性能问题。H5 用 three.js 开 3D 场景可能很流畅但小程序里的 WebView 性能和 GPU 能力都不如现代浏览器特别是屏幕内做大量纹理贴图、频繁更新渲染目标时帧率会断崖式下跌。我见过一个项目在小程序里塞了个 3D 看房功能从加载到用户滑动切换场景iPhone 上勉强能玩安卓中端机直接白屏。这种场景的正确做法要么是降级成图片序列帧要么是在服务端预渲染成视频前端用 video 组件播放。视频流播放也比 web 体验更稳小程序 video 组件是原生组件可以避开 WebView 合成的一些底层坑。蓝牙定位也是被低估的性能杀手。wx.openBluetoothAdapter后蓝牙模块会一直开启如果你忘记关闭其他蓝牙设备会持续收到你的广播包耗电特别严重。更关键的是小程序的蓝牙 API 是异步事件驱动的状态回调非常容易漏地方一变就莫名其妙断连。我的建议是做硬件能力之前先做一层封装把状态机收拢成“连接中-已连接-断开-重试”之类几个状态再在这套状态上写业务逻辑否则你会被各种回调地狱搞疯。另外有一个关于 setData 的性能知识我每次都要强调setData 并不是“设置数据”那么简单它要经历逻辑层到渲染层的完整传输。传输的数据量每增加 1KB性能就可能呈指数级下降。正确的做法是使用“数据路径”来更新比如this.setData({ list[0].name: xxx })而不是整个 list 替换。对于轮播图、列表这类高频更新场景能局部更新就局部更新能合并缓存就合并缓存。8. 上线审核与合规最容易被打回重做的一环前面所有技术都打磨好了最后等待着你的是一道关卡平台审核。很多团队把精力放在技术调研上却忽略了各个平台的审核规则导致上线前被拒一周甚至反复修改一个月。以微信小程序为例平台对类目有严格划分。你要做内容付费就要确认自己有没有相关资质做社区团购往往要提供食品经营许可等证明做陪玩、社交类小程序可能会被要求提供《增值电信业务经营许可证》或相关文网文资质而涉及“深度合成”比如美颜滤镜、AI换脸、智能配音则需要在小程序后台单独申请深度合成类目并且在用户协议里写明数据用途。这些类目和资质文件前置条件不满足代码写得再好也上不了线。经验是立项当天就去后台申请类目不要等开发完了再去申请因为有些资质审批周期极长可能一两周都下不来。提审前的自查清单通常包括用户隐私保护指引是否完整、是否声明了收集的信息类型和用途登录按钮是否有明确的用户授权说明有没有放置客服电话或反馈入口是否存在诱导分享或强制关注虚拟支付是否合规尤其是 iOS 端不允许虚拟支付如果你做知识付费、会员订阅要么去掉虚拟支付要么走微信公众号的机制。我见过太多开发者连基础版的小程序都要在审核里被挑隐私条款问题这东西没有捷径只能一条条对照。体验版的分发也是上线前的必经一步。微信开发者工具里点“上传”到小程序后台设为体验版然后就可以把体验版二维码发给同事收集试用反馈。注意体验版和使用版是两个入口权限也可以设置白名单适合内部测试。如果在苹果手机上需要防截屏那类需求一般是企业内部的管控需求微信基础库不一定支持通常要在 App 层面做或者用业务逻辑兜底比如敏感页面加水印、切换页面清空截图数据。小程序本身没有“禁止用户截屏”的系统级 API。上线之后也别以为就完事了。各个平台都要求你定期更新版本、维护内容安全。比如社区团购里如果有用户评论平台会要求你用内容安全 API 做文本检测否则被举报会被强制下架。类似这样的合规要求会在整个项目生命周期里不断出现。最后我个人的一点体会做小程序技术和业务能力当然重要但真正决定项目能走多远的往往是对平台规则的尊重和提前规划。代码里加一个兼容性判断后台提前申请一个类目上线前多读两遍隐私协议模板这些事看着小却能救你于水火之中。你也别指望一口气把所有 API 背完不如先动手做一个小项目把生命周期、登录、请求、打包、审核这条路完整走一遍。走通一次之后其他平台的小程序基本上就只是换皮和换 API 的问题了。
返回列表