微信小程序转 Taro

v1.2.0 开始支持此功能

Taro 可以将你的原生微信小程序应用转换为 Taro 代码,进而你可以通过 taro build 的命令将 Taro 代码转换为对应平台的代码,或者对转换后的 Taro 代码用 React 的方式进行二次开发。

微信原生小程序转 Taro 的操作非常简单,首先必须安装使用 npm i -g @tarojs/cli 安装 Taro 命令行工具,其次在命令行中定位到小程序项目的根目录,根目录中运行:

  1. $ taro convert

即可完成转换。转换后的代码保存在根目录下的 taroConvert 文件夹下。你需要定位到 taroConvert 目录执行 npm install 命令之后就可以使用 taro build 命令编译到对应平台的代码。

二次开发

假设已有一个转换后文件如下:

  1. import { View } from '@tarojs/components'
  2. import Taro from '@tarojs/taro'
  3. import withWeapp from '@tarojs/with-weapp'
  4. import './index.scss'
  5. var app = Taro.getApp()
  6. @withWeapp('Page')
  7. class _C extends Taro.Component {
  8. state = {}
  9. componentWillMount(e) {
  10. var orderId = e.id
  11. this.data.orderId = orderId
  12. }
  13. componentDidShow() {
  14. var that = this
  15. Taro.request({
  16. url: 'https://api.it120.cc/' + app.globalData.subDomain + '/order/detail',
  17. data: {
  18. token: Taro.getStorageSync('token'),
  19. id: that.data.orderId
  20. },
  21. success: res => {
  22. Taro.hideLoading()
  23. if (res.data.code != 0) {
  24. Taro.showModal({
  25. title: '错误',
  26. content: res.data.msg,
  27. showCancel: false
  28. })
  29. return
  30. }
  31. that.setData({
  32. orderDetail: res.data.data,
  33. logisticsTraces: res.data.data.logisticsTraces.reverse()
  34. })
  35. }
  36. })
  37. }
  38. config = {
  39. navigationBarTitleText: '物流详情'
  40. }
  41. render() {
  42. const {
  43. orderDetail: orderDetail,
  44. logisticsTraces: logisticsTraces
  45. } = this.state
  46. return (
  47. <View className="container">
  48. <View className="top-sec">
  49. <View className="a-row">
  50. <View className="label">物流单号</View>
  51. <View className="text">{orderDetail.logistics.trackingNumber}</View>
  52. </View>
  53. <View className="a-row">
  54. <View className="label">物流公司</View>
  55. <View className="text">{orderDetail.logistics.shipperName}</View>
  56. </View>
  57. </View>
  58. <View className="sec-wrap">
  59. <View className="details-info">
  60. <View className="line-box" />
  61. {logisticsTraces.map((item, index) => {
  62. return (
  63. <View className="a-row" key={index}>
  64. <View className="dot">
  65. <View
  66. className="active-dot"
  67. hidden={index == 0 ? false : true}
  68. >
  69. <View className="yuan-red" />
  70. </View>
  71. <View
  72. className="default-dot"
  73. hidden={index == 0 ? true : false}
  74. />
  75. </View>
  76. <View className="info">
  77. <View className="date-box">{item.AcceptTime}</View>
  78. <View className="text">{item.AcceptStation}</View>
  79. </View>
  80. </View>
  81. )
  82. })}
  83. </View>
  84. </View>
  85. </View>
  86. )
  87. }
  88. }
  89. export default _C

它看起来就像普通的 Taro 组件,最重要的区别就在于 @withWeapp() 这个装饰器,你可以将它理解为转换代码的运行时,@withWeapp() 会增加一些原来 Taro 没有方法和属性,例如:

this.setData

转换后的 this.setData 的 API 相当于小程序的 this.setData 的 polyfill,他和 this.setState 最大的区别就在于,this.setData 之后 data 的数据是同步更新,而渲染是异步更新,而 setState 两者都是异步的。

this.data 和 this.properties

this.datathis.properties 相当于 Taro 的 this.statethis.props 的 alias,当它们的数据更新时,对应的 stateprops 也会同步更新。

生命周期

Taro 会将原始文件的生命周期钩子函数转换为 Taro 的生命周期,完整对应关系如下:

