Taro 中的 React Native 开发总结
Taro 中的 React Native 开发总结
tags
Taro
Android
iOS
ReactNative
React
JavaScript
date
Aug 5, 2020
source
status

notion image
notion image

构建

  • RN编译不监听修改是近期代码有错。
  • RN编译后的代码文件位于project/rn_temp/下(RN编译后的源码可作参考)。
  • 小程序编译后的代码文件位于project/dist/weapp/下。
  • 进行RN的编译时,确保React Native Debugger.exe程序在运行,用于传输js数据。不开启则代码改动不会生效。
  • 编译小程序:npm run dev:weapp重新编译小程序后,Taro不会编译sitemap文件,微信开发工具会报找不到sitemap文件的错误,需要手动将src文件夹内的sitemap放入dist小程序编译根目录中。
  • 编译RN:npm run dev:rn,编译完成后会开启一个node命令行窗口,通过8081端口用于传输JS代码,此时应该确保React Native Debugger.exe程序在运行。
  • 打包RN(debug):flush-android-debug.batreact-native run-andoridnpx react-native run-android
  • 打包RN(release):flush-android-release.batreact-native run-andorid --variant=releasenpx react-native run-android --variant=release 需要卸载原模拟器里的debug版本,否则自动安装不上,或者可以去project/android/app/build/outputs/apk/release/目录里拿出APK手动拖到模拟器窗口里或者放到真机上安装。
  • 打包debug版的APK安装后,进行调试前需要按下Ctrl+M,点击Debug JS Remotely开启远程JS调试,用于接收React Native Debugger.exe传输过来的,编译后的JS代码。不开启则代码改动不会生效。
  • 确保以上操作都正确,如果还是没更新代码或者连接React Native Debugger.exe的话,去开启模拟器的WIFI。
  • 打包本地图片,新增本地图片时或者初次打包release时需要(确保assets目录存在,不存在则新建文件夹,敲命令前先编译RN):
react-native bundle --platform android --dev false \
--entry-file rn_temp/index.js --bundle-output \
android/app/src/main/assets/index.android.bundle \
--assets-dest android/app/src/main/res/
运行完毕后需要进入android/app/src/main/res/目录内删除svg格式图片,否则APK打包报错
  • 使用他人的node_modules包(尤其是不同系统平台的),在使用npm命令前可能需要重置node-sass包:npm rebuild node-sass
💡
以上命令都在项目根目录运行
  • iOS Debug 构建需要另外在project/ios/目录下安装pod install(每次插件更新都要运行pod install)
💡
pod install报插件红字错误的话,删除Podfile.lock文件后再重试
  • iOS Debug 构建流程:
      1. npm run dev:rn
      1. 在Xcode中导入project/ios/编译后的ios项目目录
      1. 在Xcode左上角菜单栏preference -> accounts -> 添加Apple ID开发账号 -> 主界面选择模拟器 -> 构建运行
  • iOS APP内呼出开发控制菜单:command + control + Z,或者顶部菜单栏:设备(Device)-> 摇一摇(Shake)
  • iOS 构建成功后无法在模拟器中启动APP:检查Xcode设置中开发者账号是否需要重新登录验证。
  • iOSpod install构建错误:
    • CocoaPods could not find compatible versions for pod "Folly": In snapshot (Podfile.lock):
      删除project/ios/Podfile.lock文件后重新运行pod install
  • iOS真机调试:参考连接
    • 将手机连接电脑,配置打包签名为Automatically,Team选择个人开发者账户,Bundle Identifier会联网检查,如果不处在同一Team下进行开发,则需要修改为不重复的包名。运行终端模拟器选择刚才连接的手机,点击运行将自动安装到手机上,第一次安装后需要到设置中开启信任模式,否则iOS将禁止调试app运行。运行后的app摇一摇可以呼出控制菜单,如果想要连上React Native Debugger.exe,需要开启Debug JS Remotely,并通过WiFi连接到和电脑的同一局域网,并设置连接地址IP等操作。
notion image
  • Xcode构建崩溃:顶部菜单栏->Product->Clean Build Folder 清空构建缓存

代码

  • this.setState()是异步执行的,可以通过回调参数来保证已经set完毕:
this.setState({
  a: 'a' 
}, () => { console.log(this.state.a) })
  • Taro判断小程序平台和RN平台表达式:
process.env.TARO_ENV === 'rn'
process.env.TARO_ENV === 'weapp'
  • RN判断Android平台和iOS平台表达式(需导包):
import {Platform} from 'react-native'

...

Platform.OS === 'android' Platform.OS === 'ios'
  • 只有页面才有componentDidShow生命周期方法,组件是没有的。可以在页面里的componentDidShow方法中,通过持有组件引用来调用组件内方法,来达到componentDidShow的效果。
  • Taro中的props的值是不能改变的需要转换成state来改变:
static defaultProps = { show: false }
state = { sShow: false }

constructor(props) {
  super(props);
  this.state.sShow = props.show;
}
  • Taro中的e.stopPropagation();冒泡处理在RN不适用需要加上判断

标签

  • 任何标签都是大写字母开头,否则RN报红屏错误。
  • 所有文本都需要使用<Text></Text>标签包裹,否则RN红屏闪退,且colorfont-size等文本属性只针对<Text>标签生效。
  • RN限制文本行数:
<Text numberOfLines={1} />
  • 给组件设置引用,可以通过引用调用组件内部方法:
this.Modal && this.Modal.show()

...

{isShow && (
  <Block>
    <Image />
  </Block>
)}
<Modal ref={(c) => this.Modal = c}>
调用前需要判断组件是否已经渲染完毕,否则会报undefined
  • 当导入的RN组件与Taro组件重名时,可以起一个别名一起使用:
import {Image} from '@tarojs/components'
import {Image as RnImage} from 'react-native'
  • 视图渲染中所用用到RN相关组件的地方都需要加平台判断,组件引用时也需要加平台判断,否则小程序编译会报缺少npm包AccessibilityInfo,开始安装...:(import则会在编译时自动忽略不属于当前平台的组件)
import {Image as RnImage} from 'react-native'

...

componentDidMount = () => {
  if (process.env.TARO_ENV === 'rn') {
    this.RnImage.getSize()
  }
}

...

{process.env.TARO_ENV === 'rn' && (
  <RnImage ref={(c) => this.RnImage = c} source={{uri: data}} />
)}
  • 点击事件等事件传递,需要使用bind()方法:
onClickItem = (a, b, c, event) => {
  ...
}

...

<View onClick={this.onClickItem.bind(this, a, b, c)} />
  • onClick中的this参数一定要写在首位,后面按照方法参数顺序排列。方法中的event对应标签中的this,用来传递事件对象,要放在末尾。不再使用小程序的data-*方式传参。
  • 不能在<Image></Image>标签内写子布局,且<Image />标签无法直接设置点击事件,需要使用<View></View>标签包裹起来,然后给<View />标签点击事件。
  • <View />标签只包裹<Image />时,需要给定和<Image />标签一样的宽高值,否则会导致弹性布局的垂直居中属性无效。
  • ( )中只有一个组件标签时,需要使用<Block></Block>将标签包裹起来,否则会有语法错误,需要导包:
import {Block, Image} from '@tarojs/components'

...

{isShow && (
  <Block>
    <Image />
  </Block>
)}
  • Taro自带的<Image />组件在RN里会有圆角问题,必要时可使用RN的<Image />组件:
import {Block, Image} from '@tarojs/components'
import {Image as RnImage} from 'react-native'

...

{process.env.TARO_ENV != 'rn' ? (
  <Block>
    <Image
      className="img"
      src={'https://avatar.png'}
    />
  </Block>
) : (
  <Block>
    <RnImage
      className="img"
      source={{uri: 'https://avatar.png'}}
    />
  </Block>
)}

...

.img {
  width: 100px;
  height: 100px;
  border-radius: 100px;
}
RN的组件在用法上完全和RN原生用法一致
  • 在RN上使用Taro的<Image />组件时,必须设置固定宽高,否则第一次进入APP时将不会显示,且不能带有mode属性。
  • iOS文本下划线颜色设置:
<Text style={{textDecorationColor: "white"}}>
  文本
</Text>

样式

  • 优先使用flex布局。
  • 行内样式必须加{ }包裹,否则不生效(RN需要{{ }}):
