从 react 到

react native

如何安装

create-react-app

npm install -g create-react-app
create-react-app my-app

cd my-app
npm start

react

npm install -g yarn react-native-cli
react-native init AwesomeProject

cd AwesomeProject
react-native run-android

react-native-cli

* 集成到原生应用略有不同

react native

 如何调试

react

 

  1. react 浏览器插件

  2. redux 浏览器插件

  1. 摇一摇,在调试页面F12

  2. 0.43以上版本能下载react-devtools,可以查看app结果和props、styles,基本交互同react浏览器插件

react native

样式

- 写法

react

className

render() {
  return <span className="menu navigation-menu">Menu</span>
}

inline style

var divStyle = {
  color: 'white',
  backgroundImage: 'url(' + imgUrl + ')',
  WebkitTransition: 'all', // note the capital 'W' here
  msTransition: 'all' // 'ms' is the only lowercase vendor prefix
};

ReactDOM.render(<div style={divStyle}>Hello World!</div>, mountNode);

react native

创建一个样式表:

var styles = StyleSheet.create({
  container: {
    borderRadius: 4,
    borderWidth: 0.5,
    borderColor: '#d6d7da',
  },
  title: {
    fontSize: 19,
    fontWeight: 'bold',
  },
  activeTitle: {
    color: 'red',
  },
});

使用一个样式表:

<View style={styles.container}>
  <Text style={[styles.title, this.props.isActive && styles.activeTitle]} />
</View>

并不是真正的css样式

样式

- 布局

react

传统的css布局

position

static, relative, absolute, fixed...

display

inline, block, inline-block, table, flex, grid ...

灵活

react native

够用

  1. 每个组件自带relative特性,可以声明为absolute
  2. 主要使用flex进行布局

  associatedKeysScrollWrap: {
    position: 'absolute',
    width: WindowSize.WIDTH,
    height: WindowSize.HEIGHT - HEADER_HEIGHT,
    top: HEADER_HEIGHT,
    left: 0,
  },
  associatedKeysContainer: {
    flex:1,
    paddingBottom: WindowSize.HEIGHT / 2
  },
  associatedKeysItem: {
    width: WindowSize.WIDTH,
    height: 56,
    flexDirection: 'row',
    justifyContent: 'flex-start',
    alignItems: 'center',
  },
  searchIcon: {
    marginLeft: 20,
  },
  associatedKeysText: {
    marginLeft: 20,
    fontSize: 16,
    color: 'rgba(33,33,33,0.9)'
  },

图片

react


<img className="preview-image" 
     src={source} 
     onClick={this.loadImage.bind(this)}/>

react

react native

引用网络资源

<Image style={styles.image} 
       source={{uri: this.imageUrl}}/>

引用本地资源

<Image resizeMode='stretch'
       source={{uri: 'icon'}}
       style={styles.icon} />

混合App(一部分UI使用React Native,而另一部分使用原生),可以使用已经打包到App中的图片资源(以拖拽的方式放置在Xcode的asset类目中,或是放置在Android的drawable目录里)。注意此时只使用文件名,不带路径也不带后缀

引用本地资源 - 2

<Image source={require('./img/icon.png')} />

会将资源打包进整个bundle

在web中,如果我们不规定图片的尺寸,图片在加载前不占空间,之后按自身大小显示

但是在rn中,为了防止这种情况造成的视图抖动,官方规定需要提前赋予图片尺寸(除非是require方式加载图片),否则图片无法显示。

 

在rn中,特殊图片格式,如gif, webp, 动态webp等需要额外添加支持。

注意

图片

背景图片

react

使用css

.container { //以图片中心为基准进行原比例缩放,以覆盖整个背景
    background-image: url("../images/bg.webp");
    background-position: 50% 50%;
    background-size: cover; // contain | 100% | 100% 100% | ...
    background-repeat: no-repeat;
}

灵活

react native

Image做为父组件

<Image style={styles.bg}
       source={{uri: this.bgImg}}
       resizeMode={'stretch'}> // 'contain' | 'cover'
  <Text style={styles.text}>
   {text}
  </Text>
</Image>

不太兼容

Image做为子组件,撑满容器

container: {
  width: w,
  height: h,
},
bg: {
  width: w,
  height: h,
  position: 'absolute',
  borderRadius: 1,
  left: 0,
},

官方实现

<ImageBackground>

勉强够用

事件

点击事件

react

任何组件都可以绑定onClick事件

* 移动端有必要的话需要处理300ms延时

react

需要用TouchableXX组件接收点击事件

react native

  1. TouchableOpacity:透明度改变
  2. TouchableHighlight:背景颜色和透明度改变
  3. TouchableNativeFeedback:在安卓上,有涟漪效果
  4. TouchableWithoutFeedback:不显示任何视觉反馈
<TouchableOpacity 
  style={styles.button} 
  onPress={this.handlePress.bind(this)}>
  {children}
</TouchableOpacity>

* 也可以用PanResponder类指定触摸事件接收的对象

由于处理点击事件和点击时的交互都会占用js资源,会导致交互样式掉帧。

需要在处理事件时添加时延(一般为300ms),并在componenWillUnmount时清除


handleClick() {
  this.clickTimeout = setTimeout(() => {
    // do something
  }, 300)

注意

事件

back键

react

1. 跟客户端配合;

2. 使用History API

react

提供原生支持

BackAndroid.exitApp();

react native

// mount时添加监听
BackAndroid.addEventListener('hardwareBackPress', this.handleBackButton);

// unmount时移除监听
BackAndroid.removeEventListener('hardwareBackPress', this.handleBackButton);

handleBackButton() {
  // ...
  if(flag){ 
    return true; // 暂时不退出
  }
  return false; // 立刻退出
}

跟原生的交互

react

基于webview

对于Android调用JS代码的方法有2种:
1. 通过WebView的loadUrl()
2. 通过WebView的evaluateJavascript()

对于JS调用Android代码的方法有3种:
1. 通过WebView的addJavascriptInterface()进行对象映射
2. 通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url
3. 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息

window.webviewSelected = () => { // 客户端调用js代码,在离开该webview时执行
  AHandler.foo();// 通过js调用客户端代码
};

react native

1. RCTDeviceEventEmitter 事件

import { NativeModules } from 'react-native';
const NativeModule = NativeModules.NativeModule;

NativeModule.foo(
  JSON.stringify(data),
  () => {// do something},
  (err) => { // do something}
);
const { DeviceEventEmitter} = ReactNative;

DeviceEventEmitter.addListener('event_name', this.handleEvent);

2和3相似。

客户端继承ReactContextBaseJavaModule创建模块,按照rn指定的步骤注册并添加模块;

前端引用react-native的NativeModules模块,继而调用客户端定义的模块。

import { NativeModules } from 'react-native';
const NativeClientInfo = NativeModules.NativeModule;

NativeModule.foo('xx').then(v => {
  bar(v);
});

2. Callback 回调

3.  Promise 回调

其他

动画

Animated

LayoutAnimation

只能控制width, height,在flex布局中自动呈现动态效果

用起来很爽的组件

ListView => FlatView

From React to React Native

By Esther Y

From React to React Native

  • 842