React Native填坑之旅--Flow篇
RZZMad
8年前
<p>flow不是React Native必会的技能,但是作为正式的产品开发优势很有必要掌握的技能之一。所以,算是RN填坑之旅系列的番外篇。</p> <p>Flow是一个静态的检查类型检查工具,设计之初的目的就是为了可以发现JavaScript脚本里不容易被发现的错误。在js开发的过程中,总会遇到一些问题。小的还可以,比如用alert或者console等输出一些信息可以debug,并解决。但是如果项目比较大的时候,这些手法只能起到一定的辅助作用。更有甚者,有些问题不运行到那段代码,根本不会发现错误。非死book的兄弟们就是为了解决这个问题,于是开发了flow。</p> <p>首先,跳转的你的项目目录下。然后就开始正文了。</p> <h2><strong>安装&配置</strong></h2> <p>Flow就从安装开始。</p> <pre> <code class="language-javascript">npm install --save-dev flow-bin</code></pre> <p>创建配置文件。</p> <pre> <code class="language-javascript">touch .flowconfig</code></pre> <p>先不管空白的 <em>.flowconfig</em> 配置文件。在 <em>package.json</em> 文件里flow脚本。</p> <p>your project/package.json</p> <pre> <code class="language-javascript">"scripts": { "flow": "flow; test $? -eq 0 -o $? -eq 2", },</code></pre> <p>然后给需要flow检查的文件里加上 //@flow 或者 /*@flow*/ 。然后就可以检查了。(也可以在命令中加上--all, 这样就会检查所有文件)。</p> <p>在根目录下运行命令:</p> <pre> <code class="language-javascript">npm run flow</code></pre> <h2><strong>用Flow检查</strong></h2> <p>现在就把flow用起来。Flow绝不是上面几个命令而已,而是一套类型体系。下面通过一个例子来了解一下flow和flow的类型体系。</p> <pre> <code class="language-javascript">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 }; }</code></pre> <p>第一次检查会出很多的错,因为上面的写法没有按照flow的定义添加类型声明。下面来添加类型声明。</p> <pre> <code class="language-javascript">// @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 }; }</code></pre> <p>运行命令check命令之后会显示错误的内容:</p> <pre> <code class="language-javascript">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 </code></pre> <p>flow的错误提示我们,需要给出方法的参数类型。</p> <h3><strong>Flow: Any类型</strong></h3> <p>修改代码:</p> <pre> <code class="language-javascript">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; }</code></pre> <p>这样修改之后就没有什么错误提示了。我们给参数指定了 <strong>any</strong> 类型。这个类型是所有类型的父类型,也是所有类型的子类型。所以,任何类型都可以用 <strong>any</strong> 代表了。但是这样并不能发挥类型检查的优势。</p> <h3><strong>Flow:类型别名</strong></h3> <p>使用flow的类型别名可以解决上面的问题。输出的默认方法的第一个参数其实是一个State类型的实例。在本例中使用的State是一个对象,其中 tracks 是一个数组, activeTrack 是一个可以为空的对象。为State定义一个类型别名:</p> <pre> <code class="language-javascript">type State = { tracks: Array<any>, activeTrack: ?any }; const initialState = { tracks: [], activeTrack: null };</code></pre> <p>正好之前定义的 initialState 就是 State 类型的一个实例。同理,我们也可以为 initialState 的 activeTrack 定义一个类型。</p> <pre> <code class="language-javascript">type Track = { //这里给出定义 }</code></pre> <p>然后 State 类型就是这样的了:</p> <pre> <code class="language-javascript">type Track = { //这里给出定义 } type State = { tracks: Array<any>, activeTrack: ?Track };</code></pre> <p>注意,这里我们用到了一个特殊的类型: <strong>Maybe Type</strong> (可能类型或者可空类型)。这个类型的定义方式就是在类型的前面放一个问号。</p> <p>下面也为两个方法 setTracks 和 setPlay 定义返回的类型,并应用到对应的方法上:</p> <pre> <code class="language-javascript">// @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 }; }</code></pre> <h3>Flow: Type Union</h3> <p>SetTrackAction 和 PlayTrackAction 可以使用 <strong>Type Union</strong> 的方式统一起来:</p> <pre> <code class="language-javascript">type Action = SetTrackAction | PlayTrackAction;</code></pre> <p>修改代码:</p> <pre> <code class="language-javascript">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 }; }</code></pre> <h3><strong>Flow: 模块处理</strong></h3> <p>这里主要说明一种情况。如果引入的另外一个模块和本模块定义了一个同名的类型别名,但是里面包含的内容不同,那么Flow会检查出来并报错。</p> <p>比如,现在我们的模块里有了 Track 这个类型,是这样的:</p> <pre> <code class="language-javascript">type Track = { trackCode: string };</code></pre> <p>如果在引入的 <em>actionTypes.js</em> 文件中也包含一个 Track 类型,但是定义的有些不同:</p> <pre> <code class="language-javascript">type Track = { trackCode: number };</code></pre> <p>两个 Track 的不同就在于 trackCode 的类型,一个是 <strong>string</strong> ,一个是 <strong>number</strong> 。</p> <p>运行flow之后就会显示出来具体的报错:</p> <pre> <code class="language-javascript">test/actionTypes.js:13 13: trackCode: 123 ^^^ number. This type is incompatible with the expected return type of 5: trackCode: string ^^^^^^ string </code></pre> <h3><strong>Flow: 声明类型</strong></h3> <p>上面的问题解决起来很简单,把两个类型的定义保持一致就可以。但是,我们不可能在任何一个需要 Track 类型的文件中都定义一个一模一样的类型。</p> <p>Flow提供了一种特殊的类型声明方式,可以一次声明到处使用。</p> <p>在 <em>.flowconfig</em> 文件中的[lib]下添加如下内容。如果这个文件为空的话,运行 flow init 命令。</p> <pre> <code class="language-javascript">[libs] decls</code></pre> <p>在根目录下:</p> <pre> <code class="language-javascript">mkdir decls cd decls touch flowTypes.js</code></pre> <p>在文件 <em>flowType.js</em> 中:</p> <pre> <code class="language-javascript">declare type Track = { trackCode: string; };</code></pre> <p>把其他的 Track 类型声明全部都删掉,然后运行命令:</p> <pre> <code class="language-javascript">npm run flow</code></pre> <h3><strong>最后</strong></h3> <p>Flow对React的支持与上文所述的基本上大同小异。</p> <p> </p> <p>来自:https://segmentfault.com/a/1190000007110697</p> <p> </p>