<View style={'height: 100rpx'}>
  • Taro只会对写在CSS文件里的px单位自动做缩放,不会对行内样式中的px单位自动做缩放。
  • 在行内样式中,小程序平台需要使用rpx来保证小程序各尺寸缩放,RN平台则不需要写单位(RN行内样式单位默认为点:point),小程序的rpx和RN的point比例为2倍,css文件中的px是行内样式中point的2倍:
<View style={process.env.TARO_ENV === 'rn'
  ? {height: 100}
  : 'height: 200rpx;'
}>
  • 很多CSS样式在RN上不支持,有些可能会导致闪退,在CSS上做平台判断:
/* #ifdef rn */
...
/* #endif */

...

/* #ifndef rn */
...
/* #endif */
  • RNposition样式只支持relativeabsolute,不支持blockfixed等(会闪退)。
  • border-radius不支持百分比。圆头像图片框圆角值需要设置成宽高值的一半,过大或过小都会在iOS上导致变形。
  • display: flex;小程序默认方向为水平,RN默认方向为竖直
  • iOS中border-bottom样式失效修复:不能直接在<Text>上加border-bottom样式,需要在外面套一层<View>,给<View>border-bottomborder-radius同理。
💡
<Text>只适用文本相关样式,文本无关样式需要给<View>加上并包裹<Text>达到效果。
  • text-align: center;不生效:
      1. 检查样式是否已设置在<Text>标签上。
      1. 检查<Text>是否有宽度。
      1. 检查<Text>的父组件是否已设置display: flex;

插件

  • 安装流程
    •  
下载插件:  npm install <插件名>@<版本号>  --save
示例:npm install react-native-image-crop-picker@0.23.1 --save
 
React Native 0.60.x之前的版本需要手动link,
链接到RN项目:react-native link <插件名>
示例:react-native link react-native-image-crop-picker

iOS端则需要进入/ios目录中运行一遍命令:pod install
以上命令都在项目根目录运行
  • 提示
      1. 有一些新版本RN的插件会使用androidX包导入,比如react-native-view-shot插件,使用低版本RN需要修改导入包路径为android.support.xxx,具体路径需要根据包名而定。
      1. 选择插件时在Readme中,注意插件有没有对RN版本有要求,根据RN版本下载,最好选择都支持IOS/Android
版本要求
react-native
react-native-image-resizer
1.2.0
path: "/data/user/0/com.package.project/cache/1594624681380.JPEG"
DecodeUtil.java:图片转换为Bitmap格式,原插件会在转换为YUV格式,提高二维码识别速度。但是getYUV420sp()在转换中抛出异常,找不到错误位置,判断是数组越界,换为直接使用Bitmap格式。
  • CameraRoll RN原生组件,CameraRoll模块提供了访问/保存本地相册的功能。
  • react-native-webview
    • 低版本RN自带的WebView插件,高版本已分离,需要手动npm install
      针对iOS进行修改:react-native-webview的iOS端实现禁止在注入js内使用window.postMessage()方法,可以在project/node_modules/react-native/React/Views/RCTWebView.m中进行修改:
BOOL postMessageIsNative = [
  [webView stringByEvaluatingJavaScriptFromString:testPostMessageNative]
  isEqualToString:@"true"
];
// package-edit:解除webview内嵌js中window.postMessage
//if (!postMessageIsNative) {
//  RCTLogError(@"Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined");
//}
// package-edit:解除webview内嵌js中window.postMessage
#endif

ReactNative-iOS原生代码二改

//project/node_modules/react-native/React/Base/RCTModuleMethod.mm
static BOOL RCTParseUnused(const char **input)
{
  return RCTReadString(input, "__unused") ||
         // package-edit:Xcode11以上编译报错修复
         RCTReadString(input, "__attribute__((__unused__))") ||
         // package-edit:Xcode11以上编译报错修复
         RCTReadString(input, "__attribute__((unused))");
}
  • 修复ios中文输入法:
// package-edit:修复中文输入法 RN 0.57版本
if (_onChange && backedTextInputView.markedTextRange == nil) {
  _onChange(@{
     @"text": self.attributedText.string,
     @"target": self.reactTag,
     @"eventCount": @(_nativeEventCount),
  });
}
参考链接