趣店 FED 团队选用 Taro 框架开发小程序的经验分享

2024-06-07
来源:网络整理

前言

我所在的公司趣店FED从去年10月份开始使用Taro框架开发小程序(当时的版本是1.1.0-beta.4),至今已经上线了两个微信小程序和两个支付宝小程序。

选择Taro的原因一方面是其可以解决微信小程序原生开发的痛点,另一方面团队也有多端统一开发的需求,而Taro无疑是当时最好的支持。另外其还满足个人和团队整体的技术栈,可以显著降低团队的学习成本。

可以说Taro对于小程序和H5的支持不错,网上也有很多例子可以查看,但在支持程度上,披露的项目均未适配RN:

这种情况可以理解,毕竟多终端统一起来很难,需要准确把握终端之间的差异,做出合理的选择。Taro 虽然是为多终端设计的,但其重心在小程序端,并没有对多终端做一定的开发约束,所以上手困难也是正常的。笔者曾在 2018 iWeb 峰会-厦门站分享过《多终端统一开发实践》,提到了使用 Taro 开发 RN 端的坑点和大致思路,并付诸实践。

结合趣店FED近半年来的实践经验,我们开发了第一个Taro三端统一应用:taro-(高仿网易严选微信小程序),来探讨一下本文的重点:Taro开发多端应用的正确做法。

相关代码已开源:/js-/t....

在线预览

小程序端已支持微信小程序、支付宝小程序,但无法提供线上版本,请在本地运行代码。

H5、RN端均可在线预览(直接调用网易严选接口,若想体验登录、购物车功能请使用网易邮箱账号登录):

小程序H5-访问链接

请在本地运行代码

博览会

以下是操作截图:

主页、类别详情、添加到购物车、个人

风格管理

样式管理是多端开发首要的挑战,因为对样式的支持和一般的 Web 样式差别很大。前文提到的未适配 RN 的多端项目,大多都是因为样式而失败,使用了大量 RN 不支持的样式。这种情况下再去兼容 RN 就等于重写页面,恐怕是无能为力的。这也是本文要强调的,我们需要把握多端开发的正确姿势。

从样式上看,H5 最灵活,小程序次之,RN 最弱。统一多端样式就是为了齐头并进,即用 RN 的约束来管理样式,同时兼顾小程序的局限性。核心可以总结为三点:

使用弹性布局

在进一步讨论之前,重要的是要了解影响样式方案的 RN 方面的主要差异:

使用 Flex 布局不仅仅是因为 RN 的 View 标签有默认样式:flex;flex-:,更重要的是 Flex 可以解决重影空白问题:

// View 标签高度不会是 100px,图片下方会有几像素空白,称为幽灵空白 <Image src={...} style={{ height: '100px' }} View>

常规的解决方案是在View标签上设置font-size/line-:0,或者:-等,但是RN中不支持这些,给View标签设置:flex才是唯一可靠的解决方案。

再说了,Flex 布局能力这么强大,何乐而不为呢?只是要注意,RN 中 View 标签默认的主轴方向是 。如果不把其他的改成和 RN 一致的话,那么需要在所有使用 :flex 的地方明确声明主轴方向。

基于BEM的写作风格

RN 其实只支持一种声明样式的方式,就是声明属性:

height: '100%' }}

这也意味着 Taro 仅支持 RN 端的选择器(最终会被编译成对象字面量)。BEM() 在这里起到了作用:

避免风格冲突(RN和小程序风格独立,H5不独立) 自解释、语义化

比如一个列表每行有两个元素,每行的最后一个元素有特定的样式,用伪元素选择器很容易实现:nth-​​(even),但在RN中需要自己计算:

