前言

很久之前就想研究React Native了,但是一直没有落地的机会,我一直认为一个技术要有落地的场景才有研究的意义,刚好最近迎来了新的APP,在可控的范围内,我们可以在上面做任何想做的事情。

PS:任何新技术的尝鲜都一定要控制在自己能控制的范围内,失败了会有可替换方案,不要引起不可逆的问题,这样会给团队造成灾难性的后果。

事实上,RN经过一段时间发展,已经有充分数量的人尝试过了,就我身边就有几批,褒贬也不一:

① 做UI快

② 还是有很多限制,不如原生Native

③ 入门简单,能让前端快速开发App

④ iOS&Android大部分代码通用

⑤ code-push能做热更新,但是用不好依旧坑

......

在得到一些信息后,可以看出,要用RN高效率的做出比较不错的App是有可能的,单看投入度与最初设计是否合理,而且现在关于React Native的各种文档是相当丰富的,所以这个阶段想切入RN可能是一个不错的选择。

带着试试不吃亏的想法,我们开始今天的学习,这里是一些比较优质的学习资料:

https://github.com/reactnativecn/react-native-guide

准备阶段

搭建开发环境

http://reactnative.cn/docs/0.36/getting-started.html

官方的例子其实写的很好了,我照着官方的例子能很好的跑起来,大家自己去看看吧

这里在运行时候要注意一下,我因为开启了翻墙工具,一运行就crash,这里猜测是翻(科学上网法)墙工具对localhost造成了影响,导致不能读取文件,这个可能涉及到RN底层实现,我们后面深入了再去做研究,这里关闭翻墙工具即可。

然后第二个问题,是http的图片展示不出来,这里折腾了很久,却发现后面的章节有了说明,app默认只支持https的链接,这里大家改下配置即可:

https://segmentfault.com/a/1190000002933776

RN中的js使用的是比较新的语法,这里也需要大家进行学习,我学习的感受是ES6提供了很多语法糖,但是有几个东西也要注意。

Class

JavaScript之前的继承全部是复写原型链模拟实现的,作为大型应用框架,继承是必不可少的,所以ES6直接将这块API化了,我这里写一个简单的demo:

 class Animal {
constructor(name) {
this.name = name;
}
say() {
console.log('我是' + this.name);
}
} class Person extends Animal {
say() {
console.log('我是人类');
super.say();
}
} var p = new Person('叶小钗')
p.say();
 /*
我是人类
我是叶小钗
*/

Module

我们一般使用requireJS解决模块化的问题,在ES6里面提出了Module功能在官方解决了模块化的问题,这里优缺点不是我们考虑的重点,简单了解下语法,两个核心为:

① export

② import

ES6以一个文件为单位,一个文件可以多个输出,这里以RN的一个引用为例:

 import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import styles from './static/style/styles.js';

可以假想,这里一定会有一个react文件,并且里面可能是这个样式的:

export default class React......

expoet class Component ......

PS:一个文件只能有一个default

输出的default一定会出现,不使用大括号包裹,其余部分随意输出,这里与我们使用require或有不同,需要注意。

应该说ES6提供了很多语法糖,有人喜欢,有人不喜欢,这个看爱好使用吧,比如=>箭头函数。了解了以上关系,再配合ES6的一些文档,基本可以写RN的代码了。

城市列表

拆分目录

这里,我们做一个城市列表,真实的访问接口获取数据,然后渲染页面,看看做出来效果如何。

首先,我们初始化一个RN项目:

react-native init Citylist

然后使用Xcode打开iOS中的项目,编译运行:

 import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native'; export default class Citylist extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit index.ios.js
</Text>
<Text style={styles.instructions}>
Press Cmd+R to reload,{'\n'}
Cmd+D or shake for dev menu
</Text>
</View>
);
}
} const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
}); AppRegistry.registerComponent('Citylist', () => Citylist);

这里除了index.io.js,其他文件我们不必理睬,我们做的第一件事情是,将样式文件剥离出去,新建static文件夹,加入images和style,将样式文件移入style文件,新建style.js:

 import {
StyleSheet
} from 'react-native'; export let styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});

然后首页代码再做一些改动:

 import React, { Component } from 'react';
import {
AppRegistry,
Text,
View
} from 'react-native'; import {styles} from './static/style/style'; export default class Citylist extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit index.ios.js
</Text>
<Text style={styles.instructions}>
Press Cmd+R to reload,{'\n'}
Cmd+D or shake for dev menu
</Text>
</View>
);
}
} AppRegistry.registerComponent('Citylist', () => Citylist);

PS:这里有一个箭头函数

 () => Citylist
//===>
function () {
return Citylist;
}

静态资源剥离后,我们先不处理其它的,我们来做数据请求。

数据请求

RN虽然内置了ajax库,但是一般推荐使用RN自带的Fetch,最简单的使用是:

