Skip to content

小程序生态:原生与跨端

小程序是运行在「超级 App」宿主里的轻量应用:入口在微信 / 支付宝 / 抖音内,不需要安装,但代码与能力都受宿主平台约束。本页梳理小程序生态的两条技术路线——写原生小程序、或用 uni-app / Taro 做跨端编译,以及登录、发布与合规这些绕不开的工程问题。

小程序是什么:被托管的应用

小程序不是普通网页,也不是普通 App,它的特殊之处在于宿主托管

维度传统 H5原生 App小程序
运行载体浏览器操作系统宿主 App(微信 / 支付宝 / 抖音等)内的运行时
获取方式URL 打开应用商店下载扫码 / 搜索 / 聊天分享卡片
安装需下载安装无需安装(按需下载代码包)
能力来源浏览器 API系统 SDK宿主通过 JSAPI 桥暴露的能力(受限)
发布无审核(自有域)应用商店审核平台审核 + 平台规范约束
代码分发服务器应用包平台托管的分包代码(有体积上限)

关键推论:

  • 能力是平台给多少用多少:支付、登录、分享、订阅消息都走宿主封装的 API,且平台可以随时收紧(历史上头像昵称、手机号、隐私接口都改过规则);
  • 生态价值大于技术价值:小程序通常不是技术选型,而是「要出现在微信 / 支付宝生态里获客」的业务决策,因此遵守平台规范是硬约束;
  • 与原生 App 是互补关系:低频、获客、裂变场景适合小程序;高频、重体验、深度系统能力场景仍是 App 主战场。

选型坐标可对照技术全景与选型中的客户端版图;「是否做小程序」的判断应来自产品渠道而非技术偏好。

原生小程序运行模型:双线程 + 数据驱动

以微信为例(其余平台同构异名),小程序不是「一个 WebView 里的页面」:

  • 逻辑层:运行在 JS 引擎(无 window / document),执行业务逻辑、管理页面数据;
  • 视图层:渲染 WXML/WXSS,负责显示;
  • 两层之间用 setData 把逻辑层数据序列化后传到视图层做 diff 更新,类似一个带桥的响应式系统。

这带来三条铁律:

  1. 页面没有 DOM:不能 getElementById、不能直接操作节点,一切更新靠 setData 或组件的属性绑定;
  2. setData 有成本:数据量大、频率高会明显卡顿,甚至超出单次传输上限(约 1MB,超限直接失败);
  3. 天生单向:视图层事件只能通过事件回调把值带回逻辑层。

工程骨架

project/
├── app.js            # 全局逻辑:App() 注册、onLaunch 等
├── app.json          # 全局配置:pages、window、tabBar、分包等
├── app.wxss          # 全局样式
└── pages/
    └── index/
        ├── index.js    # Page() 注册:data / 生命周期 / 事件
        ├── index.json  # 页面配置(页面级 window 与组件声明)
        ├── index.wxml   # 结构:受控标签集
        └── index.wxss   # 样式:支持 rpx 等单位

app.jsonpages 数组的第一项是首页,每个页面必须登记:

json
{
  "pages": ["pages/index/index", "pages/detail/detail"],
  "window": {
    "navigationBarTitleText": "示例",
    "navigationBarBackgroundColor": "#ffffff"
  },
  "tabBar": {
    "list": [
      { "pagePath": "pages/index/index", "text": "首页" },
      { "pagePath": "pages/detail/detail", "text": "详情" }
    ]
  }
}

页面生命周期

生命周期时机典型用途
onLoad(options)页面创建(带 query 参数)读取页面参数、初始化
onShow页面每次显示刷新数据、埋点
onReady首次渲染完成初始化 canvas / 组件
onHide页面隐藏暂停耗时任务
onUnload页面销毁释放资源
onPullDownRefresh / onReachBottom下拉刷新 / 触底刷新、分页加载

组件(自定义组件)生命周期走 lifetimesattached / ready / detached 等),逻辑不同:页面强调「进出一屏」,组件强调「挂载到视图树」。