{list.map((item, index) => ( <View className={classNames('block__element', index % 2 === 1 && 'block__element--even' )} /> )}

基于BEM编写样式而不依赖于其他选择器可能会使代码稍微复杂一些,但也可以确保它在多个终端上运行而不会出现任何支持问题。

使用属性覆盖组件样式

小程序和 RN 在页面和组件之间传递样式时都存在问题:

// 目前 Taro RN 端还未实现往组件传递 className 对应样式 'my-style' /> // CompA,样式不生效 this.props.compClass} />

虽然上述场景小程序可以通过组件外部样式来实现,但是官网文档强调“在同一个节点上使用普通样式类与外部样式类时,两个类的优先级是未定义的,所以最好避免这种情况”;使用全局样式也是可以的,但是这样的样式难以维护。

那么,传递并覆盖组件样式就成为唯一的选择,需要注意的是,样式文件会被编译并兼容多端,但方法需要在运行时兼容:

background: '#fff' })} /> // 简单演示,如 RN 不支持 background,需改成 background-color function postcss(style) { const { background, ...restStyle } = style const newStyle = {} if (background) { newStyle.backgroundColor = background } return { ...newStyle, ...restStyle } }

常见开发框架_程序开发框架技术_小程序多端开发框架开发原理

从这个角度来看, - 可能是多端开发最好的样式解决方案,但 Taro 暂时还不支持。另外微信小程序官方文档中提到“尽量避免将静态样式写入 ,以免影响渲染速度”。将所有样式写入属性中可能不太靠谱,但如果只用于覆盖少量样式,可能影响不大。

风格兼容性

即使掌握了上述的风格管理思想,多端风格差异的问题依然存在。例如:这种风格在 RN 侧会报错。Taro 提供了解决方案:

.text { /*postcss-pxtransform rn eject enable*/ white-space: nowrap; /*postcss-pxtransform rn eject disable*/ }

但是项目中的问题不止一个,全部这样写不太美观,可以使用Sass稍微封装一下:

