ZKmall开源商城的移动商城搭建:Uni-app+Vue3 实现多端购物体验
现在用户买东西的场景越来越多了 —— 有人习惯用微信小程序刷一刷,有人喜欢在手机 APP 里慢慢挑,还有人在平板上比价、在 H5 页面里临时下单。要是每个端都单独开发一套商城,不仅成本高,维护起来也头疼。ZKmall开源商城用了 Uni-app 加 Vue3 的组合,一套代码能同时跑在 iOS、Android、微信小程序、支付宝小程序这些平台上,既省了功夫,又保证了用户不管在哪种设备上逛,体验都差不多。这背后可不是简单的代码复制,而是一套精巧的多端适配技术和业务逻辑设计。
技术架构:为什么选 Uni-app 和 Vue3?
选对技术框架,多端开发就成功了一半。ZKmall 的技术选型是经过实际项目验证的,既考虑了开发效率,也兼顾了运行性能。
Uni-app 的跨平台能力是最吸引人的地方。它基于 Vue 语法,写一套代码就能编译成各种平台的应用 ——iOS 的 IPA 包、Android 的 APK、微信 / 支付宝小程序,甚至 H5 页面都能直接输出。开发团队不用再学小程序的语法、APP 的原生开发,用熟悉的 Vue 知识就能搞定多端。比如一个商品详情页,写完之后在小程序里点进去是小程序的样式,在 APP 里打开就是原生应用的体验,不用改代码。这种 "一次开发,多端运行" 的模式,比每个端单独开发能省 60% 以上的时间。
Vue3 的语法特性让复杂业务逻辑更清晰。ZKmall 全用了 Vue3 的 Composition API,把商品列表、购物车这些功能的逻辑拆成一个个独立的函数,比如处理商品加载的useProductList
、管理购物车的useCart
,哪里需要就直接引入,代码既干净又好复用。用ref
和reactive
管理数据,比 Vue2 的响应式机制更灵活,商品价格变了、库存少了,页面能立马反应过来。setup
函数作为组件入口,把数据和方法都放在一起,看代码的时候不用来回跳着找。这些特性在处理购物车数量计算、订单状态更新这些复杂场景时,优势特别明显。
配套工具也得跟上。状态管理用了 Pinia,比 Vuex 简单多了,存个用户信息、购物车数据,定义个 store 就能在全应用里用,还支持 TypeScript,写代码的时候能少犯点错。路由用 Uni-app 自带的uni-router
,在pages.json
里配置一下页面路径,多端跳转的行为都能保持一致。UI 组件选了 uView UI,里面的商品轮播、地址选择器这些都是为移动端量身定做的,而且在不同平台上会自动调整样式,比如小程序的按钮圆角小一点,APP 的按钮更圆润,不用自己调。请求数据用uni.request
封装了一下,不管哪个端,请求拦截、错误提示都统一处理,不用重复写代码。
性能优化是多端应用的关键。Uni-app 会把 Vue 代码编译成原生渲染的代码,所以 ZKmall 在小程序和 APP 上的速度跟原生应用差不多。组件引入用了 easycom 机制,写的时候不用手动导入,打包时还能自动精简体积。商品列表太长容易卡?用图片懒加载和虚拟滚动,只加载用户能看到的部分,滑动起来就顺畅多了。像购物车改数量这种用户频繁操作的地方,加了防抖处理,不会一点击就反复发请求。
多端适配:怎么让一套代码适应不同平台?
不同平台的 "脾气" 不一样 —— 微信小程序有自己的支付接口,APP 能调用手机的指纹功能,H5 页面得跳转到浏览器支付。ZKmall 用了几招巧妙的适配技术,让这些差异在代码里不那么显眼。
条件编译能在同一文件里写不同平台的代码。Uni-app 有#ifdef
、#ifndef
这些指令,比如判断当前是微信小程序还是 APP,然后执行对应的代码。就拿支付来说,微信小程序得用它自己的wx.requestPayment
,APP 端用uni.requestPayment
,H5 页面直接跳转到支付链接,这些不同的逻辑都能写在同一个payOrder
函数里,用条件编译隔开。这样一来,不管哪个平台调用这个函数,都能找到对应的支付方式,代码也不用拆得七零八落,维护起来方便多了。
样式适配要让界面在各种设备上都好看。移动端屏幕大小差别大,ZKmall 用 rpx 做单位,1rpx 等于屏幕宽度的 1/750,不管是小屏手机还是平板,元素都能自动缩放。手机上商品列表一排显示 2 个,平板上想多显示一个?用@media
查询判断屏幕宽度,超过一定尺寸就改成 3 列布局。导航栏和状态栏在 iOS 和 Android 上样式不一样,用page-meta
组件动态调整,比如 iOS 的导航栏标题居中,Android 的靠左,用户看着就顺眼。小程序和 APP 的按钮样式不一样?用条件编译的样式块,给微信小程序的按钮单独加个圆角,不会影响其他平台。
API 封装能把平台差异藏起来。Uni-app 已经封装了很多通用 API,比如uni.navigateTo
跳转页面、uni.getStorage
存数据,在各个平台上用法都一样。但有些平台特有的接口怎么办?ZKmall 封装了个platform.js
工具类,把定位、选图片这些功能统一成简单的接口。比如获取位置,微信小程序得申请权限再调用wx.getLocation
,APP 端直接用uni.getLocation
,工具类里处理好这些差别,业务代码里只要调用platform.getLocation()
就行了,不用管底层是怎么实现的。
权限管理得按平台规矩来。获取相机、位置这些权限,每个平台的流程都不同。微信小程序的权限弹窗只能在用户点击时才弹,APP 可以主动申请。ZKmall 先用uni.getSetting
查一下用户有没有授权,没授权就用uni.authorize
去申请,提示文案也按平台改,比如微信小程序里写 "请允许获取位置以推荐附近商品",APP 里就说 "需要位置权限才能显示本地仓库"。扫码功能调用相机前,肯定要先检查权限,没开的话就弹个引导弹窗,告诉用户去设置里打开。
原生能力能让应用功能更强大。有些功能 Uni-app 没直接封装,比如 APP 的消息推送、小程序的分享。ZKmall 从插件市场找了个推、极光推送这些插件,条件编译一下,只在 APP 里引入,用户登录后就注册设备令牌,订单发货了能收到推送。微信小程序里想分享商品?用uni.showShareMenu
打开分享按钮,再写个onShareAppMessage
函数设置分享标题和图片,用户转发给朋友就方便多了。
核心模块:多端商城的业务逻辑怎么实现?
移动商城的核心功能就那几样 —— 看商品、加购物车、下单、付款、查订单。ZKmall 在实现这些模块时,既保证了多端代码复用,又兼顾了不同平台的特性。
商品模块得让用户看得爽、找得快。列表页用uni-list
组件搭的,商品卡片循环渲染出来,图片用懒加载,滚动到哪才加载哪张。顶部的分类筛选用uni-segmented-control
做的,点一下就切换分类,筛选条件变了就重新加载数据,加载的时候显示个骨架屏,用户不会觉得卡。详情页用scroll-view
实现长滚动,顶部轮播图用swiper
,评价列表太长就分页加载,滑到底部自动加载下一页。选规格的时候弹个u-popup
弹窗,选完颜色尺寸,价格和库存立马就更新,这些逻辑都写在setup
函数里,拆成加载商品、处理规格、加入购物车几个小函数,看起来清清楚楚。
购物车模块得管好商品的增删改。用 Pinia 的cartStore
存购物车数据,里面有商品列表、选中状态、总金额这些。没登录的时候,数据存在本地uni.storage
里,登录后就同步到服务器,同步的时候加个 loading 状态,防止用户连续点。购物车页面用u-checkbox
做选择框,u-stepper
调数量,选了哪个商品,cartStore
里的calcTotal
函数就自动算总金额。删商品的时候弹个确认窗,用户点了确认就调用removeCartItem
方法,本地和服务器的数据一起更新。用 Vue3 的computed
属性实时算选中的商品数量和总价,页面上的数据永远是最新的。
订单模块要处理从下单到收货的全流程。列表页用uni-section
按状态分组,待支付、待发货的订单分开显示,每个订单里有商品缩略图、金额和状态标签。点 "支付" 按钮就调payOrder
函数,传个订单 ID,付完钱跳转到结果页。订单状态用过滤器格式化一下,比如把 "UNPAID" 显示成 "待支付"。详情页用uni-timeline
展示物流进度,一目了然。"取消订单"" 确认收货 "这些按钮,根据订单当前状态动态显示,比如待支付的订单才显示" 取消 " 按钮,点了之后调用接口改状态,页面立马刷新。Vue3 的ref
和reactive
让订单数据更新特别方便,支付成功后改一下orderStatus
,页面上的按钮和状态文字就全变了。
用户模块主要管登录和个人信息。登录页用u-form
做的表单,手机号和密码都有验证,比如手机号得是 11 位,密码不能少于 8 位。点登录按钮就调用userStore.login
方法,成功了就存 token,跳回首页。个人中心页面用v-if
判断登录状态,没登录就显示 "去登录" 按钮,登录了就展示头像、昵称和订单入口。"收货地址" 页面用uni-list
列出来,点 "新增" 就跳去编辑页,编辑页用u-region
组件做省市区三级联动,选起来很方便。这些逻辑都封装成useUser
、useAddress
这样的组合函数,哪里需要就引哪里,不用重复写。
支付模块得把各种支付方式串起来。订单确认页显示商品清单、金额明细和支付方式,选了微信支付或支付宝,点 "立即支付" 就调payOrder
函数。支付的时候弹个加载窗,成功了就跳结果页,失败了就提示原因。不同平台的支付流程不一样,但用户看起来是一样的 —— 微信小程序里点支付,直接弹出微信的支付框;APP 里可能用指纹支付;H5 页面跳转到支付网关。这些差异都用条件编译处理了,业务代码不用管是哪个平台,只管调用支付函数就行。结果页给两个按钮,"查看订单" 和 "继续购物",点一下就跳过去,流程很顺畅。
性能和体验:怎么让移动商城用起来更舒服?
手机性能有限,用户又没耐心等,所以移动商城的性能和体验特别重要。ZKmall 从启动速度、页面流畅度、交互反馈这些方面下了功夫,让应用用起来又快又顺手。
启动速度得让用户少等。Uni-app 的分包加载很有用,把首页、商品列表这些用户一打开就看的页面放主包,个人中心、设置这些不常用的放分包,主包控制在 2MB 以内,小程序启动速度能快 40%。首页的数据,像轮播图、分类列表,在App.vue
的onLaunch
里就提前请求,存在全局状态里,页面打开的时候直接用,不用再等加载。启动页用简单的静态图,别搞复杂动画,不然用户还没看到内容就烦了。APP 端在 HBuilderX 里开了 "运行内存优化",没用的资源不加载,启动时间能控制在 3 秒以内。
页面渲染要保证滑动流畅。商品列表特别长的时候,用recycle-view
做虚拟滚动,只渲染用户能看到的那几行,DOM 节点少了,滚动起来就不卡,1000 多个商品的列表,帧率能从 30fps 提到 55fps。v-for
里别用v-if
,先用计算属性把要显示的数据过滤好,再渲染。图片用 CDN 加速,根据设备分辨率加载不同尺寸的图,比如手机加载 @2x 的,平板加载 @3x 的,还得设好宽高,免得图片加载的时候页面来回晃。弹窗、提示框这些元素,用 Vue3 的teleport
挂载到body
下面,不受父组件样式影响,渲染起来更快。
网络请求得减少等待时间。请求工具类做了不少优化:同一个商品详情,一秒内多次请求的话,只发一次,结果大家共用;分类数据这种不常变的,缓存 30 分钟,不用每次都去查服务器;网络不好的时候,请求失败了自动重试 2 次。列表数据分页加载,下拉刷新看新的,上拉加载更多,不用一下子加载几百条。用户信息、购物车这些经常用的数据,存在uni.setStorageSync
里,减少重复请求。弱网环境下,显示加载状态和网络提示,请求超时设成 15 秒,别让用户一直等
交互体验要让用户操作有反馈。按钮、商品卡片这些能点的元素,点的时候加个反馈,比如颜色变深一点,或者轻微缩一下,用u-button
的hover-class
就能实现。提交订单、支付这些操作,加个 loading 状态,按钮变灰不能再点,防止用户多点。页面跳转用uni.navigateTo
的动画,看起来流畅。出错了用u-toast
提示,字要少而清楚,比如 "库存不足" 比 "您所购买的商品当前库存数量已不足,请减少购买数量后重试" 好多了。重要操作像删商品,弹个二次确认窗,免得用户手滑。移动端手势也用上了,商品详情页的图片能缩放,左右滑能切图,用起来方便。
内存占用得控制好,不然应用容易崩。页面关掉的时候,在onUnload
里把定时器、事件监听都清掉;图片预览完及时关掉,别占着内存;列表页离开时把数据清空,别缓存一堆 DOM 节点。图片懒加载用uni.createIntersectionObserver
实现,只有图片进入屏幕才加载,初始加载的时候内存占用少。用 Uni-app 的性能分析工具盯着内存使用,发现内存泄漏就赶紧改,比如有没有没销毁的闭包、没解绑的事件。
测试和发布:多端应用怎么保证质量?
多端应用测试起来比单端麻烦,每个平台都有自己的规矩,发布流程也不一样。ZKmall 有一套完整的流程,确保应用在各个平台上都能顺利上线。
多端测试得覆盖各种情况。功能测试按平台写用例,重点测那些平台特有的功能,比如小程序的分享、APP 的推送。兼容性测试得用真机,iOS 从 12 开始,Android 从 7.0 开始,各种品牌、尺寸的手机都得试试,看界面有没有变形。性能测试用各平台的工具,微信小程序用开发者工具的性能面板,Android 用 Android Studio 的 Profiler,看看启动时间、内存占用、帧率达不达标。还得找真实用户体验一下,问问他们在不同平台上用着顺不顺手,有没有觉得别扭的地方。
自动化测试能提高效率。用 Jest 测 Pinia 的状态管理和工具函数,比如加商品到购物车,算出来的总价对不对。用 Vue Test Utils 测组件,比如商品卡片能不能正常显示,点加入购物车会不会触发事件。E2E 测试用 HBuilderX 的自动化工具,模拟用户从看商品到下单的全过程,确保整个流程能走通。代码提交的时候,GitLab 的 CI/CD 流水线自动跑这些测试,有问题能及时发现。
发布流程得按平台规矩来。小程序端,微信的就用微信开发者工具上传代码,在公众平台提交审核,通过了才能发布;支付宝小程序类似,在开放平台审核。APP 端,iOS 打包成 IPA 文件,传到 App Store Connect,苹果审核比较严,得按它的规矩来;Android 打包成 APK,上传到华为、小米、应用宝这些应用市场,有些市场要软著和 ICP 备案,得提前准备好。H5 端打包成静态文件,放 CDN 或服务器上,记得配 HTTPS 和缓存策略。发布前一定要在生产环境测一遍,看看 API 地址、支付接口这些配置对不对,别上线了才发现连接的还是测试服务器。
版本管理要有序。版本号用语义化的,比如 v1.2.3,第一个数字是大版本,改得 incompatibility 了才升;第二个是小版本,加功能了就升;第三个是修订版,修 bug 了就升。每次发布都写个 CHANGELOG,告诉用户加了什么功能,修了什么问题。小程序和 APP 的版本号保持一致,用户反馈问题的时候,一说版本号就知道是哪个端的哪个版本。大更新前先放个测试版,让部分用户试试,没问题了再正式发。
经验和展望:多端开发有哪些坑和机会?
ZKmall 做下来,踩了不少坑,也总结了些经验,对想做多端商城的人可能有点用。
代码复用和平台差异要平衡。核心业务逻辑,比如算价格、处理订单状态,复用率能到 80% 以上,但 UI 样式和平台特有功能,能复用 60% 就不错了。别强求 100% 复用,那样代码会搞得很复杂,用条件编译把平台差异隔离开,反而好维护。ZKmall 的做法是:通用逻辑抽成 hooks 或工具类,平台特有代码用条件编译分开,形成 "通用 + 平台" 的代码结构。
性能优化要分平台。不同平台的瓶颈不一样:小程序受包体积和渲染限制,得优化启动速度和内存;APP 更在意原生体验和后台运行;H5 要优化首屏加载和响应式。统一的优化方案效果不好,得针对每个平台的特点来。比如小程序里图片要压缩得小一点,APP 里可以用高清图。