十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

小程序开发框架选型:原生、uni-app、Taro如何选择?

小程序开发框架选型:原生、uni-app、Taro如何选择? 小程序开发框架的选型问题几乎每个月都有团队在问原生开发、uni-app、Taro到底选哪个答案不是“哪个火选哪个”而是要看项目要覆盖几个小程序平台、团队主流技术栈是 Vue 还是 React、业务对微信独家能力依赖有多深。这篇文章会直接把三者放到同一张表里做对比然后分别讲清楚原生开发的真实成本、uni-app 和 Taro 的适用场景并通过“scroll-view 填满剩余高度”“网络请求封装”这类实际开发场景给出可以照做的代码示例和选型建议。1. 核心技术选型速览先看结论。下面这张表基本能覆盖 90% 团队在做选型时需要关注的维度对比项微信小程序原生开发uni-appTaro主流编程语言WXML WXSS JS JSONVue 2 / Vue 3React同时支持 Vue跨端范围仅单个平台小程序微信、支付宝、百度、字节等主流小程序 H5 App微信、支付宝、百度、字节等主流小程序 H5 React Native开发工具微信开发者工具HBuilderX / CLICLI 微信开发者工具调试页面代码复用度单平台内复用多平台需各写一套高一套代码编译多端高一套代码编译多端原生 API 覆盖微信平台能力更新最快支持最完整通过 uni API 封装独家能力用条件编译通过 Taro API 封装能力边界以框架维护进度为准UI 渲染方案原生组件渲染小程序端转原生组件App 端存在 WebView 渲染场景小程序端转原生组件H5 端走 DOM学习成本掌握微信小程序专属语法Vue 开发者上手快需理解条件编译和平台差异React 开发者上手快需理解跨端限制适合团队只做微信端且需要深度使用平台能力Vue 技术栈需要多端覆盖React 技术栈需要多端覆盖社区与维护官方维护资料丰富DCloud 主导插件生态大京东开源React 社区活跃注意一个容易被忽略的事实uni-app 和 Taro 不是竞品关系里的“平替”它们都只是“跨端编译方案”。真正的竞争发生在“原生开发”和“跨平台开发”之间而 uni-app / Taro 只是跨平台方案里的两个代表。2. 三种方案背后的实现原理2.1 原生小程序开发是怎么工作的原生开发就是用微信官方定义的一套语法写小程序。页面由四个文件组成.wxml负责结构、.wxss负责样式、.js负责逻辑、.json负责页面配置全局逻辑放在app.js全局配置放在app.json。{ pages: [ pages/index/index ], window: { navigationBarTitleText: 原生小程序 } }原生开发的代码不需要经过编译转换微信开发者工具里保存后即时预览。这个特性意味着微信每次发布新 API原生项目可以直接用遇到组件或渲染问题可以对照微信官方文档排查问题链路最短没有框架层的抽象损失性能可控。问题也直接如果哪天业务要求同步上线支付宝小程序、抖音小程序原生代码不能直接搬过去。虽然支付宝小程序和微信小程序的目录结构很像但 API 体系和样式规范已经出现明显分化比如授权登录、支付、分享卡片能力都不同等于再写一遍。所以原生开发真正的门槛不是语法难而是当“多平台”成为硬需求时维护成本会翻倍。2.2 uni-app 的原理与定位uni-app 是 DCloud 推出的跨端框架背后是 Vue 体系。开发者用 Vue 单文件组件写代码uni-app 再把它编译成各端产物。开发时也有两种项目形态在 HBuilderX 里直接创建 uni-app 项目可视化程度高用 CLI 创建项目便于接入 Git、CI/CD适合团队协作。uni-app的项目核心还是pages.json配置路由和页面样式逻辑层代码使用uni.request、uni.login这类统一 API 来调用平台能力。平时开发时代码里要区分“哪些是 Vue 语法层面的内容”和“哪些是平台 API 层面的抽象”。uni-app 最大的价值在于覆盖范围广一套代码可以跑微信、支付宝、百度、字节等小程序也能编译成 H5 站点和 Android / iOS App。对很多中小企业来说一套技术栈打通小程序 H5 App是非常实际的需求。npm 包和 Vue 插件的可用性也较高因为 uni-app 本身没有放弃 Web 生态多数纯逻辑类的 npm 包可以直接使用。但要注意不是所有能跑在浏览器里的代码都能跑在小程序里涉及 DOM 操作、window全局变量的库编译到小程序端时仍然会出问题。2.3 Taro 的原理与定位Taro 是京东开源的一套跨端框架社区里对它的标签是“React 语法写小程序”。Taro 3 之后采用了开放式架构核心思路是把 React 或 Vue 的代码通过编译器和运行时适配层映射成微信小程序等平台能理解的原生结构。使用 React 开发时页面就是一个函数组件import { View, Text } from tarojs/components; import { useLoad } from tarojs/taro; export default function Index() { useLoad(() { console.log(页面加载); }); return ( View TextHello Taro/Text /View ); }Taro 对 React 生态的支持比较成熟Hooks 写法、TypeScript 类型推导都能比较舒服地工作。如果团队本来就在写 React选 Taro 等于把前端的 React 开发经验直接平移过来。同时 Taro 也具备多端编译能力除了小程序端还能编译到 H5、React Native。这一点对以后扩展 App 项目有帮助但不要指望“一套业务代码同时完美运行到小程序和 App”跨端方案能解决的是“同一套业务逻辑、同一套团队技术栈”的复用界面上一定存在差异需要处理。3. 原生开发的真实优势与限制3.1 优势能力最新、问题链路最短原生开发最大的好处是不用为“能不能用某个新API”而发愁。微信小程序每年的版本更新速度很快比如虚拟支付、广告组件、硬件能力、新的分享能力等往往会第一时间在原生小程序里开放。如果你做的是电商、工具、私域运营类小程序原生能让你的产品更早使用官方能力。原生开发调试也方便。微信开发者工具里网络面板、缓存面板、性能分析、真机调试一应俱全。遇到问题只关注“微信原生框架怎么表现”不需要先判断是不是框架转换导致的。3.2 限制每次跨平台都是重新开发原生的限制主要体现在多平台需求出现时。以登录为例。微信端需要走wx.login拿 code然后换 openid支付宝端可能走my.getAuthCode体系。即使两个平台的页面写得很像登录代码仍然不能共用。要同时维护微信、支付宝、抖音三个原生项目意味着每次业务更新都要改三遍每个端都要重新自测和发布团队的人力会被迅速消耗掉。3.3 什么时候优先选原生如果项目只需要在微信一个平台运行并且大概率不会扩展到其他平台原生开发是稳妥的选择。使用微信最新 API 时没有框架层延迟性能也有保证。很多“功能驱动型”的微信小程序比如工具、预约、私域运营都不需要跨端原生的性价比很高。另一个反直觉的情况是团队已经有比较强的小程序原生开发经验并且对性能要求非常苛刻。此时使用跨端框架反而会受限于框架维护方的能力更新速度。不如保持原生状态把精力花在业务和性能调优上。4. uni-app 适合什么团队如果一个团队内部已经有 Vue 项目历史并且接下来需要同时覆盖微信、支付宝、H5 甚至 Appuni-app 是当前比较顺滑的方案。4.1 uni-app 的开发体验用 uni-app 的典型页面结构template view classcontainer view classcard v-foritem in list :keyitem.id text{{ item.name }}/text /view /view /template script setup import { ref } from vue; const list ref([ { id: 1, name: 示例数据 } ]); /script style scoped .container { padding: 24rpx; } .card { background: #fff; border-radius: 16rpx; } /style只要会写 Vue上手 uni-app 基本没有额外负担。开发者需要注意的重点已经从“记忆微信 API”变成“理解平台差异和条件编译”。比如不同小程序平台的视频组件能力不同需要在代码里主动做判断。条件编译是 uni-app 处理差异的主要手段// #ifdef MP-WEIXIN console.log(仅微信小程序生效); // #endif // #ifdef H5 console.log(仅 H5 生效); // #endif这种写法在提交代码时会看到很多类似结构它解决了“同一个文件里对不同平台写不同逻辑”的问题但也要求开发者自律。条件编译太多会让代码阅读成本上升所以能通过统一 API 解决的事情尽量不要拆分平台。4.2 uni-app 与原生打包扩展uni-app 的应用不限于小程序。它也能构建 Android / iOS App。常见流程是在 HBuilderX 中通过“发行”菜单完成云打包或本地打包如果需要接入自己的原生插件或者要集成厂商渠道包可以在 Android Studio 中完成离线打包。离线打包的核心是把 HBuilderX 生成的app-resources资源包放到 Android 原生工程中再通过离线 SDK 编译。此时工程里就会有比较明显的原生代码结构不能把它当作纯 uni-app 项目处理。这一点和热词中提到的“uni-app x 怎么使用 Android Studio 原生打包”有关本质上uni-app 负责业务代码Android Studio 负责原生壳和渠道差异。如果项目只能覆盖小程序并不需要一开始就引入 App 打包。框架的能力边界要等真正出现 App 需求时再扩展。4.3 uni-app 需要留意的坑页面生命周期和 Vue 生命周期并存onLoad、onShow、onHide需要单独记忆scroll-view必须在 flex 布局下明确高度否则会出现“明明用了 scroll-view 却一直在滚动整个页面”的问题nvue 页面与 vue 页面的渲染方式不同样式表现不完全一致平台 API 并不能做到百分百统一遇到兼容问题要优先查官方文档。5. Taro 适合什么团队Taro 的典型用户画像非常清晰React 技术栈团队。5.1 用 React 写小程序的体验Taro 引入组件时并不从react-domimport而是从tarojs/components导入小程序对应的基础组件。React 的 JSX 写法基本保留函数组件是第一等公民Hooks 能正常使用。import { View, Text, Button } from tarojs/components; import { useState } from react; import Taro from tarojs/taro; export default function Counter() { const [count, setCount] useState(0); const add () { setCount(count 1); Taro.showToast({ title: 当前数量${count 1}, icon: none }); }; return ( View Text{count}/Text Button onClick{add}增加/Button /View ); }这套代码在小程序端就是原生按钮在 H5 端则是普通 DOM 按钮。因为运行时适配层做了处理React 的组件模型仍然成立。5.2 React 与 Taro 开发必须记住的差异虽然 React 语法能保留但小程序环境并不等同于浏览器。最常见的问题是无视平台限制// 错误小程序里没有 window const width window.innerWidth;// 正确通过 Taro 环境信息获取 const { windowWidth } Taro.getSystemInfoSync();不要在 Taro 代码里随便使用 DOM API。第三方 React 组件只要涉及document.createElement、window.addEventListener都很难在小程序端正常工作。选型前就应该对这些 npm 包做兼容性排查。Hooks 在 Taro 里使用也有细节。Taro 提供了一些自己的 hook比如useLoad、useDidShow、useUnload这些对应小程序端拉页面的生命周期与 React 的useEffect不能完全等价。初次接触时容易写错的地方是在useEffect里发请求首次渲染没问题但页面从后台回前台时不会触发需要监听页面显示时应该用useDidShow组件卸载逻辑仍然可以用useEffect的清理函数但页面卸载时要关心useUnload。此外Taro 的样式隔离逻辑和原生小程序类似。默认情况下React 组件内引入的 css 会作为全局样式处理如果没有使用 CSS Modules很容易出现样式互相污染。这对习惯了 CRA 或 Next.js 局部样式隔离的 React 开发者来说需要特别适应。5.3 Taro 的生态与扩展Taro 背靠京东体系内部的大型小程序项目数量很多这也是它能持续维护的重要原因。对于复杂商城类项目组件库、状态管理库、TypeScript 支持都有较成熟的案例。需要注意的是Taro 和原生小程序混写其实也是可用的。部分原生页面或原生组件可以直接嵌入 Taro 项目这让从原生项目渐进式迁移变得可行。不过混写会提高工程复杂度更建议在项目早期就确定好边界。6. 同一场景下的代码对比scroll-view 填满剩余高度这里用一个非常常见、又容易踩坑的场景做对比页面里有固定头部下面需要一个列表区域但只有列表内容能滚动整个页面不能滚动。先说结论scroll-view不是普通 view要让它滚动必须把它的高度限制为一个固定值或一个确定布局空间。6.1 原生小程序中的实现在微信原生小程序里很多人第一眼会把scroll-view当成普通容器结果列表一次能把整个页面拉得很长页面也滚动了滚动逻辑完全错乱。正确做法是让父级变成 flex 纵向布局并且给 scroll-view 一个确定高度的 flex 空间。view classpage view classheader固定头部/view scroll-view classlist-scroll scroll-y view wx:for{{list}} wx:keyindex classitem{{item}}/view /scroll-view /view.page { height: 100vh; display: flex; flex-direction: column; } .header { height: 88rpx; flex-shrink: 0; } .list-scroll { flex: 1; height: 0; }核心是flex: 1配合height: 0让scroll-view被压缩到父级剩余高度而不是自动撑开。这时内部的列表项超出容器后scroll-y才会生效。6.2 uni-app 中的实现uni-app 的页面同样支持 CSS flex 布局Vue 结构里scroll-view的父级要给定高度百分比或100vh。template view classpage view classheader固定头部/view scroll-view classlist-scroll scroll-y view v-foritem in list :keyitem classitem{{ item }}/view /scroll-view /view /template script setup import { ref } from vue; const list ref(Array.from({ length: 30 }, (_, i) 内容 ${i 1})); /script style scoped .page { height: 100vh; display: flex; flex-direction: column; } .header { height: 88rpx; flex-shrink: 0; } .list-scroll { flex: 1; height: 0; } /style如果一定要用 JS 计算剩余高度思路是先拿到屏幕高度再减掉固定头部高度。比如const query uni.createSelectorQuery(); query.select(.header).boundingClientRect(); query.selectViewport().scrollOffset(); query.exec((res) { // res[0] 是 header 的高度 });但这种做法会引入性能与兼容问题布局简单时优先考虑 flex 方案不要一开始就写 JS 计算。6.3 Taro 中的实现Taro 里使用 React 风格写页面但布局思路一致。import { View, ScrollView } from tarojs/components; import { useState } from react; import ./index.scss; const list Array.from({ length: 30 }, (_, i) 内容 ${i 1}); export default function Index() { const [items] useState(list); return ( View classNamepage View classNameheader固定头部/View ScrollView classNamelist-scroll scrollY {items.map((item) ( View classNameitem key{item} {item} /View ))} /ScrollView /View ); }对应 SCSS.page { height: 100vh; display: flex; flex-direction: column; } .header { height: 88px; flex-shrink: 0; } .list-scroll { flex: 1; height: 0; }Taro 中还有一个处理“回到列表顶部”的需求可以通过设置scrollTop或使用scroll-into-view实现。普通的view没有滚动容器对应的scrollTop能力所以只有把容器换成ScrollView才能控制内部滚动位置。7. 网络请求与登录态的封装设计框架选型决定的不仅是页面写法还决定业务层怎么访问后端接口、怎么处理用户登录状态。比较好的实践是不管用原生、uni-app 还是 Taro在业务代码里都不直接散落调用wx.request或uni.request而是先封装一层 request。7.1 原生小程序的 request 封装const request (url, data {}, method GET) { return new Promise((resolve, reject) { wx.request({ url: https://api.example.com${url}, data, method, header: { content-type: application/json }, success: (res) { if (res.statusCode 400) { resolve(res.data); } else { reject(res); } }, fail: reject }); }); }; module.exports { request };7.2 uni-app 的 request 封装const request (url, data {}, method GET) { return new Promise((resolve, reject) { uni.request({ url: https://api.example.com${url}, data, method, header: { content-type: application/json }, success: (res) { if (res.statusCode 400) { resolve(res.data); } else { reject(res); } }, fail: reject }); }); }; export default request;7.3 Taro 的 request 封装import Taro from tarojs/taro; interface RequestOptions { url: string; data?: Recordstring, unknown; method?: GET | POST | PUT | DELETE; } export async function requestT({ url, data, method GET }: RequestOptions) { const res await Taro.requestT({ url: https://api.example.com${url}, data, method, header: { content-type: application/json } }); return res.data; }通过这层封装未来的登录 token、统一错误处理、埋点上报都可以集中管理。无论选择哪个框架这个结构都应该成为项目的基础设施否则换端或调整 API 时全局业务代码会产生大量改动。登录态处理在多端下面临的问题最多。微信登录、支付宝登录、抖音登录虽然都叫“登录”但底层的授权能力、服务端换取用户信息的流程都不同。跨端框架一般只把“获取用户登录凭证”的能力封装成统一 API但服务端对接逻辑仍然需要按 platform 参数做不同处理。在跨端项目里比较好的设计是前端只往服务端传platform和登录凭证由服务端统一下发 token{ platform: weixin, code: 前端通过 uni.login / Taro.login 获取的 code }而不是在前端写大量平台分支。平台差异收敛得越靠后端前端维护的负担就越小。8. 团队选型决策清单技术选型很难有唯一正确答案但可以通过几个条件排除掉明显不合适的选项。下面是一套比较实用的决策流程8.1 判断是否选原生如果满足多数条件优先考虑原生小程序开发只需要在微信平台上线业务会经常使用微信最新开放的 API团队已经有原生小程序开发经验对首屏性能有较高要求没有短期内覆盖支付宝、抖音小程序的计划。8.2 判断是否选 uni-app如果满足多数条件优先考虑 uni-app团队主栈是 Vue 2 / Vue 3需要同时覆盖微信、支付宝小程序或未来要扩展 H5、App希望使用一套代码管理业务项目中大量使用 uni-app 生态插件没有强依赖最新微信原生能力的诉求。8.3 判断是否选 Taro如果满足多数条件优先考虑 Taro团队主栈是 React需要快速开发微信、支付宝等多端小程序团队拥有比较丰富的 React Hook 与 TypeScript 开发经验后续有扩展到 H5 或 React Native 的规划愿意接受 Taro 框架层的升级节奏。最不推荐的情况是团队没有明显跨端需求只因为“跨端是趋势”强行引入 uni-app 或 Taro。这样会额外引入框架学习成本和条件编译维护成本却享受不到跨端红利最终变成“为了跨端而跨端”。9. 常见问题与排查方法问题现象可能原因排查方式解决方案scroll-view 不滚动整个页面跟着滚scroll-view 高度未被限制检查父级是否 flex 布局scroll-view 是否设置了 flex:1 和 height:0给 scroll-view 一个确定高度或使用 flex 剩余空间方案scroll-view 普通 view 内容无法控制滚动位置只有 scroll-view 才有 scrollTop 等接口查看组件文档确认使用组件正确将普通 view 替换为 scroll-viewTaro 项目中使用 window/document 报错小程序没有 BOM/DOM 对象搜索代码里直接使用 window、document替换成 Taro.getSystemInfoSync 或使用条件编译uni-app HBuilderX 云打包失败证书配置、依赖版本或网络问题查看打包日志确认 appid 和证书匹配重新生成证书配置必要时切离线打包React 组件在 Taro 里运行 UI 不更新setState 更新时机、状态引用被修改检查是否直接修改了 state 数组创建新数组再 setState必要时使用 useEffect 观察变化跨端登录不稳定不同平台登录 code 互换逻辑不一致抓接口请求查看 platform 参数和 code由服务端区分平台前端只传 platform 与 code条件编译代码在不同平台表现不同编译条件写错或遗漏平台清理 watch 缓存检查 #ifdef 条件重新编译添加正确平台条件npm 包在跨端后不可用依赖了浏览器 API阅读包源码检查是否操作 DOM换纯逻辑库或自己实现兼容层10. 最佳实践与建议10.1 保持平台差异在后端收敛不要在前端每个按钮里都写“如果微信怎么样支付宝怎么样”。推荐把平台类型和登录凭证传到后端由后端完成平台差异处理。这样小程序端只关心页面展示和用户交互代码会干净很多。10.2 三层封装原则无论选择哪个框架业务代码尽量不要直接触碰平台 API。建议至少抽出三层request 层统一 loading、token、错误码处理公共组件层封装用户信息、列表、空状态等通用视图业务工具层把日期格式化、权限判断、埋点参数封装成纯函数。封装越彻底未来跨端迁移时能保留的资产越多。跨端迁移并不是完全重写业务逻辑层大部分代码仍然可以复用。10.3 样式层面优先使用纯 CSS 布局在设计复杂滚动、吸顶、悬浮布局时优先用 CSS flex / sticky 等能力完成而不是在 JS 里频繁调用createSelectorQuery量高度和位置。布局越依赖 JS 实时计算页面在复杂机型上的闪烁和错位风险越高。用 flex 让 scroll-view 自动获得剩余高度是目前各种方案里通用性最好的做法。它既不用区分 rpx 和 px也不用在不同机型之间重新适配。10.4 新项目启动时做好 TypeScript 与代码规范如果是新项目不管原生、uni-app 还是 Taro都应该从一开始启用 TypeScript。跨端框架对 TS 的支持已经比较成熟类型约束能帮助团队尽早发现 API 误用减少把问题带到线上才暴露的概率。对样式相对复杂的项目建议给页面组件的关键区域写好注释尤其是哪些区域是固定高度、哪些区域是自适应。后续接手的人能快速理解滚动容器为什么这样布局避免为了“修一个样式 bug”又改回 JS 计算高度。10.5 兼容性与合规边界小程序涉及用户信息、手机号、支付、位置等敏感能力时必须向用户明示用途并遵循对应平台的隐私保护要求。尤其是跨端项目各平台对隐私弹窗、用户授权、数据存储都有不同规定上线前需要在每个目标平台做合规检查。涉及用户手机号授权、位置授权、相册访问的功能不能只调通一个平台就当完成必须以真机环境在各端分别测试。11. 写到最后的选择思路综合以上内容不同团队的最优方案是分层的。如果今天只有一个微信小程序需求团队还没有历史包袱直接学微信原生开发也完全可行。原生语法本身不复杂真正复杂的业务逻辑不管写在哪个框架里都一样。如果团队已经在写 Vue并且产品规划里有支付宝小程序、H5、App等长线需求uni-app 是一个高效率方案。它能把 Vue 团队的既有能力直接迁移过来避免另起炉灶。如果团队本来就是 React 开发Taro 在语法和生态上的匹配度明显更高。React Hooks 开发习惯可以延续TypeScript 大型项目的工程经验也能直接用上。没有万能框架但有相对合理的选型路径先确认目标平台和团队技术栈再判断是否需要跨端最后再看框架对目标平台的能力覆盖和社区活跃度。小程序开发框架选型本质上是在“平台原生化”和“团队工程化”之间做取舍。想清楚项目阶段让技术框架服务于产品迭代而不是为了追新而引入复杂度最终自然能找到当前阶段正确的方案。
返回列表