Page.onLoadcomponentWillMount
onShowcomponentDidShow
onHidecomponentDidHide
onReadycomponentDidMount
onUnloadcomponentWillUnmount
onErrorcomponentDidCatchError
App.onLaunchcomponentWillMount
Component.createdcomponentWillMount
attachedcomponentDidMount
readycomponentDidMount
detachedcomponentWillUnmount
moved保留

常见问题

在小程序 IDE 显示 _createData 错误

这个错误通常是由于原始代码的初始 data 中没有对应的数据,后来通过 this.setData 补充数据造成的。在 Taro 中推荐不管是 state(data) 还是 properties(props) 都要设置一个默认值。你可以在类构造器或修改原始代码提供一个默认值解决这个问题,这也应该是编写代码的最佳实践。

转换 wxParse 报错不存在文件

这是由于 wxParse 的源码使用了一个不存在的 template 声明造成的。你可以修改 wxParse 的源码文件 wxParse.wxml 134 行到 207 行:

  1. <!--循环模版-->
  2. <template name="wxParse1">
  3. <!--<template is="wxParse1" data="{{item}}" />-->
  4. <!--判断是否是标签节点-->
  5. <block wx:if="{{item.node == 'element'}}">
  6. <block wx:if="{{item.tag == 'button'}}">
  7. <button type="default" size="mini">
  8. <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
  9. <template is="wxParse0" data="{{item}}" />
  10. </block>
  11. </button>
  12. </block>
  13. <!--li类型-->
  14. <block wx:elif="{{item.tag == 'li'}}">
  15. <view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
  16. <view class="{{item.classStr}} wxParse-li-inner">
  17. <view class="{{item.classStr}} wxParse-li-text">
  18. <view class="{{item.classStr}} wxParse-li-circle"></view>
  19. </view>
  20. <view class="{{item.classStr}} wxParse-li-text">
  21. <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
  22. <template is="wxParse0" data="{{item}}" />
  23. </block>
  24. </view>
  25. </view>
  26. </view>
  27. </block>
  28. <!--video类型-->
  29. <block wx:elif="{{item.tag == 'video'}}">
  30. <template is="wxParseVideo" data="{{item}}" />
  31. </block>
  32. <!--img类型-->
  33. <block wx:elif="{{item.tag == 'img'}}">
  34. <template is="wxParseImg" data="{{item}}" />
  35. </block>
  36. <!--a类型-->
  37. <block wx:elif="{{item.tag == 'a'}}">
  38. <view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
  39. <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
  40. <template is="wxParse0" data="{{item}}" />
  41. </block>
  42. </view>
  43. </block>
  44. <block wx:elif="{{item.tag == 'table'}}">
  45. <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
  46. <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
  47. <template is="wxParse0" data="{{item}}" />
  48. </block>
  49. </view>
  50. </block>
  51. <block wx:elif="{{item.tag == 'br'}}">
  52. <template is="WxParseBr"></template>
  53. </block>
  54. <!--其他块级标签-->
  55. <block wx:elif="{{item.tagType == 'block'}}">
  56. <view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
  57. <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
  58. <template is="wxParse0" data="{{item}}" />
  59. </block>
  60. </view>
  61. </block>
  62. <!--内联标签-->
  63. <view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
  64. <block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
  65. <template is="wxParse0" data="{{item}}" />
  66. </block>
  67. </view>
  68. </block>
  69. <!--判断是否是文本节点-->
  70. <block wx:elif="{{item.node == 'text'}}">
  71. <!--如果是,直接进行-->
  72. <template is="WxEmojiView" data="{{item}}" />
  73. </block>
  74. </template>

<template name="wxParse1"> 的模板内所有 <template is="wxParse2" data="{{item}}" /> 修改为 <template is="wxParse0" data="{{item}}" /> 再运行 taro convert 即可。这样修改之后还会取消原来 wxParse 只能处理 11 级 HTML 嵌套的问题,理论上内存不爆栈可以处理无限级 HTML 嵌套。

不支持 relations 和 Behavior

目前转换暂只支持转换 PageComponentApp 三种构造器创造的小程序组件实例。 relationsBehavior 在其他许多小程序端中还没有对应的实现,我们认为实现这两个功能意义不大。

转换 wepy 文件不成功

目前只能支持转换使用原生微信小程序开发的应用。