React Native之React速學教程(下)
本文出自《React Native學習筆記》系列文章。
React Native是基于React的,在開發(fā)React Native過程中少不了的需要用到React方面的知識艺沼。雖然官方也有相應的Document,但篇幅比較多,學起來比較枯燥棘捣。
通過《React Native之React速學教程》你可以對React有更系統(tǒng)和更深入的認識。為了方便大家學習休建,我將《React Native之React速學教程》分為上乍恐、中、下三篇测砂,大家可以根據(jù)需要進行閱讀學習茵烈。
概述
本篇為《React Native之React速學教程》的最后一篇。本篇將帶著大家一起認識ES6砌些,學習在開發(fā)中常用的一些ES6的新特性呜投,以及ES6與ES5的區(qū)別加匈,解決大家在學習React /React Native過程中對于ES6與ES5的一些困惑。
ES6的特性
何為ES6仑荐?
ES6全稱ECMAScript 6.0雕拼,ES6于2015年6月17日發(fā)布,ECMAScript是ECMA制定的標準化腳本語言粘招。目前JavaScript使用的ECMAScript版本為ECMAScript-262啥寇。
下面我為大家列舉了ES6新特性中對我們開發(fā)影響比較大的六方面的特性。
1.類(class)
對熟悉Java洒扎,object-c示姿,c#等純面向對象語言的開發(fā)者來說,都會對class有一種特殊的情懷逊笆。ES6 引入了class(類)栈戳,讓JavaScript的面向對象編程變得更加簡單和易于理解。
class Animal {
// 構造方法难裆,實例化的時候將會被調用子檀,如果不指定,那么會有一個不帶參數(shù)的默認構造函數(shù).
constructor(name,color) {
this.name = name;
this.color = color;
}
// toString 是原型對象上的屬性
toString() {
console.log('name:' + this.name + ',color:' + this.color);
}
}
var animal = new Animal('dog','white');//實例化Animal
animal.toString();
console.log(animal.hasOwnProperty('name')); //true
console.log(animal.hasOwnProperty('toString')); // false
console.log(animal.__proto__.hasOwnProperty('toString')); // true
class Cat extends Animal {
constructor(action) {
// 子類必須要在constructor中指定super 方法乃戈,否則在新建實例的時候會報錯.
// 如果沒有置頂consructor,默認帶super方法的constructor將會被添加褂痰、
super('cat','white');
this.action = action;
}
toString() {
console.log(super.toString());
}
}
var cat = new Cat('catch')
cat.toString();
// 實例cat 是 Cat 和 Animal 的實例,和Es5完全一致症虑。
console.log(cat instanceof Cat); // true
console.log(cat instanceof Animal); // true
2.模塊(Module)
ES5不支持原生的模塊化缩歪,在ES6中,模塊將作為重要的組成部分被添加進來谍憔。模塊的功能主要由 export 和 import 組成匪蝙。每一個模塊都有自己單獨的作用域,模塊之間的相互調用關系是通過 export 來規(guī)定模塊對外暴露的接口习贫,通過import來引用其它模塊提供的接口逛球。同時還為模塊創(chuàng)造了命名空間,防止函數(shù)的命名沖突苫昌。
導出(export)
ES6允許在一個模塊中使用export來導出多個變量或方法颤绕。
導出變量
//test.js
export var name = 'Rainbow'
心得:ES6不僅支持變量的導出,也支持常量的導出祟身。
export const sqrt = Math.sqrt;//導出常量
ES6將一個文件視為一個模塊奥务,上面的模塊通過 export 向外輸出了一個變量。一個模塊也可以同時往外面輸出多個變量袜硫。
//test.js
var name = 'Rainbow';
var age = '24';
export {name, age};
導出函數(shù)
// myModule.js
export function myModule(someArg) {
return someArg;
}
導入(import)
定義好模塊的輸出以后就可以在另外一個模塊通過import引用氯葬。
import {myModule} from 'myModule';// main.js
import {name,age} from 'test';// test.js
心得:一條import 語句可以同時導入默認方法和其它變量。
import defaultMethod, { otherMethod } from 'xxx.js';
3.箭頭(Arrow)函數(shù)
這是ES6中最令人激動的特性之一父款。=>
不只是關鍵字function的簡寫溢谤,它還帶來了其它好處。箭頭函數(shù)與包圍它的代碼共享同一個this
,能幫你很好的解決this的指向問題憨攒。有經驗的JavaScript開發(fā)者都熟悉諸如var self = this;
或var that = this
這種引用外圍this的模式世杀。但借助=>
,就不需要這種模式了肝集。
箭頭函數(shù)的結構
箭頭函數(shù)的箭頭=>之前是一個空括號瞻坝、單個的參數(shù)名、或用括號括起的多個參數(shù)名杏瞻,而箭頭之后可以是一個表達式(作為函數(shù)的返回值)所刀,或者是用花括號括起的函數(shù)體(需要自行通過return來返回值,否則返回的是undefined)捞挥。
// 箭頭函數(shù)的例子
()=>1
v=>v+1
(a,b)=>a+b
()=>{
alert("foo");
}
e=>{
if (e == 0){
return 0;
}
return 1000/e;
}
心得:不論是箭頭函數(shù)還是bind浮创,每次被執(zhí)行都返回的是一個新的函數(shù)引用,因此如果你還需要函數(shù)的引用去做一些別的事情(譬如卸載監(jiān)聽器)砌函,那么你必須自己保存這個引用斩披。
卸載監(jiān)聽器時的陷阱
錯誤的做法
class PauseMenu extends React.Component{
componentWillMount(){
AppStateIOS.addEventListener('change', this.onAppPaused.bind(this));
}
componentWillUnmount(){
AppStateIOS.removeEventListener('change', this.onAppPaused.bind(this));
}
onAppPaused(event){
}
}
正確的做法
class PauseMenu extends React.Component{
constructor(props){
super(props);
this._onAppPaused = this.onAppPaused.bind(this);
}
componentWillMount(){
AppStateIOS.addEventListener('change', this._onAppPaused);
}
componentWillUnmount(){
AppStateIOS.removeEventListener('change', this._onAppPaused);
}
onAppPaused(event){
}
}
除上述的做法外,我們還可以這樣做:
class PauseMenu extends React.Component{
componentWillMount(){
AppStateIOS.addEventListener('change', this.onAppPaused);
}
componentWillUnmount(){
AppStateIOS.removeEventListener('change', this.onAppPaused);
}
onAppPaused = (event) => {
//把方法直接作為一個arrow function的屬性來定義讹俊,初始化的時候就綁定好了this指針
}
}
需要注意的是:不論是bind還是箭頭函數(shù)垦沉,每次被執(zhí)行都返回的是一個新的函數(shù)引用,因此如果你還需要函數(shù)的引用去做一些別的事情(譬如卸載監(jiān)聽器)仍劈,那么你必須自己保存這個引用厕倍。
4.ES6不再支持Mixins
在ES5下,我們經常使用mixin來為組件添加一些新的方法贩疙,如:
var SetIntervalMixin = {
componentWillMount: function() {
this.intervals = [];
},
setInterval: function() {
this.intervals.push(setInterval.apply(null, arguments));
},
componentWillUnmount: function() {
this.intervals.forEach(clearInterval);
}
};
var TickTock = React.createClass({
mixins: [SetIntervalMixin], // Use the mixin
getInitialState: function() {
return {seconds: 0};
},
...
但讹弯,很不幸的是,ES6不支持使用Mixins了这溅,不過我們可以使用闸婴,增強組件來替代Mixins。
//Enhance.js
import { Component } from "React";
export var Enhance = ComposedComponent => class extends Component {
constructor() {
this.state = { data: null };
}
componentDidMount() {
this.setState({ data: 'Hello' });
}
render() {
return <ComposedComponent {...this.props} data={this.state.data} />;
}
};
//HigherOrderComponent.js
import { Enhance } from "./Enhance";
class MyComponent {
render() {
if (!this.data) return <div>Waiting...</div>;
return <div>{this.data}</div>;
}
}
export default Enhance(MyComponent); // Enhanced component
用一個“增強組件”芍躏,來為某個類增加一些方法邪乍,并且返回一個新類,這無疑能實現(xiàn)mixin所實現(xiàn)的大部分需求对竣。
另外庇楞,網上也有很多其他的方案,如react-mixin否纬。
5.ES6不再有自動綁定
在ES5中吕晌,React.createClass會把所有的方法都bind一遍,這樣可以提交到任意的地方作為回調函數(shù)临燃,而this不會變化睛驳。但在ES6中沒有了自動綁定烙心,也就是說,你需要通過bind或者箭頭函數(shù)來手動綁定this引用乏沸。
// 通過使用 bind() 來綁定`this`
<div onClick={this.tick.bind(this)}>
// 也可通過使用箭頭函數(shù)來實現(xiàn)
<div onClick={() => this.tick()}>
心得: 因為無論是箭頭函數(shù)還是bind()每次被執(zhí)行都返回的是一個新的函數(shù)引用淫茵,所以,推薦大家在組件的構造函數(shù)中來綁定
this
蹬跃。
constructor(props) {
super(props);
this.state = {count: props.initialCount};
this.tick = this.tick.bind(this);//在構造函數(shù)中綁定this
}
// 使用
<div onClick={this.tick}>
### 6.static關鍵字
在ES6中我們可以通過static關鍵字來定義一個類函數(shù)匙瘪。
```javascript
class People {
constructor(name) { //構造函數(shù)
this.name = name;
}
sayName() {
console.log(this.name);
}
static formatName(name) //將formatName定義為類方法
return name[0].toUpperCase() + name.sustr(1).toLowerCase();
}
}
console.log(People.formatName("tom")); //使用類方法formatName
ES6 VS ES5(ES6與ES5的區(qū)別)
新版本的React /React Native使用了ES6標準,下面就讓我們一起了解一下基于ES6的React/React Native相比ES5有哪些不同蝶缀。
心得:很多React/React Native的初學者經常會被ES6問題迷惑:官方建議我們ES6丹喻,但是網上搜到的很多教程和例子都是基于ES5版本的,所以很多人感覺無法下手翁都,下面就讓我們一起認識ES6與ES5在React/React Native開發(fā)上有哪些不同和需要注意的地方碍论。
下面是我們需要知道的ES6與ES5在4大方面上的區(qū)別。
1.在定義方面的不同
在定義組件柄慰,方法骑冗,屬性等方面,ES6與ES5是有所不同的先煎,下面就讓我們一起看一下有哪些不同贼涩。
心得:因為向下兼容的原因,你在開發(fā)過程中可使用ES6也可以使用ES5的規(guī)范薯蝎,但為了代碼的風格一致性遥倦,建議盡量減少混寫。
定義組件
ES5
在ES5里占锯,通常通過React.createClass來定義一個組件類袒哥,像這樣:
var Photo = React.createClass({
render: function() {
return (
<Image source={this.props.source} />
);
},
});
ES6
在ES6里,我們通過繼承React.Component 來定義一個組件類消略,像這樣:
class Photo extends React.Component {
render() {
return (
<Image source={this.props.source} />
);
}
}
定義方法
相比ES5堡称,ES6在方法定義上語法更加簡潔,從上面的例子里可以看到艺演,給組件定義方法不再用 名字: function()的寫法却紧,而是直接用名字(),在方法的最后也不能有逗號了胎撤。
ES5
var Photo = React.createClass({
test: function(){
},
render: function() {
return (
<Image source={this.props.source} />
);
},
});
ES6
class Photo extends React.Component {
test() {
}
render() {
return (
<Image source={this.props.source} />
);
}
}
定義組件的屬性類型和默認屬性
ES5
在ES5里晓殊,屬性類型和默認屬性分別通過propTypes成員和getDefaultProps方法來實現(xiàn)。
var Video = React.createClass({
getDefaultProps: function() {
return {
autoPlay: false,
maxLoops: 10,
};
},
propTypes: {
autoPlay: React.PropTypes.bool.isRequired,
maxLoops: React.PropTypes.number.isRequired,
posterFrameSrc: React.PropTypes.string.isRequired,
videoSrc: React.PropTypes.string.isRequired,
},
render: function() {
return (
<View />
);
},
});
ES6
在ES6里伤提,可以統(tǒng)一使用static成員來實現(xiàn)巫俺。
class Video extends React.Component {
static defaultProps = {
autoPlay: false,
maxLoops: 10,
}; // 注意這里有分號
static propTypes = {
autoPlay: React.PropTypes.bool.isRequired,
maxLoops: React.PropTypes.number.isRequired,
posterFrameSrc: React.PropTypes.string.isRequired,
videoSrc: React.PropTypes.string.isRequired,
}; // 注意這里有分號
render() {
return (
<View />
);
} // 注意這里既沒有分號也沒有逗號
}
也有人這么寫,雖然不推薦肿男,但讀到代碼的時候你應當能明白它的意思:
class Video extends React.Component {
render() {
return (
<View />
);
}
}
Video.defaultProps = {
autoPlay: false,
maxLoops: 10,
};
Video.propTypes = {
autoPlay: React.PropTypes.bool.isRequired,
maxLoops: React.PropTypes.number.isRequired,
posterFrameSrc: React.PropTypes.string.isRequired,
videoSrc: React.PropTypes.string.isRequired,
};
心得:對React開發(fā)者而言介汹,static在一些老版本的瀏覽器上是不支持的却嗡。React Native開發(fā)者可以不用擔心這個問題。
2.在導入(import)與導出(export)組件上的不同
導入組件
ES5
在ES5里嘹承,如果使用CommonJS標準窗价,引入React包基本通過require進行,代碼類似這樣:
var React = require("react");
var {
Component,
PropTypes
} = React; //引用React抽象組件
var ReactNative = require("react-native");
var {
Image,
Text,
} = ReactNative; //引用具體的React Native組件
var AboutPage=require('./app/AboutPage') //引入app目錄下AboutPage組件赶撰,即AboutPag.js
var PopularPage=require('./app/PopularPage') //引入app目錄下PopularPage組件舌镶,即PopularPage.js
var FavoritePage=require('./app/FavoritePage') //引入app目錄下FavoritePage組件柱彻,即FavoritePage.js
ES6
在ES6里豪娜,沒有了require,而是使用import來導入組件哟楷,有點像Java的寫法瘤载。
import React, {
Component,
PropTypes,
} from 'react';//引用React抽象組件
import {
Image,
Text
} from 'react-native' //引用具體的React Native組件
import AboutPage from './app/AboutPage' //引入app目錄下AboutPage組件,即AboutPag.js
import PopularPage from './app/PopularPage' //引入app目錄下PopularPage組件卖擅,即PopularPage.js
import FavoritePage from './app/FavoritePage' //引入app目錄下FavoritePage組件鸣奔,即FavoritePage.js
另外,ES6支持將組件導入作為一個對象惩阶,使用“ * as”修飾即可挎狸。
//引入app目錄下AboutPage組件作為一個對象,接下來就可使用“AboutPage.”來調用AboutPage的方法及屬性了断楷。
import * as AboutPage from './app/AboutPage'
心得:使用“ * as ”修飾后锨匆,導入的組件直接被實例化成一個對象,可以使用“.”語法來調用組件的方法和屬性冬筒,和沒有“ * as ”修飾是有本質區(qū)別的恐锣,使用的時候要特別注意。
導出組件
ES5
在ES5里舞痰,要導出一個類給別的模塊用土榴,一般通過module.exports來導出:
var MyComponent = React.createClass({
...
});
module.exports = MyComponent;
ES6
在ES6里,通常用export default來實現(xiàn)相同的功能:
export default class MyComponent extends Component{
...
}
3.在初始化state上的不同
ES5
var Video = React.createClass({
getInitialState: function() {
return {
loopsRemaining: this.props.maxLoops,
};
},
})
ES6
ES6下响牛,有兩種寫法:
class Video extends React.Component {
state = {
loopsRemaining: this.props.maxLoops,
}
}
不過我們推薦更易理解的在構造函數(shù)中初始化(這樣你還可以根據(jù)需要做一些計算):
class Video extends React.Component {
constructor(props){
super(props);
this.state = {
loopsRemaining: this.props.maxLoops,
};
}
}
4.在方法作為回調上的不同
在開發(fā)工作中玷禽,經常會使用到回調,如按鈕的單擊回調等呀打,這也是在很多編程語言中都會經常出現(xiàn)的情況论衍。ES6與ES5在使用回調方面是有區(qū)別的。
ES5
var PostInfo = React.createClass({
handleOptionsButtonClick: function(e) {
// Here, 'this' refers to the component instance.
this.setState({showOptionsModal: true});
},
render: function(){
return (
<TouchableHighlight onPress={this.handleOptionsButtonClick}>
<Text>{this.props.label}</Text>
</TouchableHighlight>
)
},
});
在ES5中聚磺,React.createClass會把所有的方法都bind一遍坯台,這樣可以提交到任意的地方作為回調函數(shù),而this不會變化瘫寝。但官方現(xiàn)在逐步認為這反而是不標準蜒蕾、不易理解的稠炬。
在ES6下,你需要通過bind來綁定this引用咪啡,或者使用箭頭函數(shù)(它會綁定當前scope的this引用):
ES6
class PostInfo extends React.Component{
handleOptionsButtonClick(e){
this.setState({showOptionsModal: true});
}
render(){
return (
<TouchableHighlight
onPress={this.handleOptionsButtonClick.bind(this)}
//onPress={e=>this.handleOptionsButtonClick(e)}//這種方式和上面的效果是一樣的
>
<Text>{this.props.label}</Text>
</TouchableHighlight>
)
},·
}
參考
React's official site
React on ES6+
About
本文出自《React Native學習筆記》系列文章首启。
了解更多,可以關注我的GitHub
@http://jiapenghui.com
推薦閱讀
- React Native 學習筆記
- [Reac Native布局詳細指南](https://github.com/crazycodeboy/RNStudyNotes/tree/master/React Native布局/React Native布局詳細指南/React Native布局詳細指南.md)
- React Native調試技巧與心得
- React Native發(fā)布APP之簽名打包APK
- React Native應用部署撤摸、熱更新-CodePush最新集成總結