taro + typescript 多端工程化开发的探索---by喜茶前端团队
-
喜茶前端团队的技术栈为 react + mobx + typescript
目前正在对 喜茶go 小程序进行重构,并同时支持微信小程序、支付宝小程序、H5、IOS、Android。
综合以上 我们选了 taro + typescript
以下,是团队在多端工程化开发中的一些探索/解决方案
一、让 taro 支持多环境(dev、test、pre、prod)、同时支持多端开发
1.package.json 的 scripts 增加变量 TARO_APP_TYPE={type} TARO_APP_API={env}
修改 ./config/index.js 的 outputRoot 让其根据不同的 type + env 生成不同目录 支持多端开发
并将 TARO_APP_TYPE、TARO_APP_API 加入到 env 中const { TARO_APP_TYPE, TARO_APP_API, NODE_ENV } = process.env const outputRoot = `dist/${NODE_ENV === 'development' ? 'tmp' : 'build'}-${TARO_APP_TYPE}-${TARO_APP_API}` const config = { env: { TARO_APP_TYPE: '"' + TARO_APP_TYPE + '"', TARO_APP_API: '"' + TARO_APP_API + '"' }, outputRoot }
2.setProjectConfig.js 根据不同的 TARO_APP_API 动态设置 微信小程序 project.config.json 中的 appId
根目录创建文件 setProjectConfig.js
var fs = require('fs') const config = {} const testAppId = 'test' const prodAppId = 'prod' switch (process.env.TARO_APP_API) { case 'dev': config.appid = testAppId break case 'test': config.appid = testAppId break case 'pre': config.appid = prodAppId break case 'prod': config.appid = prodAppId break default: config.appid = testAppId } function writeJson() { fs.readFile('./project.config.json', function (err, data) { if (err) { return console.error(err) } var person = { ...JSON.parse(data.toString()), ...config } var str = JSON.stringify(person) fs.writeFile('./project.config.json', str, (writeFileErr) => { if (writeFileErr) { console.error(writeFileErr); } else { console.log('----------修改成功-------------'); } }) }) } writeJson()
package.json 添加 script
"set:dev": "cross-env TARO_APP_API=dev node ./setProjectConfig.js", "set:test": "cross-env TARO_APP_API=test node ./setProjectConfig.js", "set:pre": "cross-env TARO_APP_API=pre node ./setProjectConfig.js", "set:prod": "cross-env TARO_APP_API=prod node ./setProjectConfig.js",
相关 weapp script 增加 "npm run set:${env} 例如:
"build:weapp-api-dev": "npm run set:dev && cross-env a taro build --type weapp",
3.不同环境的配置信息 例如 不同环境对应不同的 api 地址
创建 ./src/config.ts
if (process.env.TARO_APP_API === 'dev') { hosts.api = '' } else if (process.env.TARO_APP_API === 'test') { hosts.api = '' } else if (process.env.TARO_APP_API === 'pre') { hosts.api = '' } else if (process.env.TARO_APP_API === 'prod') { hosts.api = '' } else { hosts.api = '' }
二、静态资源的处理
除了 tabbar 必须本地引用的图片,我们将其他资源统一上传到七牛云管理,最大化的减少小程序包的大小,以及提升各端的编译速度
1.配置本地 nginx
创建 ./nginx/local.conf 文件
server { listen 80; server_name local-cdn-taro.heytea.com; root /www/heytea/taro/src/assets; error_log off; access_log off; error_page 405 =200 $uri; }
- ./src/config.ts 增加 hosts.cdn 域名配置, 并通过 二级目录做版本控制
const cdnDir = '/taro/v1' if (process.env.TARO_APP_API === 'dev') { hosts.cdn = 'http://local-cdn-taro.heytea.com' } else if (process.env.TARO_APP_API === 'test') { hosts.api = 'https://static.heytea.com/' + cdnDir } else if (process.env.TARO_APP_API === 'pre') { hosts.api = 'https://static.heytea.com/' + cdnDir } else if (process.env.TARO_APP_API === 'prod') { hosts.api = 'https://static.heytea.com/' + cdnDir } else { hosts.api = 'https://static.heytea.com/' + cdnDir }
- 长文本(协议、说明)内容的CDN化
创建 ./src/assets/json/{name}.json 文件
{ "code": 0, "data": [ { "val": "喜茶隐私保护政策" } ] }
然后,在相应的页面发起 api 请求 hosts.cdn+'/json/+'{name}.json' 获取数据
- 静态资源自动同步到七牛云
1). 通过 gitlab 的 CI 来执行脚本上传资源 (推荐 较安全)
2). 本地创建七牛云上传脚本
三、adapter 多端功能的适配
1.taro 提供非常多的 Taro.{fnName} 解决了大部分多端功能的适配,但在一些业务场景或者一些比较细的功能,还存在一些没适配到
chooseImage 在 微信小程序跟支付宝小程序表现不一致,RN中不支持
tabBar 的相关操作 目前也只支持到 微信小程序- 有些功能是需要业务上的妥协来进行适配的
location 的相关 在H5上会因为多浏览器表现不一致 导致更大适配成本,通过 adapter 留个口,方便以后扩展适配
- 为了更好统一的处理错误,也需要将一些统一适配
路由跳转
ajax 请求 通知 adapter 统一处理配置、参数、错误、规范返回格式- 全局统一使用 await/async 为了尽量不在 page 里做 try/catch 对部分功能进行二次适配
例如 storage.ts
import Taro from '@tarojs/taro' export function setStorage(key: string, value: any | string): Promise<boolean> { return new Promise(resolve => { Taro.setStorage({ key, data: value, }).then(() => resolve(true), () => resolve(false)) }) } export function getStorage(key: string): Promise<any> { return new Promise(resolve => { Taro.getStorage({ key }).then((res: any) => resolve(res.data), () => resolve(null)) }) } export function getStorageInfo(): Promise<any> { return new Promise(resolve => { Taro.getStorageInfo().then((res: any) => resolve(res), () => resolve(null)) }) } export function removeStorage(key: string): Promise<boolean> { return new Promise(resolve => { Taro.removeStorage({ key }).then(() => resolve(true), () => resolve(false)) }) } export function clearStorage(): void { Taro.clearStorage() }
四、错误上报、性能监控、业务埋点
- 通过 adapter 接入 GrowingIO SDK 实现基础功能
- 在各个 adapter/components 中 实现自定义错误信息上报,尽可能的不在 page 里处理上报错误
router adapter 中 记录并上报路由跳转错误
ajax adapter 中 记录并上报 接口 相关错误
在 img、link component 中记录加载/跳转错误信息
...- 由于 喜茶go 小程序每天百万级别pv 我们选择可配置的 百分比 上报性能相关数据
按随机百分比Math.random()<{x} 统计上报页面首屏、白屏、接口、渲染时间
五、利用扫普通链接二维码打开小程序 和 中转页 实现多端统一二维码
实现一个二维码 在多端扫描 打开对应的小程序或者H5或者跳转到APP指定页面
- 微信/支付宝小程序后台分配配置
二维码地址 https://{env}-m.heytea.com/ 小程序路径 pages/transfer/index 并把 前缀占用规则 改为 占用
- 编写 ./src/pages/transfer/index.tsx 文件
@inject('user') @observer class Index extends Component<IProps> { static defaultProps = { user: store.user } config: Config = { navigationBarTitleText: 'loading…' } fail = (e) => { Taro.showToast(e.errMsg) Taro.switchTab({ url: routes.menu }) } componentDidMount() { const { setUrlQuery } = this.props.user const { q = '' } = this.$router.params const path = decodeURIComponent(q).replace(/http[s]?:\/\/[^/?]+/, '') if (path) { let [page, query = ''] = path.split('?') if (page === '' || page === '/') { page = routes.menu } query ? query += '?' : '' const url = page + query if (routerBar.indexOf(page) >= 0) { setUrlQuery(query) // 为了解决 wx.switchTab: url 不支持 queryString Taro.switchTab({ url, fail: this.fail }) } else { Taro.redirectTo({ url, fail: this.fail }) } } else { Taro.switchTab({ url: routes.menu }) } } render() { return ( <View> <Text>loading……</Text> </View> ) } }
六 统一 webview 页的处理
创建 ./src/pages/webview/index.tsx 页,通过适配器模式 统一处理 webview 相关业务,例如H5直接跳转页面,小程序APP,利用 WebView 组件渲染,并对 IOS、安卓 做相关处理优化
本文转载已经取得作者授权
作者:HeyteaTech
链接:https://www.jianshu.com/p/d785b9b9f141
来源:简书
简书著作权归作者所有,任何形式的转载都请联系原作者获得授权并注明出处。