fetch('https://mywebsite.com/mydata.json')

PS:我们在学习RN的时候,也是在学习神马方式是适合的,或者说熟悉使用合适的组件

请求一个接口是这样写的(使用promise):

 fetch('https://apikuai.baidu.com/city/getstartcitys')
.then((response) => response.json())
.then((jsonData) => {
console.log(jsonData);
})
.catch((e) => {
console.log(e)
})

这里打开调试环境一看,输出了我们要的数据:

一般来说,我们需要对数据请求应该封装为一个底层库,这里只做一些简单改造,真实项目不会这样做:

 export default class Citylist extends Component {
getdata(url, suc, err) {
return fetch(url)
.then((response) => response.json())
.then((data) => {
if(data.errno == 0) {
suc && suc(data.data)
}
})
.catch((e) => {
console.log(e)
});
}
render() { this.getdata('https://apikuai.baidu.com/city/getstartcitys', function(data) {
s = ''
}); return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit index.ios.js
</Text>
<Text style={styles.instructions}>
Press Cmd+R to reload,{'\n'}
Cmd+D or shake for dev menu
</Text>
</View>
);
}
}

PS:这里的使用不一定正确,先完成功能再改进吧

我们取所有的城市cities,这个数据量很大,有1000多条记录,也可以测试下拖动效率了,这里为类加入构造函数,因为列表是可变的,暂时把列表数据归为state(react也不是太熟,如果有问题后续优化,先完成功能):

 constructor(props) {
super(props);
this.state = {
cities: []
};
}
 var scope = this;
//本来想使用箭头函数的,但是了解不太清楚,demo时候暂时这样吧
this.getdata('https://apikuai.baidu.com/city/getstartcitys', function(data) {
scope.state.citys = data.cities;
});

列表渲染

处理了数据问题后,我们开始做列表渲染,这里使用ListView组件,这个组件用以显示一个垂直滚动列表,适合长列表,两个必须的属性是datasource和renderRow:

dataSource:列表数据源

renderRow:逐个解析数据源中的数据,然后返回一个设定好的格式来渲染

简单书写代码:

 export default class Citylist extends Component {
constructor(props) {
super(props); this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
cities: this.ds.cloneWithRows([
{cnname
:
"文山壮族苗族自治州",
enname
:
"wszzmzzzz",
extflag
:
"1",
flag
:
"0",
name
:
"wenshanzhuangzumiaozuzizhizhou",
parentid
:
"28",
regionid
:
"177",
shortname
:
"文山",
shownname
:
"文山",
type
:
"2"},{cnname
:
"文山壮族苗族自治州",
enname
:
"wszzmzzzz",
extflag
:
"1",
flag
:
"0",
name
:
"wenshanzhuangzumiaozuzizhizhou",
parentid
:
"28",
regionid
:
"177",
shortname
:
"文山",
shownname
:
"文山",
type
:
"2"},{cnname
:
"文山壮族苗族自治州",
enname
:
"wszzmzzzz",
extflag
:
"1",
flag
:
"0",
name
:
"wenshanzhuangzumiaozuzizhizhou",
parentid
:
"28",
regionid
:
"177",
shortname
:
"文山",
shownname
:
"文山",
type
:
"2"}
])
};
}
getdata(url, suc, err) {
return fetch(url)
.then((response) => response.json())
.then((data) => {
if(data.errno == 0) {
suc && suc(data.data)
}
})
.catch((e) => {
console.log(e)
});
}
componentDidMount(){
var scope = this;
this.getdata('https://apikuai.baidu.com/city/getstartcitys', function(data) {
console.log(data) scope.setState({
cities: scope.ds.cloneWithRows(data.cities)
});
//scope.state.citys = data.cities;
//this.getdata('https://apikuai.baidu.com/city/getstartcitys', (data) => {
// this.state.citys = data.cities;
//});
});
}
render() {
return (
<View style={styles.container}>
<ListView
dataSource={this.state.cities}
renderRow={(rowData) => <Text>{rowData.cnname}</Text>}
/>
</View>
);
}
}

然后就这样了,虽然丑是丑点,但是还能看嘛,这里我们先不去理睬城市的排序,也不做搜索功能,我们先把布局处理下,他的丑陋我已经受不了了

样式处理

现在我们开始处理这段样式:

 import React, { Component } from 'react';