@mixin eject($attr, $value) { /*postcss-pxtransform rn eject enable*/ #{$attr}: $value; /*postcss-pxtransform rn eject disable*/ } .text { @includes eject(white-space, nowrap); }

Sass虽然不能解决差异性,但是对于一些两端不兼容的样式,通过Sass统一是比较合理的方式,代码也比较美观,易于维护。

终端能力差异

相比于风格,终端能力的差异就没那么明显了,终端之间的差异是客观存在的,更何况iOS上的RN和上的RN已经存在不少差异了。

针对端能力的差异,要么改变实现思路,比如RN端不支持Taro.(get/set),就用/+Taro.(get/set)来实现,要么就用环境判断的方法。

Taro 提供了 .env. 用于环境判断,大部分细小的差异都可以这样解决:

function foo() { if (process.env.TARO_ENV === 'weapp') { // 微信小程序逻辑 } if (process.env.TARO_ENV === 'h5') { // H5 逻辑 } if (process.env.TARO_ENV === 'rn') { // RN 逻辑 } }

这个也是考验开发者的封装能力,一般建议把这些不同逻辑的判断统一起来,比如封装在src/中,对外提供一致的接口,业务页面中尽量不要混入过多的判断。

对于简单的环境判断无法处理的问题,只能使用原生开发,例如 Taro 暂不支持 RN 组件,需要自己用原生 RN 实现(注:Taro v1.2.16 已经支持):

import { WebView } from '@tarojs/components' // Taro 已开启 Tree shaking,可以放心引入各端组件 // 未使用的内容在编译时会被自动去掉 import WebViewRN from './rn' export default class extends Component { render() {、 {/* 根据环境进行调用 */} return process.env.TARO_ENV === 'rn' ? : } } // 原生 RN 页面,从 react-native 引入 WebView import Taro, { Component } from '@tarojs/taro' import { WebView } from 'react-native' export default class WebViewRN extends Component { render() { return } }

.env. 是在编译时而不是运行时进行处理的,而且 Taro 引入了 Tree,意味着如果 RN 没有编译的话,上面的原生代码就不会被打包,保证编译到其他终端时不会引入不支持的内容(否则在非 RN 终端中引用会报错)。

现在可以引入原生页面了,多端的问题基本可以解决。

但是上述方式会让代码中填满大量的 .env.,效果并不理想。3 月中旬发布的 Taro v1.2.17 提供了更加便捷的跨平台开发方式,更加适合多端兼容:

// 例如原先有一个组件 test.js // 若需要分别实现 h5 端、RN 端的组件,则将相应组件命名为: // test.h5.js,test.rn.js // 这样只需要引入 test,Taro 会根据环境自动引入相应的组件 // 就不需要写 process.env.TARO_ENV 的判断了 import Test from '../../components/test' render() { // 例如编译 h5 时,实际引入的组件是 test.h5.js // 组件只需要遵循对外接口统一即可 return <Test data={data} onClick={onClick} /> }

Taro RN 的陷阱

Taro RN 方面还存在不少小问题,在本次项目开发过程中也解决了几个 Bug:

除此之外,还有几个问题,由于时间限制,还没有提交解决。我们暂时会绕过它们,但高适应性问题仍然值得一提。

小程序、H5 可以使用 rpx/em 实现适配,但 RN 的自适应解决方案比较复杂,一般需要获取宽高然后进行换算。Taro 提供的 () 可以解决这个问题,但是在编译 RN 样式文件时并没有考虑到这一点,即 : 会被编译成 :50 而不是 :Taro.(100),无法适配不同的屏幕尺寸。

所以目前Taro RN端还不容易实现自适应,要么对非百分比宽高使用 +Taro.(),要么自己写个脚本处理编译后的样式文件。

该已提2204,有需要的可以关注解决进度。

Taro H5侧面隐患

Taro 对 H5 的支持尚可,如果只是想要实现小程序兼容 H5,还是建议使用 BEM 写样式 + 属性来覆盖组件样式,这样可以有效避免小程序中自定义组件的诸多限制,但又不必像 RN 那样受 CSS 特性限制,伪元素使用起来毫无压力。

另外小程序和RN不存在跨域问题,H5有,可以通过 解决。另外编译打包后的静态资源文件名是固定的,建议改成hash值,方便缓存管理。这些配置在项目中的src/下都可以找到,就不再赘述了。

H5端的坑更多集中在内置组件不全,以及终端能力不足上。毕竟Taro的设计是借鉴微信小程序,弥补其他端的差异,编译成小程序时直接使用小程序内置组件,而H5端需要一套功能等同的完整内置组件,可想而知Taro要实现一致性,细节的复杂程度有多高。

例如,Taro.() 还不支持,暂时只能通过以下方式规避:

if (process.env.TARO_ENV === 'h5') { Taro.navigateBack({ delta: Taro.getCurrentPages().length - 1 }) setTimeout(() => { Taro.redirectTo({ url }) }, 100) }

幸好官方计划在下一个1.3版本对H5进行重构,届时这个问题将会得到解决。

其他

要实现多端统一,细节太多,无法一一提及。上述实现思路虽然简单,但也隐含着各个终端之间差异的挣扎与取舍。本文只列举最基本的几点,以说明Taro多端开发的核心思路。

本项目代码并未过度封装以方便阅读,也实现了足够多的样式细节以供试错。具体的试错点和注意事项均在代码中以注释 //TODO(Taro 暂不支持)和 //NOTE(开发技巧、注意事项)标注。更多内容等你去实践和体会。

ps:Taro 的版本更新速度相当快,本项目最早是基于 v1.2.11 开发的,2 月 19 日发布时使用的是 v1.2.13,到 3 月 11 日已经更新到 v1.2.17,作者会根据版本变化,尝试对本文的内容和代码进行相应的完善。

总结

正如前言中所说,Taro 虽然是为多端设计的,但其重点是小程序端,目前对 RN 端的支持并不是特别理想。不过,在充分了解多端差异,掌握正确的多端开发姿势(尤其是样式管理方面,避免项目完成后兼容性发生较大变化)之后,一定可以在简单的项目上大显身手。

两周开发一个小程序并不稀奇,但花了两周时间才完成小程序端(微信、支付宝、百度等),以及H5和端,后续更新只需要改一个地方,产出和维护效率真的惊人,这大概就是“一次搞定”的魅力吧(虽然在前端领域,开发成“一次搞定”很容易)

相信随着小程序热​​度的不断上升,会有更多优秀的开源框架和解决方案涌现出来。我们不倾向于重新发明轮子,而更关心如何基于已有的解决方案更好地开发多端应用。对前端感兴趣的朋友,不妨加入我们,一起努力#(驻地厦门)

本文由趣店FED出品,首发于趣店技术学院;项目开源地址:/js-/t….

分享