中秋在家闲得无事,想着做点啥,后来想想,为啥不学学 react native。在学习 React Native 时, 需要对前端(HTML,CSS,JavaScript)知识有所了解。对于JS,可以看看阮一峰老师的《ECMAScript 6 入门》这篇文章。里面涉及很多 ES6 的新特性。我之前也是看了阮老师的文章做了一些学习笔记 ES6 学习笔记。
1、环境搭建
环境搭建中文教程,点击跳转RN中文社区 :http://reactnative.cn/docs/0.40/getting-started.html#content
社区内容讲得很详细,大家跟着操作一步一步来即可,遇到问题,直接百度。也可以看看这篇文章:React Native 填坑指南 会教你如何把坑填平。
2、React Native 基础
先来看看一个实例,这个地址源码地址:示例教程:电影列表。之所以选择这个例子,是因为它包含了几乎所有的 react native入门基础知识。
import React, { Component } from "react"; import { Image, FlatList, StyleSheet, Text, View } from "react-native"; var REQUEST_URL = "https://raw.githubusercontent.com/facebook/react-native/0.51-stable/docs/MoviesExample.json"; export default class SampleAppMovies extends Component { constructor(props) { super(props); this.state = { data: [], loaded: false }; // 在ES6中,如果在自定义的函数里使用了this关键字,则需要对其进行“绑定”操作,否则this的指向会变为空 // 像下面这行代码一样,在constructor中使用bind是其中一种做法(还有一些其他做法,如使用箭头函数等) this.fetchData = this.fetchData.bind(this); } componentDidMount() { this.fetchData(); } fetchData() { fetch(REQUEST_URL) .then(response => response.json()) .then(responseData => { // 注意,这里使用了this关键字,为了保证this在调用时仍然指向当前组件,我们需要对其进行“绑定”操作 this.setState({ data: this.state.data.concat(responseData.movies), loaded: true }); }); } render() { if (!this.state.loaded) { return this.renderLoadingView(); } return ( <FlatList data={this.state.data} renderItem={this.renderMovie} style={styles.list} /> ); } renderLoadingView() { return ( <View style={styles.container}> <Text>Loading movies...</Text> </View> ); } renderMovie({ item }) { // { item }是一种“解构”写法,请阅读ES2015语法的相关文档 // item也是FlatList中固定的参数名,请阅读FlatList的相关文档 return ( <View style={styles.container}> <Image source={{ uri: item.posters.thumbnail }} style={styles.thumbnail} /> <View style={styles.rightContainer}> <Text style={styles.title}>{item.title}</Text> <Text style={styles.year}>{item.year}</Text> </View> </View> ); } } var styles = StyleSheet.create({ container: { flex: 1, flexDirection: "row", justifyContent: "center", alignItems: "center", backgroundColor: "#F5FCFF" }, rightContainer: { flex: 1 }, title: { fontSize: 20, marginBottom: 8, textAlign: "center" }, year: { textAlign: "center" }, thumbnail: { width: 53, height: 81 }, list: { paddingTop: 20, backgroundColor: "#F5FCFF" } });
这个例子从电影数据库中取得最近正在上映的 25 部电影,并在一个 FlatList
中展示出来。
2.1 import
import React,{Component} from 'react'; // 导入‘react’文件里export的一个默认的组件,将其命名为React以及Component这个非默认组件
还有其他一些 import 的用法,具体含义如下:
-
- import defaultcomponent form 'XXX' 导入 XXX 文件中的默认组件,命名为 defaultcomponent
-
-
import {a} from 'XXX' 导入 XXX 文件中的 a 组件
-
import {a as b} from 'XXX' 导入 XXX 文件中的a组件,并将其重命名为 b
-
import * as a from 'XXX' 导入 XXX 文件中的所有组件,并将其命名为 a,调用具体组件的方式为 a.b、a.c。。。但不包含默认组件
-
2.2 var 定义变量
在组件前面,定一个变量 REQUEST_URL 用于保存请求网址,。
2.3 export 语句
模块的功能有两个关键字: export 和 import。export 用于用户自定义模块。import用于输入其他模块的功能,同时创建命名空间(namespace),防止函数名冲突。
ES6允许将独立的JS文件作为模块,也就是说,允许一个 JavaScript 脚本文件调用另一个脚本文件。最简单的模块就是一个 JS 文件,里面使用 export 关键字输出变量。
//profile.js export var firstName = "Pandora"; export var lastName = "G.Dragon"; export var year = 1973; //export还有下面这种写法,两者是等价的 var firstName = "Pandora"; var lastName = "G.Dragon"; var year = 1973; export({firstName, lastName, year});
使用 export 定义模块之后,其他 JS 文件就可以通过 import 关键字加载这个模块(文件)了。加载方式如下:
import {firstName, lastName, year} from './profile'; function setHeader(element) { element.textContent = firstName + '' + lastName; }
上面的代码片段中,使用了 import 关键字接受一个对象——用“{ }”表示。里面指定了要从其他模块中导入的变量。大括号里面的变量名必须与被导入模块对外接口的名称相同。
2.4 Class 类
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class
关键字,可以定义类。基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class
写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的“类”改写,就是下面这样。
//定义类 class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return '(' + this.x + ', ' + this.y + ')'; } }
上面代码定义了一个“类”,可以看到里面有一个 constructor
方法,这就是构造方法,而 this
关键字则代表实例对象。也就是说,ES5 的构造函数 Point
,对应 ES6 的 Point
类的构造方法。
Point 类除了构造方法,还定义了一个 toString
方法。注意,定义“类”的方法的时候,前面不需要加上 function
这个关键字,直接把函数定义放进去了就可以了。另外,方法之间不需要逗号分隔,加了会报错。
Class 之间可以通过 extends
关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
class ColorPoint extends Point {}
上面代码定义了一个 ColorPoint
类,该类通过 extends
关键字,继承了 Point
类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个 Point
类。下面,我们在 ColorPoint
内部加上代码。
class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 调用父类的constructor(x, y) this.color = color; } toString() { return this.color + ' ' + super.toString(); // 调用父类的toString() } }
上面代码中,constructor
方法和 toString
方法之中,都出现了super
关键字,它在这里表示父类的构造函数,用来新建父类的 this
对象。
子类必须在 constructor
方法中调用 super
方法,否则新建实例时会报错。这是因为子类没有自己的 this
对象,而是继承父类的 this
对象,然后对其进行加工。如果不调用super
方法,子类就得不到 this
对象。
2.5 Props(属性)
大多数组件在创建时就可以使用各种参数来进行定制。用于定制的这些参数就称为props
(属性)。
以常见的基础组件 Image
为例,在创建一个图片时,可以传入一个名为 source
的 prop 来指定要显示的图片的地址,以及使用名为 style
的 prop 来控制其尺寸。
import React, { Component } from 'react'; import { Image } from 'react-native'; export default class Bananas extends Component { render() { let pic = { uri: 'https://upload.wikimedia.org/wikipedia/commons/d/de/Bananavarieties.jpg' }; return ( <Image source={pic} style={{width: 193, height: 110}} /> ); } }
2.6 state
props 是在父组件中指定,而且一经指定,在被指定的组件的生命周期中则不再改变。 对于需要改变的数据,我们需要使用 state。
大多数组件在创建时就可以使用各种参数来进行定制。用于定制的这些参数就称为 props
(属性)。
一般来说,你需要在 constructor 中初始化 state
(译注:这是 ES6 的写法,早期的很多 ES5 的例子使用的是 getInitialState 方法来初始化 state,这一做法会逐渐被淘汰),然后在需要修改时调用 setState
方法。
提示一些初学者应该牢记的要点:
-
一切界面变化都是
状态 state 变化
-
state
的修改必须通过setState()
方法-
this.state.likes = 100; // 这样的
直接赋值修改无效!
-
setState 是一个 merge 合并操作,只修改指定属性,不影响其他属性
-
setState 是
异步
操作,修改不会马上生效
-
2.7 react native 生命周期
组件的生命周期方法对应着组件的不同生命阶段,通常我们分为三个阶段:组件初始化及挂载阶段、组件运行期阶段及组件卸载阶段。
- 初始化及挂载阶段
一、这是组件类的构造函数,通常在此初始化 state 数据模型。
constructor(props) { super(props); this.state = { //key : value }; }
二、表示组件将要加载到虚拟 DOM,在 render 方法之前执行,整个生命周期只执行一次。
componentWillMount() {
}
三、表示组件已经加载到虚拟 DOM,在 render 方法之后执行,整个生命周期只执行一次。通常在该方法中完成异步网络请求或者集成其他 JavaScript库。
componentDidMount() {
}
- 运行期阶段
一、在组件接收到其父组件传递的 props
的时候执行,参数为父组件传递的props
。在组件的整个生命周期可以多次执行。通常在此方法接收新的props
值,重新设置 state
。
componentWillReceiveProps(nextProps) { this.setState({ //key : value }); }
二、在 componentWillReceiveProps(nextProps)
执行之后立刻执行;或者在state
更改之后立刻执行。该方法包含两个参数,分别是 props
和 state
。该方法在组件的整个生命周期可以多次执行。如果该方法返回 false
,则 componentWillUpdate(nextProps, nextState)
及其之后执行的方法都不会执行,组件则不会进行重新渲染。
shouldComponentUpdate(nextProps, nextState) { return true; }
二、在 shouldComponentUpdate(nextProps, nextState)
函数执行完毕之后立刻调用,该方法包含两个参数,分别是 props
和 state
。render()
函数执行之前调用。该方法在组件的整个生命周期可以多次执行。
componentWillUpdate(nextProps, nextState) {
}
三、在 render()
方法执行之后立刻调用。该方法包含两个参数,分别是 props
和 state
。该方法在组件的整个生命周期可以多次执行。
componentDidUpdate(preProps, preState) {
}
四、render
方法用于渲染组件。在初始化阶段和运行期阶段都会执行。
render() { return( <View/> ); }
- 卸载阶段
一、在组件由虚拟 DOM 卸载的时候调用。
componentWillUnmount() {
}
2.8 fetch
fetch,说白了,就是 XMLHttpRequest 的一种替代方案。如果有人问你,除了 Ajax 获取后台数据之外,还有没有其他的替代方案?答案是还可以使用一种更优的解决方案 fetch。
到现在为止,fetch 的支持性还不是很好,但是在谷歌浏览器中已经支持了fetch。fetch 挂在在 BOM 中,可以直接在谷歌浏览器中使用。
查看 fetch 的支持情况:fetch的支持情况
fetch 方法会返回一个 Promise,这种模式可以简化异步风格的代码。如果你想了解 promise 的含义,可以参考文章 :手把手教你实现一个完整的 Promise 。带你了解 promise 的本质内核。
下面我们来写第一个 fetch 获取后端数据的例子:
// 通过fetch获取百度的错误提示页面 fetch('https://www.baidu.com/search/error.html') // 返回一个Promise对象 .then((res)=>{ return res.text() // res.text()是一个Promise对象 }) .then((res)=>{ console.log(res) // res是最终的结果 })
是不是很简单?再来看看 get 和 post 方法的使用:
// 通过fetch获取百度的错误提示页面 fetch('https://www.baidu.com/search/error.html?a=1&b=2', { // 在URL中写上传递的参数 method: 'GET' }) /* post 方法,把前面的 get 注释即可 fetch('https://www.baidu.com/search/error.html', { method: 'POST', body: new URLSearchParams([["foo", 1],["bar", 2]]).toString() // 这里是请求对象 }) */ .then((res)=>{ return res.text() }) .then((res)=>{ console.log(res) })
React Native 中已经内置了 XMLHttpRequest API (也就是俗称的 ajax)。一些基于 XMLHttpRequest 封装的第三方库也可以使用,例如 frisbee 或是 axios 等。但注意不能使用 jQuery,因为 jQuery 中还使用了很多浏览器中才有而 RN 中没有的东西(所以也不是所有 web 中的 ajax 库都可以直接使用)。
2.9 样式
style 的定义方式:
- 1、直接在 render()函数中定义
//todo 设置样式一,直接在render中定义样式 var mStyle = {color:'red',fontSize:34}; return<Text style={mStyle}> https://github.com/93Laer </Text> // or 类似于安卓中的匿名内部内 // return<Text style={{color:'red',fontSize:34}}> https://github.com/93Laer </Text>
- 2、方式二,通过 StyleSheet 创建 style,测试多个 style,以哪个为准
// 创建样式 const styles = StyleSheet.create({ bigblue:{ color:'blue', fontSize:34, fontWeight:'bold' }, red:{ color:'red', fontSize:14 } }); // 使用样式 //todo 设置样式二,通过StyleSheet创建样式 return<Text style={styles.bigblue}> https ://github.com/93Laer </Text>
直接在组件中传入多个 style 对象,最后显示的效果就不展示了,在结尾直接给出结论
//这里通过多种方式定义style,主要是告诉读者定义style的多种方式 var mStyle = {color:'red',fontSize:34}; return<Text style={[mStyle,{color: 'blue',fontSize:20}]}> https ://github.com/93Laer </Text>
通过 StyleSheet 创建多个 style,并传入
return<Text style={[styles.bigblue,styles.red]}> https ://github.com/93Laer </Text>
结论:当设置多个 style 时以最后一个为准,可理解为最后一个将之前的样式覆盖了。也可理解为,style 从 styles 数组中依次拿出 style,并赋值给自己,所以最后一次赋值就会显示效果
到此,关于 react native 入门的基础知识就讲解完毕了。