React Native填坑之旅--Flow篇(番外)


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類型的一個實例。同理,我們也可以為initialStateactiveTrack定義一個類型。

type Track = {
  //這里給出定義
}

然后State類型就是這樣的了:

type Track = {
  //這里給出定義
}

type State = {
  tracks: Array<any>,
  activeTrack: ?Track
};

注意,這里我們用到了一個特殊的類型:Maybe Type(可能類型或者可空類型)。這個類型的定義方式就是在類型的前面放一個問號。

下面也為兩個方法setTrackssetPlay定義返回的類型,並應用到對應的方法上:

// @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

SetTrackActionPlayTrackAction可以使用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的支持與上文所述的基本上大同小異。各位可以移步官網細看。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM