React Native 入门宝典
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React Native 入门宝典相关的知识,希望对你有一定的参考价值。
声明:该书的笔者为徐嬴老师,一名具有5年IOS开发经验,和两年RN开发经验的老司机。
原文可以在gitbook上找到 笔者只是为他的书中提的的一些列问题,进行有偿答疑。
有偿答疑。本书将持续保持更新,有关问题可以加群讨论。
简介
笔者在研究ReactNative过程中,发现其中文资料相对较少,已出版的大部分图书资料都已过时。Facebook中的ReactNative开发团队以每月更新一版的速度在向前推进版本。
为更好的让广大开发者快速入门ReactNative,笔者结合自身开发和教学的经验,整理一份可用,且快速入门的资料。
希望大家可以通过这份资料,快速入门ReactNative技术。
开发环境的配置
安装Nodejs环境
1,进入http://nodejs.cnnodejs中文网,下载与自身环境相一致的nodejs安装包
2,安装包下载之后,双击进行安装
win平台的同学注意,安装过程中要关闭杀毒软件和各种安全工具
3,安装完成之后,打开命令行工具,检查是否安装成功,执行如下命令:
$ node -v
该命令执行结果为当前node版本,笔者当前版本为:
v6.9.1
4,检查NPM是否安装成功,
npm 是Node包管理工具,之后需要使用它来安装其他node程序包
在在命令行中输入如下命令:
$ npm -v
该命令执行结果为:
4.5.0
安装Yarn
Yarn
Yarn是Facebook出品的一个依赖包管理工具,起作用和npm一样。但Yarn具有更多好用的特性:
Yarn 会缓存它下载的每个包,所以不需要重复下载。它还能并行化操作以最大化资源利用率,所以安装速度之快前所未有
Yarn 在每个安装包的代码执行前使用校验码验证包的完整性。
Yarn 使用一个格式详尽但简洁的 lockfile 和一个精确的算法来安装,能够保证在一个系统上的运行的安装过程也会以同样的方式运行在其他系统上
网络适应单个请求失败不会导致安装失败,请求失败时会重试。
安装yarn
访问Yarn官网 ,下载响应平台的安装包,进行安装
使用Yarn
安装好之后,其他工具会自动使用Yarn进行加速。
安装create-react-native-app命令行工具
create-react-native-app是一个综合创建ReactNative工程,并且不需要配置的工具,它极大的简化了入门开发的流程。具体的内容,大家可以进入其github.com的主页进行浏览。
安装create-react-native-app需要使用npm进行,在任意目录下,输入如下命令,便可以在该目录下创建一个ReactNative工程。
$ npm install -g create-react-native-app
安装成功之后,会展示安装路径。如安装不成功,请检查网络,重新安装。
创建ReactNative工程
ReactNative工程的模板需要通过create-react-native-app工具进行创建。创建方法如下:
1,通过命令行进入存放ReactNative工程的文件夹。
2,在该文件夹下执行如下命令:
$ create-react-native-app myapp
myapp为工程名,可以更换为其他字符,但必须小写
安装过程需要1-5分钟不等,如想提升安装速度,可以安装yarn,详情见yarn官网
下面为笔者安装成功截图:
笔者使用了yarn进行提速,所有命令中显示为yarn。
运行预览工程
1,工程创建完成之后,便可以启动工程,开始开发和调试。
启动工程,首先要使用命令行工具进入工程更目录,然后运行如下指令
$ npm start
工程 启动之后,会生成一个二维码和一个本地链接,通过此此二维码或本地链接,便可预览工程运行效果。
2,启动工程之后,需要在手机端安装Expo App,使用Expo App对所开发的ReactNative App进行预览运行。
安装ExpoApp的方法如下:
ios平台:在AppStore中搜索Expo client,如图
android平台下,访问此链接:http://expo.io/--/api/v2/versions/download-android-apk下载安装包并安装,安装过程中需要给此App全部权限。
3,Expo App在手机端安装完成之后,打开ExpoApp,通过其扫描二维码的功能,扫描生成的二维码,便可以在App内预览开发中的App工程
4,新建工程的运行效果为:
ES6基础语法
变量
ES6中,增加了let和const两个关键字声明变量。
建议大家放弃var声明变量的模式。var的特性与现代语言差异过大,非常容易造成bug
1,let
let声明变量非常简单,如下代码:
let name = ‘Tom‘;
console.log(name);
let a = 1;
let b = 2;
let sum = a+b;
在声明变量中,变量名为驼峰命名法。例如:let myName = ‘Tom‘;
变量一定要先申明,在使用,例如:
console.log(name);//错误,找不到name
let name = ‘Tom‘;
2,const
const声明的变量,其值不能改变,代码如下:
const age = 10;
age = 11;//error,const 声明的变量,其值不能改变
但大家一定注意一点,const声明的变量值不能变,但如果该变量为对象,其对象属性值可变。
const person={
name:‘Tom‘,
age:10,
};
person.name = ‘Jhon‘;//没问题
person = {
name:‘TT‘,
age:11
}//error,错误,不能改变person的值
3,变量作用域
let和const声明的变量,以大括号为作用域边界,只有在变量作用域内,才可以使用该变量,超过变量作用域再使用该变量,报错。例如:
{
const name = ‘Tom‘;
console.log(name);//正确
}
console.log(name);//错误!name作用域再上面大括号内,超过无法使用
在有多层作用域嵌套的情况下,如果有同名变量,内层变量会屏蔽外面变量作用域,例如:
{
const name = ‘Tom‘;
{
const name = ‘Jim‘;
console.log(name);//打印为Jim
}
console.log(name);//打印为Tom
}
在多层作用域嵌套下,如没有同名变量,作用域范围不变。例如:
{
const name = ‘Tom‘;
{
console.log(name);//打印为Tom
}
console.log(name);//打印为Tom
}
注意,在多层作用域嵌套下,内层如有声明同名变量,内层变量声明之前,变量不能使用。例如:
{
const name = ‘Tom‘;
{
console.log(name);//错误!找不到name变量!
const name = ‘Jim‘;
}
console.log(name);//打印为Tom
}
字符串
javascript的字符串就是用‘‘或""括起来的字符表示。
如果‘本身也是一个字符,那就可以用""括起来,比如"I‘m OK"包含的字符是I,‘,m,空格,O,K这6个字符。
如果字符串内部既包含‘又包含"怎么办?可以用转义字符\来标识,比如:
‘I\‘m \"OK\"!‘;
表示的字符串内容是:
I‘m "OK"!
1,字符串模板
要把多个字符串连接起来,可以用+号连接:
const name = ‘Tom‘;
const age = 10;
const message = ‘Hello, ‘ + name + ‘, your age is‘ + age;
console.log(message);
如果有很多变量需要连接,用+号就比较麻烦。ES6新增了一种模板字符串,表示方法和上面的多行字符串一样,但是它会自动替换字符串中的变量:
const name = ‘Tom‘;
const age = 10;
const message = `Hello, ${name}, your age is ${age}`JavaScript为字符串提供了一些常用方法,注意,调用这些方法本身不会改变原有字符串的内容,而是返回一个新字符串
注意包裹字符串的字符为反引号,esc按键下方的字符,英文状态下输入
2,多行字符串
由于多行字符串用\n写起来比较费事,所以最新的ES6标准新增了一种多行字符串的表示方法,用反引号包裹。
`Hello
World`//这是一个多行字符串
3,操作字符串
获取字符串长度
const s = ‘Hello, world!‘;
s.length; // 13
要获取字符串某个指定位置的字符,使用类似Array的下标操作,索引号从0开始:
const s = ‘Hello, world!‘;
s[0]; // ‘H‘
s[6]; // ‘ ‘
s[7]; // ‘w‘
s[12]; // ‘!‘
s[13]; // undefined 超出范围的索引不会报错,但一律返回undefined
注意:字符串是不可变的,如果对字符串的某个索引赋值,不会有任何错误,但是,也没有任何效果
const s = ‘Test‘;
s[0] = ‘X‘;
console.log(s); // s仍然为‘Test‘·
JavaScript为字符串提供了一些常用方法,注意,调用这些方法本身不会改变原有字符串的内容,而是返回一个新字符串。
indexOf,会搜索指定字符串出现的位置:
const s = ‘hello, world‘;
s.indexOf(‘world‘); // 返回7
s.indexOf(‘World‘); // 没有找到指定的子串,返回-1
substring,返回指定索引区间的子串:
const s = ‘hello, world‘
s.substring(0, 5); // 从索引0开始到5(不包括5),返回‘hello‘
s.substring(7); // 从索引7开始到结束,返回‘world‘
更多JavaScript 字符串操作,可参见W3School教程.
数组
JavaScript的Array可以包含任意数据类型,并通过索引来访问每个元素。通过length可以访问数组长度。
1,通过索引访问数组元素
//通过索引读取数组中的元素
const item = array[0];
console.log(item);//输出0
注意:JavaScript中对数组越界操作没有异常处理
const array = [1,2,3,4];
console.log(array[5]);//不报错,打印undefined
2, 通过数组索引,可以修改数组中元素值
const array = [1,2,3,4];
array[0] = 5;
console.log(array[0]);//输出为5
注意:对越界修改数组的值,会改变数组长度,且为赋值的元素值为undefined。不建议此操作
const array = [1,2,3,4];
array[5] = 6;
console.log(array);//打印结果为[1,2,3,4,undefined,6]
3,push和pop
//push()向数组末尾添加元素
const array = [1,2,3,4];
array.push(5);
console.log(array);//打印结果[1,2,3,4,5]
//pop()从数组末尾删除元素
const array = [1,2,3,4];
array.pop();
console.log(array);//打印结果为[1,2,3];
4,splice
该方法可以操作数组在制定位置添加或删除元素
//删除指定元素
//splice(index,length)从index位置开始删除长度为length
const array = [1,2,3,4];
array.splice(1,1);//从下标1开始删除一个元素,即把2删除
console.log(array);//输出结果[1,3,4]
//添加制定元素
//splice(index,0,item1,item2,...),表示从index位置开始添加各元素,其他元素后移
const array = [1,2,3,4];
array.splice(1,0,5);
console.log(array);//输出结果[1,5,2,3,4]
//混合操作,添加删除
const array = [1,2,3,4];
array.splice(1,1,5);
console.log(array);//输出结果[1,5,3,4]
其他Array操作方法,详见W3SchoolArray教程。
对象
JavaScript的对象是一种无序的集合数据类型,它由若干键值对组成。
键值对是有键和值组成的,一般称为key-value。键和值直接用冒号分割,键只能为字符串,值可以为任何类型。
在对象中,多个键值对之前没有顺序概念,之间用逗号分割。
const obj = {
name:‘Tom‘,
age:10,
‘a:b‘:‘xx‘,
}
键为字符串,默认可以不加单引号,但如果字符串本身不加单引号有歧义
比如,上方第三个键值对中的键,为a:b一个字符串,如不加单引号,会有歧义,所以需要加。
通过对象的键可以对其对应的值进行读写操作。
const obj = {
name:‘Tom‘,
age:10,
‘a:b‘:‘xxx‘,
}
//通过 . 读取对象属性值
console.log(obj.name);//输出Tom
obj.name = ‘XXX‘;
console.log(obj.name);//输出XXX
//通过 [] 读取对象属性值,中括号内需要填字符串
console.log(obj.[‘age‘]);
obj[‘age‘] = 11;
console.log(obj[‘age‘]);
//有歧义的键,只能用[]号方式访问
console.log(obj.[‘a:b‘]);
//使用变量作为键时,只能使用[]方式访问
const key = ‘name‘;
console.log(obj.[key]);
函数
JavaScript中提供函数的定义和调用方式
1,基本语法
return x + y;
}
const sum = add(1,2);
console.log(sum);//结果为3
2,参数默认值
function add(x = 1,y = 2){
return x+y;
}
const sum1 = add();
console.log(sum1);//结果为3
const sum2 = add(2);
console.log(sum2);//结果为4
const sum3 = add(2,3);
console.log(sum3);//结果为5
3,箭头函数
ES6增加箭头函数特性
(x,y)=>{
return x + y;
}
箭头函数没有函数名,只描述了参数列表和函数体。
通常情况下,箭头函数一般作为值进行传递。
const aFunc = ()=>{
console.log(‘xxx‘);
}//将箭头函数赋值给aFunc变量
const bFunc = aFunc;//通过aFunc变量为bFunc变量赋值
箭头函数无法直接被调用,只能借助存放箭头函数值的变量进行调用,调用方法与调用普通函数一样
const add = (x,y)=>{
return x+y;
}
const sum = add(1,2);
console.log(sum);//结果为3
箭头函数也经常作为函数的参数,为函数形参赋值
function Test(func){
func();
}
const f1 = ()=>{
console.log(‘a‘);
}
const f2 = ()=>{
console.log(‘b‘);
}
Test(f1);//输出结果为a
Test(f2);//输出结果为b
4,高阶函数
高阶函数本质为能够接收其他函数作为参数传入的函数。也就是说,高阶函数本身也是一种函数,只不过它的参数可以为其他函数,一般我们用箭头函数作为高阶函数的参数。
例如我们上文中提到的Test()函数,可以称为一个高阶函数。
我们对高阶函数的要求为,会使用,不要求会编写。这是基于我们当前开发技术提出的要求。在ReactNative开发中,我们会遇到很多写好的高阶函数,供我们使用,来完成更复杂的功能。但我们并不需要自己编写一个高阶函数。
所以本节重点是研究如何使用高阶函数。
首先我们需要先明确一个概念:函数的类型。
函数类型由其输入参数和返回值决定,输入和返回值相同的函数,我们称之为同一类型函数。例如:
(x,y)=>{
return x+y;
}
(a,b)=>{
return a-b;
}
这两个为同类型函数,因输入参数和返回值类型相同。
我们来看第一个例子,首先我给出一个写好的高阶函数,同学们不用纠结高阶函数如何实现。
//func1的参数是一个f,f的类型为无输入参数,无返回值。
//func1为一个高阶函数
function func1(f){
f();
}
有了func1这个高阶函数,所有无输入参数,无返回值的函数,都可以作为参数出入该高阶函数。例如:
f1 = ()=>{
console.log(‘hello‘);
}
func1(f1);//输出hello
读到这里,大家可能会有一个疑问。使用了高阶函数,执行操作也仅仅是打印一个hello字符串。这样做的意义是什么。接下来我们便进行讨论,解释高阶函数的意义。
5,高阶函数的意义
在讨论这个问题之前,我们先明确两个概念,在上文中提到的,
func1:我们称之为高阶函数。
f1:是高阶函数的参数,也是一个函数。我们一般称之为这个函数为功能函数
高阶函数+功能函数的组合,才能实现完整的功能。其中高阶函数一般为框架提供,功能函数一般由使用框架的开发者自己编写。
为什么要是这样的组合呢?
是因为,我们做应用开发,尤其是App开发,如果代码从零开始写,那成本是非常高的。所以,我们在开发的时候,都是在各种框架下进行二次开发,这样可以非常有效的提升开发效率,避免大量重复基础的工作。我们可以认为,如果一个App的开发工作是100,那么一个优秀的框架可以帮助我们完成其中的70-80的工作量。我们只需要完成20-30的工作量便可开发一个App并发布。
所以,开发效率的核心是框架。
那框架是如何被开发出来的呢?
这个问题比较复杂,在此我们举一个简单的例子,方便大家理解,并不做深入探讨。
比如我们书中提供一个计算框架共大家使用。
计算这个功能不止包含法则,还包含数据的存储和验证。我们平时写的1+2,仅仅是通过运算符对计算法则的描述。
function compute(x,y,f){
return f(x,y);
}
上面代码中的compute便是我给大家的一个计算框架,我们可以想计算框架中传入两个计算变量一个计算规则,计算规则通过函数来描述。
我们先用计算框架做一个简单的加法计算:
//准备计算数据
const p = 1;
const q = 2;
//先用函数描述一个加法计算规则
const f1 = (a,b)=>{
return a+b;
}
//调用计算框架进行计算
const sum = compute(p,q,f1);
console.log(sum);//打印结果为3
计算框架使用我们传入的计算数据和计算法则,计算出我们的结果。这个计算框架就是高阶函数。
到这里,大家可能还是不能够理解,我简单的a+b也能完成这样的操作,为什么要用计算框架这个高阶函数。这是因为,我们的问题不足够复杂,没法提现计算框架的优势。下面我们在构造一个更复杂的计算。
我们规定,计算法则为:两个输入参数之和如果为奇数就相乘,如果之和为偶数结果为0。
这样稍微复杂的计算规则,普通的处理方式就没法进行了,这时候就体现出计算框架的功能了。
//构造计算规则
const f2 = (x,y)=>{
const s = x+y;
if(s%2 == 0){
return 0;
}
return x*y;
}
const result1 = compute(2,2,f2);
console.log(result1);//结果为0;
const result2 = compute(1,2,f2);
console.log(result2);//结果为2
通过计算框架,我们不需要关系具体计算过程,我们只需要描述特定的业务即可。
这便是高阶函数的意义:提供了一种使用框架的方法,让我们开发更加高效。
6,高阶函数的应用
这一节我们举两个例子,说明高阶函数的使用场景
提供操作基础
提供事件回调
一般来说,高阶函数无法独自使用,必须配合我们写的功能函数才能起作用。
案例1,数组的遍历
//一般我们使用for循环进行数组的遍历
const array = [1,2,3,4];
for(let i = 0; i < array.length; i++){
const value = array[i];
console.log(value);
}
此种方式,我们需要手写一个for循环来进行,但是大家思考一下,所有的数组遍历模式都差不多,那么有没有框架将for循环这种重复的基础操作封装,只让我们写遍历规则呢。
答案是肯定的,JavaScript语言本身的Array对象中,包含了一个map()方法,该方法便是一个高阶函数,其参数需要传入一个函数,函数类型为:两个输入参数,一个返回值。
第一个输入参数为数组中某一个元素的值
第二个输入参数为该元素在数组中的索引
map方法会用返回值组成一个新的数组并返回
const array = [1,2,3,4];
array.map((value,index)=>{
console.log(value);
})//依次打印数组中每一个值
const arr1 = [1,2,3,4];
const arr2 = arr1.map((value,index)=>{
return value * 2;
});
console.log(arr2);//结果为[2,4,6,8];
通过使用框架提供的高阶函数map,我们无需关注如果遍历数组,只需要关注遍历规则即可。
案例2,Timer事件回调
Timer是JavaScript中定时器,可以进行周期性操作,例如每隔1秒钟打印一个字符串。实现这样周期性操作,同样使用的高阶函数
//setInterval(func,msecond)
//该函数有两个输入参数,第一个输入参数为一个函数,类型为无输入参数,无返回值。第二个输入参数为间隔时间,单位毫秒
//我们通过函数描述周期性需要完成的任务,例如是一个打印任务
const task = ()=>{
console.log(‘hello‘);
};
//只需要将此任务传入高阶函数,并设置好间隔时间,该任务便会安装计划运行,
setInterval(task,1000);//每1秒钟打印一个hello
高阶函数的应用场景很多,我们经常用的就是以上两种类型。
对于高阶函数,是区分程序员能力的标杆,后续的案例,我们会接触越来越多的高阶函数。使大家对高阶函数的引用有更加深刻的认识。
JSON
JSON是JavaScript Object Notation的缩写,它是一种数据交换格式。
在JSON中,一共就一下几种数据类型:
number:和JavaScript的number完全一致;
boolean:就是JavaScript的true或false;
string:就是JavaScript的string;
null:就是JavaScript的null;
array:就是JavaScript的Array表示方式——[];
object:就是JavaScript的{ ... }表示方式。
上面的数据可以任意嵌套和组合。
由于JSON非常简单,很快就风靡Web世界,并且成为ECMA标准。几乎所有编程语言都有解析JSON的库,而在JavaScript中,我们可以直接使用JSON,因为JavaScript内置了JSON的解析。
把任何JavaScript对象变成JSON,就是把这个对象序列化成一个JSON格式的字符串,这样才能够通过网络传递给其他计算机。
如果我们收到一个JSON格式的字符串,只需要把它反序列化成一个JavaScript对象,就可以在JavaScript中直接使用这个对象了。
1,序列化
JSON序列化是指,将JavaScript中的对象转换为JSON格式的字符串。
例如我们在数据库中查询到了一个人的信息,需要把这个信息通过网络传递到前台的App进行显示,这时,我们需要将此对象进行JSON序列化,转换成JSON字符串才能进行传输。
const person = {
name:‘Tom‘,
age:10,
tel:‘18612341234‘
}
//JSON序列化使用JavaScript语言中自带的JSON解析器的stringify方法
const jsonData = JSON.stringify(person);
console.log(jsonData);//字符串:‘{"name":"Tom","age":11,"tel":‘1861234123‘}‘
2,反序列化
JSON反序列化也叫JSON解析。
拿到一个JSON格式的字符串,我们直接用JSON.parse()把它变成一个JavaScript对象。
一般来说,JSON格式的字符串都是通过网络请求获取
在此,我们为了测试JSON反序列化的功能,手写一个本地的JSON格式字符串。
const jsonData = ‘{"name":"Tom","age":11,"tel":18612341234}‘;
const person = JSON.parse(jsonData);
console.log(person.name);//结果为Tom.
Class
ES6新增了class关键字,对面向对象编程有了更好的支持。
1,基本语法
class Point {
constructor(x,y){
this.x = x;
this.y = y;
}
toString(){
return `x is ${this.x}, y is ${this.y}`;
}
}
const p = new Point(1,2);
const s = p1.toString();
console.log(s);//输出结果为x is 1, y is 2;
上面代码演示了ES6中的class的基本用法。
使用class定义一个类
constructor为类的构造方法,当使用类实例化一个对象是,该方法会调用,并接受实例化时传入的参数
类的属性不需要提前声明,可以直接使用,为定义的属性值为null
声明类的方法时,不需要写function
2,this对象
this对象表示该类创建的对象,可以简单理解为自身。
this有两种使用场景:
访问自身属性
调用自身方法
class Point {
constructor\(x,y\){
this.x = x;
this.y = y;
}
toString(){
//通过this访问自身属性
return `x is ${this.x}, y is ${this.y}`;
}
}
class Point {
constructor\(x,y\){
this.x = x;
this.y = y;
}
toString(){
//通过this访问自身属性
return `x is ${this.x}, y is ${this.y}`;
}
logSelf(){
//通过this调用自身方法
const s = this.toString();
console.log(s)
}
}
const p1 = new Point(1,2);
p1.logSelf();
3,手动绑定this对象
在特定情况下,类中的方法在调用时,会产生this为undefined的情况。这些情况较为复杂,后续章节会详细说明,在此不展开讲解。
例如,如下代码,变无法正常运行:
class Timer {
constructor(x){
this.x = x;
}
start(){
setInterval(this.event,1000);
}
event(){
console.log(‘a‘);
console.log(this.x);
}
}
const t = new Timer(1);
t.start();
上述代码的运行结果为,每秒钟,打印一个a字符,本应该一起打印的属性x的值,无法输出。
原因为,在此种情况下,event方法中的this对象为undefined,所以无法通过this对象访问属性x的值。
我们在学习初期,无法区分哪些情况this对象会丢失,所以我们采用一个统一的保守方案来修改此bug。
只要是自己写的方法且方法中用到了this对象,我们都统一在constructor方法中,手动对其绑定this对象,避免this对象丢失的情况发生。
class Timer {
constructor(x){
this.x = x;
//手动绑定this
this.event = this.event.bind(this);
}
start(){
setInterval(this.event,1000);
}
event(){
console.log(‘x‘);
console.log(this.x);
}
}
const t = new Timer(1);
t.start();
通过手动绑定this对象之后,this对象丢失的情况便不会发生。
4,类的继承
在ES6中,提供的extends关键字用来实现类的继承关系。
class Base {
say(){
console.log(‘this is Base‘);
}
}
class Sub extends Base {
sayHello(){
console.log(‘this is Sub‘);
}
}
const s = new Sub();
s.say();//输出结果为this is Base
s.sayHello();//输出结果为 this is Sub
Sub类通过继承获得say方法,所有通过Sub实例化的对象也同样可以使用该方法。
如果子类的方法与父类方法同名,那么子类的方法会覆盖父类的方法。
class Base {
say(){
console.log(‘this is Base‘);
}
}
class Sub extends Base {
say(){
console.log(‘this is Sub‘);
}
}
const s = new Sub();
s.say();//输出结果为this is Sub
5,super对象
super对象和this对象类似。this对象表示对象本身,super对象表示本身对象的父类对象。
super对象的使用场景两个:
调用与子类同名的父类方法
调用父类的constructor
class Base {
say(){
console.log(‘this is Base‘);
}
}
class Sub extends Base {
say(){
//调用父类的say方法
super.say();
console.log(‘this is Sub‘);
}
}
const s = new Sub();
s.say();
//输出结果为
this is Base
this is Sub
class Point {
constructor(x,y){
this.x = x;
this.y = y;
}
}
class Point3D extends Point {
constructor(x,y,z){
//调用父类的constructor构造函数
super(x,y);
this.z = z;
}
logSelf(){
console.log(`x:${this.x},y:${this.y},z:${this.z}`);
}
}
const p = new Point3D(1,2,3);
p.logSelf();//输出结果x:1,y:2,z:3
Module模块系统
ES6提供了语言级别的模块系统解决方案,是模块化编程更加简单和清晰。
我们将一个JavaScript文件看做一个js模块,在模块内部的变量和表达式,只要在相同的作用域中,不需要额外的引入操作变可以直接使用。
但在不同模块之前的变量或者表达式无法直接使用,需要使用Module系统进行暴露和导入操作,才可以使用。
在编写代码时,我们通常在一个js模块中编写一个类,方面后期的维护和阅读。当在另一个js模块中需要使用这个类时,我们需要进行以下两步操作
在编写类的js模块中,暴露该类的定义
在使用类的js模块中,引入该类的定义
在完成以上两步操作之后,便可以在新的模块中使用已经写好的类的代码。
1,暴露模块接口
在ES6中,使用export关键字对模块内的定义进行暴露。
一般我们暴露三种接口:
变量接口
函数接口
Class接口
暴露接口方法有两种形式,分别为暴露定义和通过大括号暴露名称。直接暴露名称为错误方法!
注意:export命令,只能写在模块的顶层作用域(注意,不是顶部)。顶层作用域的意思是不能再任何一个大括号内写export
//变量接口
//正确1,暴露定义
export const a = 1;
export const b = 2;
//正确2
const x = 1;
const y = 1
//通过大括号暴露名称
export {
x,
y,
};
//错误!!!
cosnt x = 1;
export x;
//函数接口
//正确1
export function f1(){
console.log(1);
}
export function f2(){
console.log(2);
}
//正确2
function f1(){
console.log(1);
}
function f2(){
console.log(2);
}
export {
f1,
f2,
}
//class接口
//正确1
export class AClass {
}
export class BClass {
}
//正确2
class AClass {
}
class BClass {
}
export {
AClass,
BClass,
}
2,引入模块接口
引入模块使用import关键字进行。使用方法如下
import { 接口名 } from ‘相对路径‘
例如,我们首先在a.js文件中暴露一个变量,
//a.js
export const name = ‘Tom‘;
如果我们想在b.js文件中使用这个变量,我们需要引入该变量接口。
我们假定a.js和b.js在同级目录下。
import { name } from ‘./a.js‘;
console.log(name);//输出结果为Tom
在import命令中,如引入文件为.js,那么其拓展名可以省略,其他类型文件不可以省略
import { name } from ‘./a‘;与上午中代码等效,日常开发经常省略.js
在引入中一定注意,暴露的变量名和引入的变量名,一定要一致,如果不一致,将引入失败。
3,默认暴露
在上述讲解中,暴露和引入都必须使用相同的接口名,如果不相同便会出错。这样的要求在实际使用中不是很方便,在使用其他人的模块是,还需要阅读其源码才能完成正确引入。
在ES6通过这样的暴露引入机制同时,为了方面开发者更加方便的使用模块,增加了一个默认暴露引入机制。
该机制通过export default关键字进行暴露,且同一模块中,只能使用一次export default,同时可以使用传统的export进行暴露
//a.js
export default const name = ‘Tom‘;
当引入默认暴露的接口是,接口名称可以自定义,无需和暴露名一致。同时也不需要大括号
import XXX from ‘./a‘;
console.log(XXX);//输出结果为Tom
默认暴露和普通暴露机制可以同时使用:
//a.js
export default const name = ‘Tom‘;
export const age = 10;
import Name , { age } from ‘./a‘;
console.log(Name);
console.log(age);
4, as重命名
在除了默认暴露以外的情况,暴露和引入的接口名必须一致。但有时模块内部有自己的命名规则,对外暴露是命名规则可能过于繁琐,我们便可以使用 as 关键字做暴露或者引入重命名,方面后面编码使用该接口。
暴露重命名
//a.js
const xxx_name = ‘Tom‘;
export {
xxx_name as name,
}
import { name } from ‘./a‘;
console.log(name)
引入重命名
//a.js
export const name = ‘Tom‘;
import { name as xxx_name } from ‘./a‘
console.log(xxx_name);
React Native 基础
以上说了那么多,都是对知识的预热,
下面我们的RN才渐渐粉墨登场了,在这里我们再次感谢我们的徐嬴老师,熬夜加班写下这本书。原文请上gitbook上找。该书gitbook上的原文
HelloWorld工程
在本机配置好开发环境之后,便可以创建工程。
首先我们先创建一个HelloWorld工程,打开命令行:
$ cd 指定目录
$ create-react-native-app hello-world
等待一段时间,当命令行出现如下信息,便表示该工程创建成功。
1,文件结构
创建好的工程根目录下,有如下文件。
其中点开通的文件为隐藏文件,如看不到,可以调整文件夹显示属性,使隐藏文件和文件夹显示即可。隐藏文件的功能在此不做介绍,后续高级章节中进行展开讲解。
App.js 应用开发入口
app.json 应用配置文件
App.test.js 应用测试用例
node_modules 依赖包文件夹
package.json node配置文件
README.md 说明文件
yarn.lock yarn配置文件
以上文件中,出第一个App.js文件外,其他文件我们暂时不会使用,大家不要更改其他文件中的内容。其他文件的功能,在后续高级章节中陆续展开讲解。
2,App.js
App.js是程序开发的入口,我们首先阅读下其源码:
为了讲解方便,我将代码分为三个区域:
//区域1
import React from ‘react‘;
import { StyleSheet, Text, View } from ‘react-native‘;
//区域2
export default class App extends React.Component {
render() {
return (
Open up App.js to start working on your app!
Changes you make will automatically reload.
Shake your phone to open the developer menu.
);
}
}
//区域3
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: ‘#fff‘,
alignItems: ‘center‘,
justifyContent: ‘center‘,
},
});
区域1
模块引入区,通过ES6的Module引入外部模块。
通常我们使用两个默认的外部模块react和react-native构建我们的初始工程。
大家注意,这里我们引入模块是并没有给出模块的相对路径,而是直接使用模块名称进行引入。
是因为在ReactNative项目中,react和react-native是依赖框架,通过npm或者yarn进行安装使用,这样的依赖框架包,我们安装之后可以直接使用模块名称进行引入,不需要在标注相对路径。
但自己书写的js模块,仍需要使用相对路径进行引入,后续我们会涉及到此内容。
区域2
React组件定义区,ReactNative采用的是React.js组件化编程思想,每一个模块中包含一个React组件,关于React.js的知识我们在下一个章节讲述。
该区域是编程的核心区域,在此区域中,我们通过代码编写逻辑,控制页面元素,响应用户交互信息和刷下页面。
区域3
样式表定义区。
在ReactNative开发中,页面元素的样式通过inline的样式表进行调整,与传统网页CSS控制样式不同,ReactNative样式是通过元素的style属性进行控制,所有样式对象都只对其style赋值的元素起作用,没有全局样式的概念。
ReactNative不支持CSS样式属性,但其样式控制属性与CSS类似,最大的不同是采用驼峰命名法,例如CSS中的background-color,在ReactNative中的属性为backgroundColor。
具体的样式,我们在后续章节会详细讲解。
React.js基础
ReactNative框架是基于React.js扩展而来,其编码基础和基本开发思想都是与React.js一致。
React.js是Facebook在2013年5月开源的一个JavaScript框架,主要用于构建UI页面。
目前Facebook及其旗下的前端都是用React.js进行构建,在之前,Facebook是用Angular.js框架进行开发,但是随着Facebook业务的增加,Angular.js框架自身的架构能力不足以支撑项目的继续继承。Facebook在内部首先开启了一个以组件化编程思想为基础的新框架,后期开源,命名为React.js,用来架设 Instagram 的网站。
React.js核心思想为组件化编程,通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。
本章会对React.js基础知识进行讲解,为ReactNative做技术基础准备。
0,开发环境
本节内容无法在手机App中运行,因React.js是浏览器JavaScript框架,所以本章内容全部在浏览器中运行调试。
首先确保,Nodejs,chrome浏览器和Yarn已经安装。
通过npm安装React工程创建工具
npm install -g create-react-app
安装成功之后便可以通过该工具创建一个React项目
cd 指定目录
create-react-app myapp
创建成功之后,进入该项目的工程文件夹根目录,运行
npm start
或
yarn start
启动项目。
首先会启动一个本地调试服务器,通过该调试服务器可以预览项目。
初始项目为下图所示:
1,React元素
React元素对象是在页面上显示的基本单位,能够在页面上看到的东西,都是React元素对象。
创建React元素对象有两种方式:
使用html标签创建React元素对象
使用React组件创建React元素对象
使用React组件创建元素的内容,在我们后续讲完React组件在详细说明
首先我们需要引入react框架,
import React from ‘react‘;
react框架在创建项目中自动被安装,同时自动安装的还有react-dom框架,react-dom框架下文中讲解。
通过react框架提供的createElement()函数创建React元素对象。
React.createElement(
type,
props,
children1,
children2,
...
)
type:为HTML标签字符串或React组件类名
props:为元素属性,类型为一个对象,属性名为对象中的键名,属性值为相应键名对应的值
从第三个参数起,都为该元素的子元素,类似HTML中的节点的子节点。
例如,我们需要在屏幕上显示一个HelloWorld字符串,我们使用HTML中的p标签进行创建React元素,元素属性为空,子节点为一个字符串:HelloWorld。
const text = ‘HelloWorld‘;
const textObject = React.createElement(‘p‘,null,text);
如果我们需要在屏幕上展示一个图片,创建React元素对象如下:
const url = ‘https://facebook.github.io/react/img/logo_og.png‘;
const imageObject = React.createElement(‘img‘,{src:url,alt:‘react-icon‘})
在实际开发中,我们需要频繁的创建React元素对象,但此种创建方式十分不方便,所以,Facebook在ES6语法基础上,发布了JSX语法,使用JSX语法,可以更加方便的创建和管理React元素对象。
2,JSX语法
JSX是React的核心组成部分,它使用XML标记的方式去直接声明界面,界面组件之间可以互相嵌套。可以理解为在JS中编写与XML类似的语言,一种定义带属性树结构(DOM结构)的语法,它的目的不是要在浏览器或者引擎中实现,它的目的是通过各种编译器将这些标记编译成标准的JS语言。
例如我们对比上文中提到的两个创建React元素对象的代码:
const text = ‘HelloWorld‘;
//使用JavaScript语法
const textObject = React.createElement(‘p‘,null,text);
//使用JSX语法
const textObject =
{text}
可以看出,JSX语法的本质就是使用标签语法代替React.createElement()方法。
使用JSX语法,必须首先引入React框架,虽然使用JSX语法虽然没有显示调用React框架,但编译后的代码仍然包含React的调用。
且JSX语法的核心优势就是可以将变量写入标签中,这是之前HTML标签语法做不到的操作。有了变量,React框架的编程能力得到了极大的提升,可以处理更多动态的问题。
对于动态和静态,我在这里做一个基础定义
静态:在写代码的这个时刻,已知所要编写的内容。例如,我要输出一个HelloWorld字符串,这个字符串在编码的这个时刻是已知的,在编写这样的操作时,我们称之为静态问题。
动态:在编写代码这个时刻,对编写内容有部分未知的空间。例如,我要求两个书的和。到底这两个数是什么,在编写代码的时候不知道,这样的问题称之为动态问题,动态问题,必须使用变量来解决使用JSX语法创建对象和HTML标签语法很像,但和标签语法最大的区别就是在与语句中可以带变量,通过大括号对变量进行包裹。
创建一个显示图片的React元素对象
const url = ‘https://facebook.github.io/react/img/logo_og.png‘;
//使用JavaScript语法
const imageObject = React.createElement(‘img‘,{src:url,alt:‘react-icon‘});
//使用JSX语法
const imageObject =
;
同样,React元素对象可以像HTML标签一样进行嵌套:
const text = ‘HelloWorld‘;
const url = ‘https://facebook.github.io/react/img/logo_og.png‘;
const divObject = (
{text}
);
JSX语法中,变量可以是值,也可以是另一个React元素对象:
const textObject =
{‘HelloWorld‘}
;
const divObject = (
{textObject}
)
//相当于
const divObject = (
{‘HelloWorld‘}
);
变量可以为数组,数组的每一个元素必须为React元素对象
const objectArray = [];
const textObject1 =
{‘HelloWorld1‘}
;
const textObject2 =
{‘HelloWorld2‘}
;
const textObject3 =
{‘HelloWorld3‘}
;
objectArray.push(textObject1);
objectArray.push(textObject2);
objectArray.push(textObject3);
const divObject = (
{objectArray}
)
//相当于
const divObject = (
{‘HelloWorld1‘}
{‘HelloWorld2‘}
{‘HelloWorld3‘}
);
3,ReactDOM
ReactDOM框架提供了将React元素对象渲染到屏幕上的方法。
使用ReactDOM框架首先需要引入
import ReactDOM from ‘react-dom‘;
ReactDOM提供了一个render()方法,通过此方法可以将React元素对象渲染到指定的div标签中。
//通过document操作获取一个div节点
const div = document.getElementById(‘root‘);
//通过JSX语法创建一个React元素
const text =
{‘HelloWorld‘}
;
//渲染组件到div中
ReactDOM.render(text,div);
4,React组件
组件化开发是React.js以及后续的ReactNative开发的核心思想。
通过对业务是视图进行封装,组件化开发可以最大化的简化开发过程,增强代码的可复用性和可维护性。
React组件是通过JavaScript类实现的。
通过继承react框架中提供的Component基础类,构造一个React组件类。
import React , { Component } from ‘react‘;
class MyComponent extends Component {
}
MyComponent即为React组件,通过React组件也可以创建React元素,让组件显示在屏幕上。
const div = document.getElementById(‘root‘);
const myComponent = ;
ReactDOM.render(myComponent,div);
但是这样屏幕上没有任何现实,因为我们还没有实现React组件的内容,但此语法没有问题。
下面我们开始实现React组件
MyComponent通过react框架提供的Component基础类继承了以下方法:
constructor()
componentDidMount()
render()
componentWillUnmount()
import React , { Component } from ‘react‘;
class MyComponent extends Component {
//组件构造方法,在此方法中对组件内数据进行初始化
//此方法传入参数为props固定不可变更,对于此参数后续介绍
constructor(props){
//此方法中固定在第一行调用super(props)对父类进行初始化
super(props);
}
//此方法为声明周期方法,在该组件显示在屏幕上时,该方法被自动调用
componentDidMount(){
}
//组件内渲染方法
//此方法返回一个React元素作为该组件的外观
render(){
return (
);
}
//此方法为声明周期方法,在该组件从屏幕上消失时,该方法被自动调用
//此方法不常用
componentWillUnmount(){
}
}
实现组件主要实现组件以下两个方面:
外观
逻辑
这也是React组件和html标签最本质的区别。React组件和外观和html标签很像但组件可以封装自己的业务逻辑,HTML仅仅是展示内容。
我们先实现一个最简单的组件,只显示一个HelloWorld
import React , { Component } from ‘react‘;
class MyComponent extends Component {
render(){
return (
{‘HelloWorld‘}
);
}
}
此组件只有一个单纯的显示功能,只用到一个render方法。
实现更多负责的组件,需要学习一下两个章节。
5,Props
props是组件属性,通过组件创建元素时,可以为该元素传入属性,所传入的属性可以在组件内部通过this.props字段访问。
例如我们声明一个显示文字的:
import React , { Component } from ‘react‘;
class MyComponent extends Component {
render(){
return (
{this.props.text}
);
}
}
此组件可以动态显示内容,使用该组件是,只需要向其属性传入相应的值既可以。
const div = document.getElementById(‘root‘);
//为元素传入属性的语法在JSX中类似html
const myComponent =
ReactDOM.render(myComponent,div)
展开操作符
在组件属性字段较多时,赋值语句会使标签语法不方便阅读,此时,我们可以采用展开操作符,简化赋值语句
const params = {
name:‘Tom‘,
age:10,
roomName:‘101‘,
}
//相当于
name={‘Tom‘}
age={10}
roomName={‘101‘}
/>
6,State
state是react框架提供了一个页面刷新的机制。它通过this.state和this.setState()来实现。
state是React组件通过继承Component获取的一个属性接口。
state是一个对象,在constructor方法中进行初始化,在render方法中使用。
state对象中的值不能直接修改,需要通过this.setState()方法进行修改。
具体方法,我们通过一个计数器案例来进行讲解:
该技术实现了一个非常简单的计数功能。这个功能中,我们需要根据用户的点击事件来更新页面上的数字。
import React , { Component } from ‘react‘;
export default App extends Component {
constructor(props){
super(props);
this.state = {
count:0,
}
}
render(){
return(
{‘计数器‘}
{this.state.count}
{
this.setState({
count:this.state.count+1,
})
}}>{‘增加‘}
{
this.setState({
count:this.state.count-1,
})
}}>{‘减少‘}
{
this.setState({
count:0,
})
}}>{‘归零‘}
)
}
}
文章持续更新如有问题,欢迎致电:18333103619.
关于本书出现的问题,笔者提供有偿解答,欢迎加微信:jkxx123321
以上是关于React Native 入门宝典的主要内容,如果未能解决你的问题,请参考以下文章
React Native基础与入门--初识React Native
React-Native入门指导之iOS篇 —— 一准备工作