原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處
代碼規(guī)范篇
JavaScript是否需要使用分號(hào)?
我的回答是:不需要分號(hào)。
這個(gè)是討論很火的一個(gè)話題。我剛從Objective-C轉(zhuǎn)到JavaScript的時(shí)候,很不習(xí)慣省略分號(hào)。但是沒(méi)過(guò)多久,我就把項(xiàng)目里所有的分號(hào)全部去掉了。原因很簡(jiǎn)單,明明是可以省去的工作量,同時(shí)又不會(huì)造成不良影響的事情。為什么不去省呢?
就像Objective-C剛推出ARC(自動(dòng)內(nèi)存回收)的時(shí)候,很多“老人”覺(jué)得ARC不放心,堅(jiān)持要使用MRC(手動(dòng)內(nèi)存回收)。我猜想現(xiàn)在還在堅(jiān)持使用MRC的人,可能不到1%了吧。
代碼的縮進(jìn),使用4個(gè)空格,還是2個(gè)空格?
我的回答是:2個(gè)空格。
跟上面一樣,我剛轉(zhuǎn)到JavaScript的時(shí)候,覺(jué)得2個(gè)空格很反人類,太擠了。所以一直堅(jiān)持4個(gè)空格。直到UI代碼慢慢變多,嵌套變深。。。我才開(kāi)始慢慢懷疑自己。前幾天,我搜索了多個(gè)熱門的JavaScript開(kāi)源項(xiàng)目,無(wú)一例外使用的是2個(gè)空格。所以,我果斷把代碼改成2個(gè)空格。
UI布局篇
如何根據(jù)不同屏幕尺寸,做等比例布局?
美工出標(biāo)注圖的時(shí)候,通常是按照屏幕寬度750出的。如果需求是UI根據(jù)不同屏幕尺寸,做等比例的縮放。我們可以寫一個(gè)叫px2dp的公共方法
import {Dimensions} from 'react-native'
const basePixelWidth = 750
const px2dp = (px: number): number => {
return px * Dimensions.get('window').width / basePixelWidth
}
比如要設(shè)置View的寬度為屏幕的一半。就可以這樣:
<View style={{width: px2dp(375)}} />
如何快速獲取屏幕寬高?
常見(jiàn)的做法,是通過(guò)react native提供的Dimensions獲取
const screenWidth = Dimensions.get('window').width
const screenHeight = Dimensions.get('window').height
我們可以做一個(gè)簡(jiǎn)單的封裝,創(chuàng)建一個(gè)screen.js文件
import {Dimensions, PixelRatio} from 'react-native'
const screenWidth = Dimensions.get('window').width
const screenHeight = Dimensions.get('window').height
let screen = {
width: screenWidth,
height: screenHeight,
}
export default screen
然后在外部,就可以通過(guò)screen.width或者screen.height獲取寬高了。
如何判斷設(shè)備是iPhoneX?
我們有時(shí)候需要對(duì)iPhoneX的異形屏做一些特殊的適配,就需要判斷一個(gè)設(shè)備是否是iPhoneX
import {Platform, Dimensions} from 'react-native'
const isIphoneX = () => {
let dimen = Dimensions.get('window')
return (
Platform.OS === 'ios' &&
!Platform.isPad &&
!Platform.isTVOS &&
(dimen.height === 812 || dimen.width === 812)
)
}
如果你比較愛(ài)偷懶,可以順便再封裝幾個(gè)方法
// 判斷是否是iOS設(shè)備
const isIOS = () => {
return Platform.OS === 'ios'
}
// 是否是Android設(shè)備
const isAndroid = () => {
return Platform.OS === 'android'
}
// 狀態(tài)欄的高度
const statusBarHeight = () => (isIOS() ? (isIphoneX() ? 44 : 20) : 0)
如何優(yōu)雅的為每個(gè)頁(yè)面增加Loading頁(yè)、錯(cuò)誤頁(yè)?
很多App大部分頁(yè)面都需要用到Loading頁(yè)。如果數(shù)據(jù)獲取失敗,還需要顯示錯(cuò)誤頁(yè),告訴用戶錯(cuò)誤原因,并且讓用戶點(diǎn)擊重試。這時(shí)候,我們可以定義一個(gè)通用的組件叫Scene,然后在這個(gè)類中,定義Loading頁(yè)面和錯(cuò)誤頁(yè)。使用的時(shí)候,只需要在我們的component這樣實(shí)現(xiàn)
render() {
let {isLoading, error} = this.state
return (
<Scene
isLoading={isLoading}
error={error}
onRetry={this.requestData}
>
...
</Scene>
)
}
當(dāng)我們需要請(qǐng)求網(wǎng)絡(luò)的時(shí)候,把isLoading設(shè)置為true。這樣Scene組件就會(huì)為我們展示一個(gè)Loading頁(yè)面。
當(dāng)網(wǎng)絡(luò)請(qǐng)求失敗的時(shí)候,把isLoading設(shè)置為false,error設(shè)置為具體的錯(cuò)誤信息。Scene組件就會(huì)顯示一個(gè)錯(cuò)誤頁(yè),并且有一個(gè)“重試”按鈕。用戶點(diǎn)擊“重試”后,會(huì)調(diào)用onRetry回調(diào)。
Scene的代碼簡(jiǎn)化后,大概是這樣
type Props = {
isLoading?: boolean,
error?: ?string,
onRetry?: Function,
children?: any,
style?: Object,
}
class Scene extends PureComponent<Props> {
render() {
let {isLoading, style, error, children, onRetry} = this.props
return (
<View style={[styles.container, style]}>
{!isLoading && children}
<ErrorView text={error} onPress={onRetry} />
<LoadingView isLoading={isLoading} />
</View>
)
}
}
這里需要注意的是,ErrorView、LoadingView需要使用下面這種絕對(duì)布局。否則可能會(huì)被頁(yè)面的內(nèi)容頂?shù)狡聊煌饷妗?/p>
{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}
我們還可以給Scene增加其他的功能,比如一個(gè)通用的HUD,空數(shù)據(jù)頁(yè)面等等。
做布局的時(shí)候,需要用到大量的style,這些style我要寫在哪里呢?
首先,我強(qiáng)烈反對(duì)把所有的style放在一個(gè)文件中。這對(duì)維護(hù)來(lái)說(shuō)是一個(gè)噩夢(mèng)。我喜歡把每個(gè)component特有的style,放在該component所在的文件中。比如下面的container樣式是Scene這個(gè)組件獨(dú)有的,就可以直接寫在Scene中。
class Scene extends PureComponent<{}> {
render() {
return (
<View style={styles.container}>
...
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.paper,
marginTop:20,
},
})
如果是多個(gè)頁(yè)面都需要用到的style,這時(shí)候考慮兩種情況。
第一,如果這個(gè)style非常非常通用,比如一個(gè)分隔線的樣式。那么優(yōu)先考慮單獨(dú)抽出一個(gè)分割線的類,比如這樣
class Separator extends PureComponent<{}> {
render() {
return (
<View style={styles.separator} />
)
}
}
const styles = StyleSheet.create({
separator: {
height: StyleSheet.hairlineWidth,
backgroundColor: Colors.line,
}
})
第二,如果這個(gè)style只是在部分頁(yè)面中出現(xiàn)。那么可以創(chuàng)建一個(gè)styles.js文件,在里面定義通用的樣式,比如:
let styles = StyleSheet.create({
border: {
borderWidth: StyleSheet.hairlineWidth,
borderColor: Colors.border,
},
line: {
height: StyleSheet.hairlineWidth,
backgroundColor: Colors.line,
},
fillParent: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
},
list: {
marginHorizontal: px(34),
marginTop: px(38),
borderWidth: StyleSheet.hairlineWidth,
borderColor: Colors.border,
borderRadius: 3,
shadowColor: 'black',
shadowOffset: {
width: 2,
height: 2,
},
shadowOpacity: 0.07,
shadowRadius: 2,
},
// ...
}
export default styles
Flow篇
flow是什么?
flow是有facebook推出的JavaScript靜態(tài)類型檢查工具。弱類型語(yǔ)言有個(gè)很大的問(wèn)題,就是一些類型錯(cuò)誤引起的bug,可能產(chǎn)品上線后才會(huì)被發(fā)現(xiàn)。導(dǎo)致很多事故的發(fā)生。
flow很好的解決了JavaScript弱類型引發(fā)的一系列問(wèn)題。對(duì)于習(xí)慣了強(qiáng)類型語(yǔ)言的我來(lái)說(shuō),只有借助flow這種類型檢查工具,才能找到安全感。這里不對(duì)flow的用法進(jìn)行詳細(xì)的介紹。只是說(shuō)一下一些使用的技巧。具體的使用可以查看官方網(wǎng)站
我的項(xiàng)目是否需要flow?
可能有人會(huì)問(wèn):我的項(xiàng)目很小,只有我一個(gè)人開(kāi)發(fā)。而且我對(duì)自己的編碼能力很有自信。那么我是否需要使用flow呢?
我認(rèn)為,如果你是一個(gè)剛剛接觸React Native的新手,對(duì)很多東西還不了解。那么我建議你可以先把flow放在一邊。
如果你接觸React Native已經(jīng)有一段時(shí)間,已經(jīng)可以開(kāi)發(fā)一個(gè)簡(jiǎn)單的頁(yè)面了。那么,我強(qiáng)烈建議你學(xué)習(xí)下flow。剛開(kāi)始使用flow的時(shí)候,可能會(huì)增加一定的工作量。但是當(dāng)flow替你找出代碼里一個(gè)又一個(gè)潛在問(wèn)題的時(shí)候,你會(huì)慶幸你使用了flow。而不是等到代碼上線后bug才被發(fā)現(xiàn)。
如何優(yōu)雅的定義props和state?
比如這樣一個(gè)復(fù)選框的控件
它需要外部傳入標(biāo)題。那么可以定義一個(gè)叫title的prop,它是string類型的:
class Checkbox extends PureComponent<{title:string}> {
....
}
當(dāng)然,我更喜歡這樣定義:
type Props = {
title: string,
}
class Checkbox extends PureComponent<Props> {
因?yàn)槿绻枰獋魅氲膶傩苑浅6啵褂玫谝环N方式的話,可讀性就會(huì)大打折扣。
復(fù)選框控件還需要一個(gè)叫做checked的state,表示是否是選中狀態(tài):
class Checkbox extends PureComponent<{title:string},{checked:boolean}> {
// ...
}
同樣,我更喜歡在外部定義:
type Props = {
title: string,
}
type State = {
checked: boolean,
}
class Checkbox extends PureComponent<Props, State> {
// ...
}
這里props是必須定義的,而State是可選的。所以如果你的組件不需要接收props,可以這么寫:
class Checkbox extends PureComponent<{}> {
// ...
}
具體props和state的定義,可以查看官方文檔
如何定義redux的state類型?
定義state的方式跟定義其他普通的對(duì)象一樣
type State = {
users: Array<{
id: string,
name: string,
age: number,
phoneNumber: string,
}>,
activeUserID: string,
// ...
};
或者這樣
type User = {
id: string,
name: string,
age: number,
phoneNumber: string,
}
type State = {
users: Array<User>,
activeUserID: string,
// ...
}
如何定義redux的action類型?
我們項(xiàng)目中肯定不止只用一個(gè)action,所以需要使用連接符,把多個(gè)action類型合并起來(lái)
type FooAction = { type: "FOO", foo: number }
type BarAction = { type: "BAR", bar: boolean }
type Action =
| FooAction
| BarAction
定義redux的state是很有必要的。因?yàn)槟阈枰?jīng)常回過(guò)頭查看state的結(jié)構(gòu)。至于action嘛。。。在小項(xiàng)目中,可以暫時(shí)用Object或者any敷衍一下。
如何定義redux的reducer類型?
reducer使用到了我們剛才定義的state和action類型
function reducer(state: State, action: Action): State {
// ...
}
如果你比較喜歡用箭頭函數(shù):
const reducer = (state: State, action: Action): State => {
// ...
}
最后
今天是2月14情人節(jié),我因?yàn)閿?shù)月前騎電動(dòng)車光榮負(fù)傷,至今出不了門。
謹(jǐn)以此文獻(xiàn)給我的太太,情人節(jié)快樂(lè) :)