最近看到了一篇关于ReactNative常见问题的博客。正好前段时间遇到过其中一些问题,感觉总结的挺好的,转载过来以备用。^_^
一般问题
RN和React.js是一个东西吗?
RN和React.js共用一些抽象层,但具体有很多差异,且目标平台不同:
RN目前只能开发iOS/Android App,而React.js用于开发web页面。
RN有哪些已经上架的案例?
官方最近推出了一个由爱好者自行提交的showcase页面
RN可以在windows下开发吗?
对于iOS开发,可以通过虚拟机等方式,但很麻烦也不推荐。做iOS开发,迟早你都需要一台Mac电脑。
对于Android开发,理论上没问题。但由于FB的员工基本都用mac,没有怎么管过windows兼容性,
所以目前的版本可能在windows上会遇到一些问题。
RN所支持的最低iOS和Android版本?
Android >= 4.1 (API 16)
iOS >= 7.0
RN和cordova/phonegap是一个东西吗?
不一样。RN不是一个webview(但包含了webview组件),不能直接复用web页面代码。
RN的性能接近原生,超过cordova/phonegap。
可以使用现有的js库吗?
由于RN理论上更接近nodejs的运行环境,所以对nodejs的库兼容更好一些。
浏览器端的js库,涉及到DOM、BOM、CSS等功能的模块无法使用,因为RN的环境中没有这些东西。
可以使用现有的objc/swift/java库吗?
可以,但需要参照这篇和这篇进行修改。
环境搭建与编译问题
创建新项目,react native init xxx命令长时间无响应,或报错shasum check failed
由于众所周知的网络原因,react-native命令行从npm官方源拖代码时会遇上麻烦。请将npm仓库源替换为国内镜像:
npm config set registry https://registry.npm.taobao.org
npm config set disturl https://npm.taobao.org/dist
另,执行init时切记不要在前面加上sudo(否则新项目的目录所有者会变为root而不是当前用户,
导致一系列权限问题,请使用chown修复)。
react-native中文网提供了完整的绿色纯净新项目包。
完整打包全部iOS和Android的第三方依赖,只要环境配置正确,无需科学上网漫长等待,即可直接运行。
报错EACCES: permission denied, open ‘Users/你的用户名/.babel.json’
执行如下命令:
sudo chown 你的用户名 ~/.babel.json
如何升级RN版本?
请用编辑器打开项目目录中的package.json,找到类似下面的一行配置
"react-native": "0.13.0",
将其改为要升级的版本号,如“0.15.0-rc”(当然要先确定这个版本已经发布到npm上了,
如果配置中有^或~之类的符号,可以参考这篇文章来了解其含义。)。
然后在当前目录的命令行中执行npm i
如果提示权限错误则在前面加上sudo(windows下不需要).
npm i执行完毕且成功不报错之后,在项目目录中运行
react-native upgrade
对于0.14以下版本升级0.14的情况,还需要额外手动处理一下。
报错:EMFILE, too many open files ‘……’
请检查node版本(4.0以上),以及是否安装了watchman(目前只有Mac能装这个)
报错:SyntaxError: Use of const in strict mode
请检查node版本(4.0以上)。
Windows下报错:ERROR Watcher took too long to load Try running watchman version
from your terminal
启动packager.js时多传一个--nonPersistent参数。
报错:Invariant Violation:Application XXXX has not been registered.
请确保index.ios.js中的
AppRegistry.registerComponent('项目名',() => ...);
与appDelegate.m中的RCTRootView*rootView = [[RCTRootViewalloc]initWithBundleURL:jsCodeLocation
moduleName:@"项目名" launchOptions:launchOptions];
或是MainActivity.java中的
mReactRootView.startReactApplication(mReactInstanceManager, "项目名", null);都保持一致。
应该使用什么IDE开发?
虽然常用的JS编辑器很多,但由于RN大量使用jsx和es6语法,
目前只有sublime text(通过插件)和webstorm(10以上版本)提供了良好的支持。
笔者推荐webstorm,因为它有更完善的语法提示和补全。
另外虽然主要的 业务逻辑是使用js开发,但仍然要依赖于原生的编译/调试环境,所以你还需要同时运行Xcode(iOS)或Android Studio(android)等
开发与调试问题
如何开启调试功能?
点击iOS模拟器顶部的Hardware菜单,选择Shake Gesture(对应真机摇一摇),会自动弹出如下图的菜单。
安卓模拟器则是点击菜单键,真机上没有菜单键的,摇一摇即可。
选 择Debug in Chrome即会启动Chrome作为运行和调试环境(注意此时JS引擎为Chrome的V8,与iOS真机的javascriptCore引擎存在一些 差异)。
选择Inspect Element即可以像调试网页元素一样查看布局元素的样式,但比较简陋。
React Devtools插件可装可不装,它只用来查看布局,不影响调试,且在目前的版本(>0.13)中还无法正常加载。
调试模式下报错:Runtime is not ready. Make sure…或是socket closed.
有时Chrome进程会失去响应,
可以尝试手动将Chrome的React Native Debugger标签切换到前台再Reload模拟器页面。
使用ListView时报错:Sticky header index 0 was outside the range {…}
看起来是个数组越界错误,但多数情况下是由于ListView的子组件渲染错误(如套数据时没有检查undefined等)引起,
而非ListView本身的问题。
ListView的数据到底应该怎么配?
下面是我曾经写的一个小例子:
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
ListView,
Dimensions,
Image,
ActivityIndicator,
RefreshControl,
TouchableHighlight,
} from 'react-native';
let Icon = require('react-native-vector-icons/FontAwesome');
let Mock = require('mockjs');
let width = Dimensions.get('window').width;
let height = Dimensions.get('window').height;
class Detail extends Component {
back(){
console.log('123')
this.props.navigator.pop();
}
render(){
return(
<View>
<View style={{marginTop:40}}>
<Text onPress={this.back.bind(this)}>返回</Text>
</View>
<View style={{marginTop:40}}>
<Text>{this.props.title}</Text>
</View>
</View>
)
}
}
class Index extends Component{
constructor(props){
super(props)
ds = new ListView.DataSource({rowHasChanged:(row1,row2)=>row1 != row2});
this.state={
dataSource:ds.cloneWithRows([]),
num:0,
all:1,
list:[],
hasMoreData:true,
isRefreshing:false,
};
this._fetch();
}
_fetch(params){
let argu = params;
if(this.state.num >= this.state.all){
this.setState({
hasMoreData:false
})
return
}
fetch('http://rap.taobao.org/mockjs/8607/all/haha?reqParam=all')
.then((response)=>(response.json()))
.then((responseJson)=>{
let data = Mock.mock(responseJson);
if(data.success){
if(argu == 'refresh'){
this.state.list=data.data.concat(this.state.list);
this.setState({
num:this.state.num + data.data.length,
all:data.totle,
dataSource:ds.cloneWithRows(this.state.list)
})
}else{
this.state.list=this.state.list.concat(data.data);
this.setState({
num:this.state.num + data.data.length,
all:data.totle,
dataSource:ds.cloneWithRows(this.state.list)
})
}
}
})
console.log(this.state.list);
}
_renderFooter(){
if(this.state.hasMoreData){
return(
<View style={{justifyContent:'center',alignItems:'center'}}>
<ActivityIndicator
animating={this.state.animating}
style={[styles.centering, {height: 50}]}
/>
<Text style={{color:'#999'}}>努力加载中……</Text>
</View>
)
}else{
return(
<View style={{justifyContent:'center',alignItems:'center',marginTop:20}}>
<Text style={{color:'#999'}}>没有更多数据了</Text>
</View>
)
}
}
switch(rowData){
this.props.navigator.push({
name:'详情',
component:Detail,
params:rowData,
})
}
_render(rowData){
return(
<TouchableHighlight onPress={this.switch.bind(this,rowData)}>
<View style={{height:height*0.14,borderBottomWidth:1,flexDirection:'row',justifyContent:'space-between'}}>
<View style={styles.pic}>
<Image
style={{width:width*0.3,height:height*0.125}}
source={{uri:rowData.pic}}
/>
</View>
<View style={styles.content}>
<View style={styles.title}>
<Text style={{lineHeight:height*0.04,fontSize:12,color:'green'}}>{rowData.title}</Text>
</View>
<View style={styles.info}>
<View style={styles.num}>
<Icon
style={styles.icon}
name="heart"/>
<Text style={{color:'gray',fontSize:8}}>{rowData.num}人在学习</Text>
</View>
<View style={styles.time}>
<Icon
style={styles.icon}
name="heart"/>
<Text style={{color:'gray',fontSize:8}}>{rowData.time}小时</Text>
</View>
</View>
</View>
</View>
</TouchableHighlight>
)
}
render(){
return(
<View style={{height:600,marginTop:20}}>
<View>
<Text>课程列表</Text>
</View>
<ListView
dataSource={this.state.dataSource}
renderRow={this._render.bind(this)}
enableEmptySections={true}
onEndReached={this._fetch.bind(this)} //滚动条触底时调用模块
onEndReachedThreshold={20} //距离底部多少执行触底函数
renderFooter={this._renderFooter.bind(this)} //上拉加载
showsVerticalScrollIndicator={false}
automaticallyAdjustContentInsets={false}
refreshControl={
this.state.hasMoreData?
<RefreshControl
refreshing={this.state.isRefreshing}
onRefresh={this._fetch.bind(this,'refresh')}
tintColor="#ff0000"
title="正在刷新……"
titleColor="#00ff00"
colors={['#ff0000', '#00ff00', '#0000ff']}
progressBackgroundColor="#ffff00"
/>
:
<RefreshControl
refreshing={this.state.isRefreshing}
onRefresh={this._fetch.bind(this,'refresh')}
tintColor="#ff0000"
title="没有更多数据了,休息一会吧。"
titleColor="#00ff00"
colors={['#ff0000', '#00ff00', '#0000ff']}
progressBackgroundColor="#ffff00"
/>
} //下拉刷新
/>
</View>
)
}
}
const styles = StyleSheet.create({
pic:{
width:width*0.35,
height:height*0.125,
padding:5
},
content:{
width:width*0.7,
height:height*0.125,
},
title:{
marginTop:10,
height:height*0.04
},
info:{
marginTop:20,
flexDirection:'row'
},
num:{
flexDirection:'row'
},
time:{
flexDirection:'row',
marginLeft:20
},
icon:{
color:'lightgreen',
marginRight:5
}
})
module.exports = Index;
使用Image时报错:You are trying to render the global Image variable as a React element. You probably forgot to require Image.
由于React的Image组件和全局的Image对象重名,所以使用Image组件时一定要记得在文件开头正确引入React的Image组件。
在使用Navigator的同时使用ListView或ScrollView,后两者的头部会多出一些空间。
将automaticallyAdjustContentInsets属性设为{false}.
有一些示例代码中有奇怪的问号,比如function foo(x:?string),代表什么意思?
这是通过一个名为flow的外部工具为javascript加上强类型检查的功能,不影响编译和运行。
报错:Adjacent JSX elements must be wrapped in an enclosing tag.
render方法中必须只能包含一个根元素
报错:Invariant Violation: onlyChild must be passed a children with exactly one child
一般是Touchable开头的几个组件,如果没有子元素或者指定多个并列子元素都会报错。
如何获取服务器端数据/可以使用Ajax吗?
可以用ajax,以及大部分现有的ajax库,而且不受浏览器跨域限制。
官方推荐用更简单的fetch api来替代传统的ajax.但目前还无法在Chrome中直接观测请求的详情。
有些事情无论你有多着急或者多害怕,我们现在都不能往前冲,冲出去也没有用,飞不起来的,现在你只需要静静地,等风来。 ———— 《等风来》