import {
AppRegistry,
ListView,
Text,
View
} from 'react-native'; import {styles} from './static/style/style'; export default class Citylist extends Component {
constructor(props) {
super(props); this.ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
cities: this.ds.cloneWithRows([])
};
}
getdata(url, suc, err) {
return fetch(url)
.then((response) => response.json())
.then((data) => {
if(data.errno == 0) {
suc && suc(data.data)
}
})
.catch((e) => {
console.log(e)
});
}
componentDidMount(){
var scope = this;
this.getdata('https://apikuai.baidu.com/city/getstartcitys', function(data) {
console.log(data) scope.setState({
cities: scope.ds.cloneWithRows(data.cities)
});
//scope.state.citys = data.cities;
//this.getdata('https://apikuai.baidu.com/city/getstartcitys', (data) => {
// this.state.citys = data.cities;
//});
});
}
render() {
return (
<View style={styles.container}>
<ListView style={styles.listView} enableEmptySections={true}
dataSource={this.state.cities}
renderRow={(rowData) =>
<View style={styles.listItem} >
<Text>{rowData.cnname}</Text>
</View>
}
/>
</View>
);
}
} AppRegistry.registerComponent('Citylist', () => Citylist);
 import {
StyleSheet
} from 'react-native'; export let styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
},
listView: {
marginTop: 30,
flex: 1,
borderBottomColor:'#CCCCCC',//cell的分割线
borderBottomWidth:1
},
listItem: {
paddingTop: 15,
paddingBottom: 15,
paddingLeft: 10,
flexDirection:'row',
borderBottomColor:'#CCCCCC',//cell的分割线
borderBottomWidth:1
}
});

事件绑定

然后,我们再为每行数据加上点击事件,这里也做简单一点,打印出当前行的值即可:

   onPressAction(data){
alert(data.cnname)
}
render() {
return (
<View style={styles.container}>
<ListView style={styles.listView} enableEmptySections={true}
dataSource={this.state.cities}
renderRow={(rowData) =>
<View style={styles.listItem} >
<Text onPress={() => this.onPressAction(rowData)}>{rowData.cnname}</Text>
</View>
}
/>
</View>
);
}

PS:我尼玛,这个RN的学习,很大程度就是一个个API或者组件的熟悉,这块不熟悉的话,做起来恼火的很

我这里开始想给Text设置边框,怎么都不能成功,后面就加了一层View就好了,这种小细节需要多摸索,这个是最终的结构:

结语

作为一个demo的话,这个例子基本可以说明一些问题的,虽然我本意是想做成这个样子的:)