WXML / WXSS 要点

  • 标签受控:常用 view / text / image / button / input / scroll-view / swiper / rich-text 等,不是 HTML 全集(没有 div / span);
  • 尺寸单位 rpx:设计稿宽度对应 750rpx,自动按屏宽缩放,免去多机型换算;
  • 模板逻辑能力弱:想放个「计算后的值」要提前算好放进 data,或在 WXS(一种受控脚本语言)里写,不能在 WXML 里自由执行 JS;
  • 条件/列表渲染:wx:if / wx:for(注意 wx:key 别漏,否则列表复用会出状态错乱)。
js
Page({
  data: { list: [], loading: false },
  onLoad() {
    this.fetchList();
  },
  fetchList() {
    this.setData({ loading: true });
    // request 来自封装过的 wx.request(注意合法域名配置)
    request({ url: '/api/list' }).then((res) => {
      // 局部更新:路径式 setData,避免整页大对象传输
      this.setData({ list: res.data, loading: false });
    });
  },
  onPullDownRefresh() {
    this.fetchList();
    wx.stopPullDownRefresh();
  }
});
xml
<view wx:if="{{list.length === 0 && !loading}}" class="empty">暂无数据</view>
<view wx:for="{{list}}" wx:key="id" class="row" bindtap="onTapItem" data-id="{{item.id}}">
  <text>{{item.title}}</text>
</view>

上面 WXML 里的双花括号是小程序模板语法本身,与本站 VitePress 无关,属正常示例。

分包、体积与性能

微信等平台对代码包有严格上限(历史上主包约 2MB、总包约 20MB,近年已逐步放宽,具体数值以各平台最新官方说明为准),超限无法上传。工程对策:

  • 分包subpackages 把低频页面(详情、活动、订单列表等)拆到独立分包,随用随下载,并支持「分包预下载」;
  • 主包瘦身:公共库外置、图片走 CDN 不塞包、大组件按需注册;
  • 首屏优化:首屏只做必要请求并合并、关键数据预取(wx.setStorage 缓存上次结果先渲染再刷新)、骨架屏;
  • setData 纪律:用路径更新局部字段、避免在滚动/高频回调里整页 setData、避免序列化不了的值(如循环引用、Date 不经处理直接传)。

跨端框架:uni-app 与 Taro

「每个超级 App 写一套」成本太高,于是有了编译型跨端框架:用熟悉的 Vue / React 写业务,编译时转换输出成各目标端的小程序代码

维度uni-appTaro
语法Vue(Vue 3 为主)React(JSX + Hooks)
目标端微信 / 支付宝 / 抖音等 + H5 + App微信 / 支付宝 / 字节等 + H5(可选 RN)
工程HBuilderX 或 Vite CLI,pages.json 声明页面CLI 脚手架,页面即路由(pages 配置)
风格类 Vue 生态(vuex / pinia、uni-ui)类 React 生态(zustand 等、Taro UI)
多端差异处理条件编译注释端能力判断 + 平台配置文件

跨端框架的共同思路是「编译时转译 + 运行时抹平」:你写的是 Vue/React 组件,构建后产出目标端可运行的原生小程序代码。要接受的事实:

  • 编译产物仍是原生小程序,性能下限由宿主决定,框架不改变「setData 驱动」的本质;
  • 端差异永远抹不平:支付、登录、隐私授权等宿主能力在两端往往同名不同义,仍需条件编译或能力封装层逐端处理;
  • 升级节奏跟着框架走:宿主平台更新 API 后,要等框架跟进才能用,框架版本的升级计划要纳入项目预算。

选型信号:

  • 只做微信且团队就是小程序出身 → 原生写,少一层编译心智;
  • 团队是 Vue / 前端出身、要同时覆盖多端甚至 H5 → uni-app;
  • 团队是 React 栈、想在端上复用 JSX 心智与 npm 生态 → Taro。

若目标是 iOS + Android 双原生 App 而非超级 App 生态,则应选 Flutter / React Native / KMP(见跨端框架Kotlin Multiplatform),不要用小程序跨端框架去碰 App。

登录、用户与合规红线

