副业创业系列 · 第 16 篇全系列共 21 篇 · 关注不迷路
写这套知识付费小程序之前,我在选前端技术栈这事上纠结了挺久。
一开始的方案挺「正统」的:微信小程序用原生写,H5 用 Vue 单独写一套。我脑子里想的是,两边都用各自最原生的东西,性能好,坑也少。
但真开始动手之前我算了一笔账。同样一个课程详情页,要写两遍;同样一个下单流程,要调两套接口;同样一个用户中心,改一次样式要改两个地方。我一个人下班撸代码,本来时间就不多,这么搞估计半年都上不了线。
那段时间我在掘金上翻了不少文章,看到有人在用 UniApp,说是一套代码能跑小程序、H5、甚至 App。我心动了,但也怕。因为跨端框架我以前听过不少吐槽,什么「一套代码,多端出 Bug」、什么「看着省事,实际更麻烦」。
犹豫了几天,我还是决定试试 UniApp。理由也简单:我是后端出身,前端写得慢,能省一半时间就是赚。而且我这套东西的主战场是小程序,H5 只是辅助,性能要求没那么苛刻。
一开始,我以为真能一套代码搞定
刚上手那几天,我确实爽了一阵。
Vue 的语法我熟,pages.json 配一下路由,manifest.json 填一下 AppID,npm run dev:mp-weixin 跑微信小程序,npm run dev:h5 跑 H5。两边同一份代码,同时跑起来的时候我还挺得意的,感觉自己捡了个大便宜。
前几个页面写得也顺。首页列表、分类页、搜索页,这些逻辑简单的,两边效果差不多。我还专门在朋友圈发了一句「跨端真香」,现在回头看挺打脸的。
打脸是从课程详情页开始的。
我踩过的 8 个大坑,你别再踩了
我把踩过的坑按痛苦程度排个序,每个都说说我是怎么填的。
坑 1:分享——小程序和 H5 完全是两套逻辑
小程序里分享得用 onShareAppMessage,配置一下标题、图片、路径就行。H5 里根本没这玩意,H5 想分享得接微信 JS-SDK,先注入配置,再自定义分享内容,逻辑完全是两套。
我的解决方案:条件编译 + 统一封装
// utils/share.jsexportdefaultfunctionshare(options) {// #ifdef MP-WEIXINreturn {title: options.title,path: options.path,imageUrl: options.image }// #endif// #ifdef H5// 调微信 JSSDK 的 wx.updateAppMessageShareData wx.ready(() => { wx.updateAppMessageShareData({title: options.title,desc: options.desc,link: window.location.href,imgUrl: options.image }) })// #endif}页面里只管调用我封装的 share() 方法,里面走哪套 API,业务代码不用管。
坑 2:支付——光这一块我改了好几天
小程序调 uni.requestPayment,参数一套;H5 上要走微信浏览器的 JSSDK 支付,或者直接跳 H5 支付页,又是一套。而且 H5 支付还得区分是不是在微信里打开的。
我的支付适配层:
// utils/pay.jsexportdefaultfunctionpay(orderInfo) {returnnewPromise((resolve, reject) => {// #ifdef MP-WEIXIN uni.requestPayment({provider: 'wxpay',timeStamp: orderInfo.timeStamp,nonceStr: orderInfo.nonceStr,package: orderInfo.package,signType: orderInfo.signType,paySign: orderInfo.paySign,success: resolve,fail: reject })// #endif// #ifdef H5if (isWechatBrowser()) {// 微信浏览器内:走 JSSDK 支付WeixinJSBridge.invoke('getBrandWCPayRequest', {// 参数 }, res => { res.err_msg === 'get_brand_wcpay_request:ok' ? resolve() : reject() }) } else {// 微信外:跳 H5 支付链接window.location.href = orderInfo.h5PayUrl }// #endif })}同样,业务代码只管调 pay(),不用关心底层是什么。
坑 3:样式——rpx 在 H5 上飘了
UniApp 官方推荐用 rpx,说是能自适应。小程序里表现挺正常,H5 里就开始飘了。有些页面在 iPhone 上看着还行,一到安卓机就错位;宽屏浏览器打开更是灾难,整个布局像被拉伸的年画。
我的解决方案:
1. 小程序继续用 rpx,没啥问题2. H5 加媒体查询兜底,宽屏限死最大宽度 3. 全局加个最大宽度,超过就两边留白
/* 只在 H5 生效 *//* #ifdef H5 */page {max-width: 750px;margin: 0 auto;}/* #endif */丑一点没关系,别错位就行。反正 H5 是辅助,主流量在小程序。
坑 4:路由——浏览器返回键行为不对
小程序的 tabBar 页面切换用 switchTab,普通页面用 navigateTo,还有 redirectTo、reLaunch 一堆。H5 上这些 API 都能用,但行为跟浏览器的历史栈对不上——用户按浏览器的返回键,跳出来的页面顺序有时候是错的。
这个坑我到现在都没完全填平。我的做法是:
• 核心流程尽量用 navigateTo,少用redirectTo和reLaunch• tabBar 切换就用 switchTab,接受它清历史栈的行为• 一些边角场景我干脆让它去,不影响核心流程就行
坑 5:各种「只在某一端存在」的 API
uni.getUserProfile | ||
uni.scanCode | ||
我印象最深的是调地理位置,小程序里跑得好好的,H5 一打开直接白屏。翻日志才发现 H5 用的是浏览器的 navigator.geolocation,得走 HTTPS,本地开发环境 http://localhost 直接被拒。
这种坑不踩一次根本想不到。我的做法是:
• 能用 UniApp 统一 API 的就用统一的 • 不能的就用条件编译封装成自己的方法 • 实在不行,这个功能 H5 砍掉或者降级
坑 6:小程序包体积限制
微信小程序主包不能超过 2M,分包不能超过 20M。我一开始没注意,图片、字体什么的都往项目里塞,打包的时候一看 3M 了,直接传不上去。
解决方案:
1. 静态资源全放 CDN,别往项目里塞 2. 大图片压缩一遍再传 3. 用分包加载,tabBar 页放主包,其他页放分包 4. manifest.json里开代码压缩和摇树优化
坑 7:调试体验割裂
小程序的问题要在微信开发者工具里看,H5 的问题要在 Chrome 里看。同一个 Bug,你得在两个工具之间切来切去。有时候小程序好着呢,H5 崩了;有时候 H5 好着呢,小程序白屏了。
这个没啥好办法,只能习惯。我一般是先在 H5 上把业务逻辑调通,再去小程序里调样式和平台特性。
我的跨端心法:80% 统一,20% 接受差异
踩了几周之后我算是明白了:一套代码跨端这话没错,但「一套代码」不代表「一份写法」。真要跨端跑得稳,你得把差异藏起来,藏在你自己封装的那层里。
我的 4 个心法
1. 老老实实用条件编译:一开始我嫌它丑,觉得代码里满是这种注释块看着很脏。后来接受了——脏就脏吧,能跑就行。分享、支付、登录这几个模块里,条件编译几乎是每几行就来一段。 2. 封装适配层:把所有跟平台有关的调用都收到一个 platform目录下,比如share.js、pay.js、auth.js。外面调用的时候只管调我封装的方法,里面到底走哪套 API,我在文件里用条件编译处理掉。这样业务代码就干净多了。3. 承认有些页面就得单独写:我一开始死犟,非要一份 .vue文件跑两端。后来在个人中心页上被折磨到崩溃——小程序里要展示各种小程序独有的入口,H5 里这些入口全没有,还多出几个只有 H5 才有的功能。我最后把它拆成了两个页面,写起来是多写了一点,但看着舒服,改起来也不慌。4. 样式上不再强求一致:小程序的样式我用 rpx,H5 我另外加一套媒体查询兜底。宽屏我干脆限死一个最大宽度,两边留白。
最后说几句:跨端到底值不值?
跨端框架不是骗人的,但也别信「一套代码搞定」这种话。
我现在的体感是:一套代码大概能覆盖八成场景,剩下那两成,你就得老老实实分开写、单独适配、逐个处理。如果你一开始就抱着「我一份代码全端跑」的幻想,一定会被现实敲醒。
但反过来说,能省八成也是真省。要是让我今天重来一遍,我还是会选 UniApp。因为对我这种下班撸代码的独狼来说,能少写一半算一半,剩下的坑,慢慢填就是了。
比技术更重要的其实是心态。以前我总想着「这套方案能不能一劳永逸」,现在我更愿意接受「没有一劳永逸的方案,只有能不能持续维护的代码」。跨端也好、单端也好,最后拼的都是你愿不愿意把那些脏活累活一遍一遍地做下去。
下一篇预告
第 17 篇:《首屏加载优化——我是怎么把白屏从 3 秒压到 1 秒以内的》
一开始我根本没把首屏当回事,直到有人反馈小程序打开卡。我才开始老老实实做优化:包体积、分包、预加载、骨架屏……我把做过的优化和效果都列出来。
—— 一个还在下班写代码、决定把 3 个月成果交出来的老程序员
夜雨聆风