taro + typescript 多端工程化开发的探索

喜茶前端团队的技术栈为 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
}
  1. 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",
  1. 不同环境的配置信息 例如 不同环境对应不同的 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;
}
  1. ./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
}
  1. 长文本(协议、说明)内容的CDN化

创建 ./src/assets/json/{name}.json 文件

{
  "code": 0,
  "data": [
    {
      "val": "喜茶隐私保护政策"
    }
  ]
}

然后,在相应的页面发起 api 请求 hosts.cdn+'/json/+'{name}.json' 获取数据

  1. 静态资源自动同步到七牛云
    1). 通过 gitlab 的 CI 来执行脚本上传资源 (推荐 较安全)
    2). 本地创建七牛云上传脚本

三、adapter 多端功能的适配

  1. taro 提供非常多的 Taro.{fnName} 解决了大部分多端功能的适配,但在一些业务场景或者一些比较细的功能,还存在一些没适配到

chooseImage 在 微信小程序跟支付宝小程序表现不一致,RN中不支持
tabBar 的相关操作 目前也只支持到 微信小程序

  1. 有些功能是需要业务上的妥协来进行适配的

location 的相关 在H5上会因为多浏览器表现不一致 导致更大适配成本,通过 adapter 留个口,方便以后扩展适配

  1. 为了更好统一的处理错误,也需要将一些统一适配

路由跳转
ajax 请求 通知 adapter 统一处理配置、参数、错误、规范返回格式

  1. 全局统一使用 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()
}

四、错误上报、性能监控、业务埋点

  1. 通过 adapter 接入 GrowingIO SDK 实现基础功能
  2. 在各个 adapter/components 中 实现自定义错误信息上报,尽可能的不在 page 里处理上报错误

router adapter 中 记录并上报路由跳转错误
ajax adapter 中 记录并上报 接口 相关错误
在 img、link component 中记录加载/跳转错误信息
...

  1. 由于 喜茶go 小程序每天百万级别pv 我们选择可配置的 百分比 上报性能相关数据

按随机百分比Math.random()<{x} 统计上报页面首屏、白屏、接口、渲染时间

五、利用扫普通链接二维码打开小程序 和 中转页 实现多端统一二维码

实现一个二维码 在多端扫描 打开对应的小程序或者H5或者跳转到APP指定页面

  1. 微信/支付宝小程序后台分配配置

二维码地址 https://{env}-m.heytea.com/ 小程序路径 pages/transfer/index 并把 前缀占用规则 改为 占用

  1. 编写 ./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、安卓 做相关处理优化

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容