小程序登录闭环必须后端参与(典型流程,以微信为例):

  1. 前端 wx.login() 拿到临时凭证 code
  2. 前端把 code 交给自己的后端
  3. 后端用 appid + secret + code 调宿主接口(如 auth.code2Session)换取 openid / session_key,并生成自己的会话(自签 token 或 session 存储)返回前端;
  4. 后续请求带自建会话,后端据此识别用户。

红线:

  • secret 只能存在于后端,绝不能打进前端代码或传给前端;
  • openidsession_key 不下发前端当「登录态」,防止被冒用或数据泄露;
  • 用户头像昵称、手机号等敏感信息走平台合规组件 + 后端换取(手机号现已改为「快速验证组件返回 code → 后端换号码」),前端不再直接拿明文;
  • 涉及用户信息的接口要申报平台「隐私保护指引」,隐私接口要调 wx.requirePrivacyAuthorize 类能力;
  • 域名白名单:正式环境 wx.request 等只能访问已配置并校验的 HTTPS 合法域名(需 ICP 备案)。

支付与开放能力(要点)

  • 支付:后端用平台支付 API 下单拿到 prepay_id 并做签名,前端 wx.requestPayment 拉起收银台;支付回调必须由后端接收并验签,不能只信前端通知(对应后端鉴权认证与回调验签的通用纪律);
  • 订阅消息wx.requestSubscribeMessage 引导用户授权后,可发一次性/长期订阅消息做触达(如订单状态、活动提醒);
  • 分享:实现 onShareAppMessage / onShareTimeline 才有转发入口;分享带参要能定位页面(path 带 query),配合 onLoad 读参;
  • 内容合规:虚拟支付、金融、医疗等类目有额外资质与平台规范,审核前要逐条对照当前类目要求。

提审与发布

发布链路与 App 不同——没有「灰度大版本商店审核外」的自主权

  1. 开发者工具上传代码包(可设「体验版」给内部/测试);
  2. 管理后台提交审核(填类目、提供测试账号、隐私说明等);
  3. 审核通过后发布;支持少量灰度(按比例 / 按白名单);
  4. 发布后发现问题:先功能开关/后端下线止血,再发新版本,别指望「下架 App」式操作;
  5. 留意基础库版本:用了新 API 要声明最低基础库版本,并在低版本设备上做降级或提示升级微信。

常见坑速查

现象对策
在逻辑层写 window / document直接报错或行为诡异记住没有 DOM;操作 DOM 的场景该用 WebView 承载
WXML 写 div / span标签不生效、白屏用受控标签 view / text
整页大对象高频 setData页面明显卡顿 / 传输超限路径式更新、节流、只传变化字段
忘写 wx:key列表复用错乱、状态串遍历绑定稳定唯一键
忘了 onShareAppMessage用户无法转发需要分享就实现,且处理好分享参数回读
code/openid 当前端登录态可被冒用、违反隐私红线会话必须由自己后端签发
secret 进前端凭证泄露事故只存后端,环境变量管理
正式环境域名未加白/未备案请求全部失败(开发工具正常、真机失败)提前配合法域名 + HTTPS + 备案
包超上限上传失败分包、CDN 化静态资源、按需引入
只在开发者工具调试真机才暴露授权、渲染差异高频用真机预览 + 体验版回归
用框架但不设升级预算平台 API 更新后被迫返工每年安排框架大版本升级

检查清单

  • [ ] 明确做哪些宿主端,并逐条核对平台能力与规范差异;
  • [ ] 决定原生 vs 跨端框架(uni-app / Taro),团队技术栈与端诉求匹配;
  • [ ] 工程已按「分包 + 静态资源外置」规划,主包体积有预算;
  • [ ] setData 纪律落地(路径更新、高频回调不整页传);
  • [ ] 登录闭环:code → 后端 → 自建会话secretopenid 不下发前端;
  • [ ] 支付回调由后端接收验签,不信任前端通知;
  • [ ] 隐私指引、敏感接口授权、域名白名单在生产前配好;
  • [ ] 提审材料(类目、测试账号、隐私说明)齐全,有灰度与止血预案。

相关链接:客户端开发索引 · 技术全景与选型 · Flutter · React Native · 桌面应用 · 后端配合见鉴权认证

基于 VitePress 构建 · 内容以知识共享方式沉淀