flow不是React Native必會的技能,但是作為正式的產品開發優勢很有必要掌握的技能之一。所以,算是RN填坑之旅系列的番外篇。
Flow是一個靜態的檢查類型檢查工具,設計之初的目的就是為了可以發現JavaScript腳本里不容易被發現的錯誤。在js開發的過程中,總會遇到一些問題。小的還可以,比如用alert或者console等輸出一些信息可以debug,並解決。但是如果項目比較大的時候,這些手法只能起到一定的輔助作用。更有甚者,有些問題不運行到那段代碼,根本不會發現錯誤。Facebook的兄弟們就是為了解決這個問題,於是開發了flow。
首先,跳轉的你的項目目錄下。然后就開始正文了。
安裝&配置
Flow就從安裝開始。
npm install --save-dev flow-bin
創建配置文件。
touch .flowconfig
先不管空白的.flowconfig配置文件。在package.json文件里flow腳本。
your project/package.json
"scripts": {
"flow": "flow; test $? -eq 0 -o $? -eq 2",
},
然后給需要flow檢查的文件里加上//@flow
或者/*@flow*/
。然后就可以檢查了。(也可以在命令中加上--all, 這樣就會檢查所有文件)。
在根目錄下運行命令:
npm run flow
用Flow檢查
現在就把flow用起來。Flow絕不是上面幾個命令而已,而是一套類型體系。下面通過一個例子來了解一下flow和flow的類型體系。
export default function(state = initialState, action) {
switch (action.type) {
case actionTypes.TRACKS_SET:
return setTracks(state, action);
case actionTypes.TRACK_PLAY:
return setPlay(state, action);
}
return state;
}
function setTracks(state, action) {
const { tracks } = action;
return { ...state, tracks };
}
第一次檢查會出很多的錯,因為上面的寫法沒有按照flow的定義添加類型聲明。下面來添加類型聲明。
// @flow
import * as actionTypes from './actionTypes';
const initialState = {
tracks: [],
activeTrack: null
};
export default function(state = initialState, action) {
switch (action.type) {
case actionTypes.TRACKS_SET:
return setTracks(state, action);
case actionTypes.TRACK_PLAY:
return setPlay(state, action);
}
return state;
}
function setTracks(state, action) {
const { tracks } = action;
return { ...state, tracks };
}
function setPlay(state, action) {
const { track } = action;
return { ...state, activeTrack: track };
}
運行命令check命令之后會顯示錯誤的內容:
test/track.js:10
10: export default function(state = initialState, action) {
^^^^^ parameter `state`. Missing annotation
test/track.js:10
10: export default function(state = initialState, action) {
^^^^^^ parameter `action`. Missing annotation
flow的錯誤提示我們,需要給出方法的參數類型。
Flow: Any類型
修改代碼:
export default function(state: any = initialState, action: any) {
switch (action.type) {
case actionTypes.TRACKS_SET:
return setTracks(state, action);
case actionTypes.TRACK_PLAY:
return setPlay(state, action);
}
return state;
}
這樣修改之后就沒有什么錯誤提示了。我們給參數指定了any類型。這個類型是所有類型的父類型,也是所有類型的子類型。所以,任何類型都可以用any代表了。但是這樣並不能發揮類型檢查的優勢。
Flow:類型別名
使用flow的類型別名可以解決上面的問題。輸出的默認方法的第一個參數其實是一個State類型的實例。在本例中使用的State是一個對象,其中tracks
是一個數組,activeTrack
是一個可以為空的對象。為State定義一個類型別名:
type State = {
tracks: Array<any>,
activeTrack: ?any
};
const initialState = {
tracks: [],
activeTrack: null
};
正好之前定義的initialState
就是State
類型的一個實例。同理,我們也可以為initialState
的activeTrack
定義一個類型。
type Track = {
//這里給出定義
}
然后State
類型就是這樣的了:
type Track = {
//這里給出定義
}
type State = {
tracks: Array<any>,
activeTrack: ?Track
};
注意,這里我們用到了一個特殊的類型:Maybe Type(可能類型或者可空類型)。這個類型的定義方式就是在類型的前面放一個問號。
下面也為兩個方法setTracks
和setPlay
定義返回的類型,並應用到對應的方法上:
// @flow
import * as actionTypes from './actionTypes';
type Track = {
trackName: string
};
type State = {
tracks: Array<any>,
activeTrack: ?Track
};
type SetTrackAction = {
type: string,
tracks: Array<Track>
};
type PlayTrackAction = {
type: string,
track: Track
};
const initialState = {
tracks: [],
activeTrack: null
};
export function setTracks(tracks: Array<Track>): SetTrackAction {
return {
type: actionTypes.TRACKS_SET,
tracks
};
}
export function setPlay(track: Track): PlayTrackAction {
return {
type: actionTypes.TRACK_PLAY,
track: track
};
}
Flow: Type Union
SetTrackAction
和PlayTrackAction
可以使用Type Union的方式統一起來:
type Action = SetTrackAction | PlayTrackAction;
修改代碼:
export function setTracks(tracks: Array<Track>): Action {
return {
type: actionTypes.TRACKS_SET,
tracks
};
}
export function setPlay(track: Track): Action {
return {
type: actionTypes.TRACK_PLAY,
track: track
};
}
Flow: 模塊處理
這里主要說明一種情況。如果引入的另外一個模塊和本模塊定義了一個同名的類型別名,但是里面包含的內容不同,那么Flow會檢查出來並報錯。
比如,現在我們的模塊里有了Track
這個類型,是這樣的:
type Track = {
trackCode: string
};
如果在引入的actionTypes.js文件中也包含一個Track
類型,但是定義的有些不同:
type Track = {
trackCode: number
};
兩個Track
的不同就在於trackCode
的類型,一個是string,一個是number。
運行flow之后就會顯示出來具體的報錯:
test/actionTypes.js:13
13: trackCode: 123
^^^ number. This type is incompatible with the expected return type of
5: trackCode: string
^^^^^^ string
Flow: 聲明類型
上面的問題解決起來很簡單,把兩個類型的定義保持一致就可以。但是,我們不可能在任何一個需要Track
類型的文件中都定義一個一模一樣的類型。
Flow提供了一種特殊的類型聲明方式,可以一次聲明到處使用。
在.flowconfig文件中的[lib]下添加如下內容。如果這個文件為空的話,運行flow init
命令。
[libs]
decls
在根目錄下:
mkdir decls
cd decls
touch flowTypes.js
在文件flowType.js中:
declare type Track = {
trackCode: string;
};
把其他的Track
類型聲明全部都刪掉,然后運行命令:
npm run flow
最后
Flow對React的支持與上文所述的基本上大同小異。各位可以移步官網細看。