通过这个例子,我们简单的学习了下RN的开发模式,做出来的感受是Facebook很强大,做了一个体系性的东西,举个例子来说(个人感受

之前我们做Hybrid的时候Header是Native提供的,大概做法是这样的:

 //Native以及前端框架会对特殊tagname的标识做默认回调,如果未注册callback,或者点击回调callback无返回则执行默认方法
//back前端默认执行History.back,如果不可后退则回到指定URL,Native如果检测到不可后退则返回Naive大首页
//home前端默认返回指定URL,Native默认返回大首页
this.header.set({
left: [
{
//如果出现value字段,则默认不使用icon
tagname: 'back',
value: '回退',
//如果设置了lefticon或者righticon,则显示icon
//native会提供常用图标icon映射,如果找不到,便会去当前业务频道专用目录获取图标
lefticon: 'back',
callback: function () { }
}
],
right: [
{
//默认icon为tagname,这里为icon
tagname: 'search',
callback: function () { }
},
//自定义图标
{
tagname: 'me',
//会去hotel频道存储静态header图标资源目录搜寻该图标,没有便使用默认图标
icon: 'hotel/me.png',
callback: function () { }
}
],
title: 'title',
//显示主标题,子标题的场景
title: ['title', 'subtitle'], //定制化title
title: {
value: 'title',
//标题右边图标
righticon: 'down', //也可以设置lefticon
//标题类型,默认为空,设置的话需要特殊处理
//type: 'tabs',
//点击标题时的回调,默认为空
callback: function () { }
}
});

通过这个约定,我们的Native就会生成一系列headerUI:

而RN做了什么呢,他可能是实现了一个这样的标签(或者说是语法糖):

<Header title="" right="[]" ></Header>

然后RN会自己去解析这个标签,生成上述的对象,然后生成Native的UI,这个我们其实也能做到,但是我们一个能做到,10个就不一定做得到了,RN牛的地方就牛在他提供了这么大一坨东西:

然后还有他一整套的样式体系,非常之大手笔,而通过RN的完善约定,生成了一套NativeUI,应该说来体验是非常高的,开发效率因为可以做到大部分iOS Android通用,虽然整体开发效率无法与Hybrid比肩,但绝对有其应用场景。

我们也有一些同事说了一些RN的问题,但是框架在发展,容器在优化,这些问题在某个时间点应该能解决的,总的说来,RN还是很有学习的价值,后面我可能会花很多功夫去进行落地!!!

为了汇集资源,这里引用这里的学习资源:https://github.com/reactnativecn/react-native-guide

React Native

React.js

ES6

系列教程

React Native探索系列教程

开源APP

研究源码也是一个很好的学习方式

图书

组件

由于已经有较好的组件库网站,这里就不做总结。可以直接查看如下网站,过后可能精选一部分优质组件出来 :P

工具

资源网站

业界讨论

React Native初探的更多相关文章

  1. React Native 初探

    推荐文章 React Native 简介:用 JavaScript 搭建 iOS 应用 (1) React Native 简介:用 JavaScript 搭建 iOS 应用 (2) React Nat ...

  2. React Native IOS源码初探

    原文链接 http://www.open-open.com/lib/view/open1465637638193.html 每个项目都有一个入口,然后进行初始化操作,React Native 也不例外 ...

  3. React Native For Android 架构初探

    版权声明:本文由王少鸣原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/171 来源:腾云阁 https://www.qclo ...

  4. React Native桥接器初探

    本文假设你已经有一定的React Native基础,并且想要了解React Native的JS和原生代码之间是如何交互的. React Native的工作线程 shadow queue:布局在这个线程 ...

  5. react native 学习资料整理

    入门教程 深入浅出 React Native:使用 JavaScript 构建原生应用 http://www.appcoda.com/react-native-introduction/  中文版 h ...

  6. 移动应用跨平台框架江湖将现终结者?速来参拜来自Facebook的React Native

    React Native使用初探 February 06 2015 Facebook让所有React Conf的参与人员都可以初尝React Native的源码---一个编写原生移动应用的方法.该方法 ...

  7. 最火移动端跨平台方案盘点:React Native、weex、Flutter

    1.前言 跨平台一直是老生常谈的话题,cordova.ionic.react-native.weex.kotlin-native.flutter等跨平台框架的百花齐放,颇有一股推倒原生开发者的势头. ...

  8. React Native指南汇集了各类react-native学习资源、开源App和组件

    来自:https://github.com/ele828/react-native-guide React Native指南汇集了各类react-native学习资源.开源App和组件 React-N ...

  9. React Native 常用学习链接地址

    Android Studio下载http://www.android-studio.org/ 第二章:Android Studio概述(一)http://ask.android-studio.org/ ...

随机推荐

  1. Ubuntu1604下安装Liggghts及CFDEM Coupling

    部分内容参考http://www.linuxdiyf.com/linux/16315.html LIGGGHTS是一款开源的DEM软件,来自于著名的分子动力学软件LAMMPS,目前借助于CFDEM C ...

  2. C#移动跨平台开发(2)Xamarin移动跨平台解决方案是如何工作的?

    概述 上一篇 C#移动跨平台开发(1)环境准备发布之后不久,无独有偶,微软宣布了开放.NET框架源代码并且会为Windows.Mac和Linux开发一个核心运行时(Core CLR),这也是开源的!I ...

  3. Nova PhoneGap框架 第十章 开发环境

    10.1 Visual Studio + ReSharper PhoneGap程序由HTML.CSS和JS编写而成,这3种语言的编写你都可以用记事本来完成,但显然效率比较低,出错的概率也比较大.一个强 ...

  4. 神通广大的CSS3选择器

    每个前端工程师可能每天都会写一些css,其中选择器是很主要的一部分.但是,大家可能每天写的大多是#id,.class这样的选择器,这并不稀奇,但是如果我们了解并且熟用css3为我们提供的强大并且优雅的 ...

  5. 构建Web API服务

    返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 构建动态Web API控制器 ABP可以自动地为应用层生成Web API 层.比如说我们之前创建的应用层: namespace N ...

  6. .NET基础 一步步 一幕幕[循环、逻辑语句块]

    循环.逻辑语句块   好久不写博客了,断更了好几天了,从上周五到今天,从北京到上海,跨越了1213.0公里,从一个熟悉的城市到陌生的城市,还好本人适应力比较好,还有感谢小伙伴的接风咯,一切都不是事,好 ...

  7. 随笔jade

    mixin在刚使用的时候写错,写成了minxin,然后进行命令生成,发现报错 查了蛮久,由于开发工具并没有提示错误,最后找到了这样的错误,记下来,望大家不要重复爬坑

  8. 命令行查看Windows激活信息(win7、win8、win10...)

    使用:Win+ R 组合键,打开运行命令框,复制命令,粘贴后回车. slmgr.vbs -xpr 查询Windows是否永久激活slmgr.vbs -dlv 查询到Windows的激活信息,包括:激活 ...

  9. VirtualBox 桥接上网方式的配置

    最近在搞Redis所以装了个virtualbox的ubuntu的虚拟机, redis不是在ubuntu上. 因为需要使用本机客户端访问redis服务,所以需要配置虚拟机和本地机器的双向访问,所以就用到 ...

  10. 用php生成一个excel文件(原理)

    1.我们用php来生成一个excel文档来讲述其原理: excel2007里面的文档目录组成部分为: 2.我们使用ZipArchive()方法来生成一个简易的excel文件. 使用方法: 3.代码如下 ...