ReactFlow程式碼靜態檢查
Flow
Flow是Facebook開源的靜態程式碼檢查工具,他的作用是在執行程式碼之前對React元件以及Jsx語法進行靜態程式碼的檢查以發現一些可能存在的問題。Flow可以用於所有前端開發的專案而不僅僅侷限於React,碼友們可以到 官網仔細瞭解(友情提示:可能需要VPN,非常不穩定),本文只介紹如何配合React開發使用。
Flow僅僅是一個用於檢查的工具,安裝使用都很方便,使用時注意以下3點即可:
- 將Flow增加到我們的專案中。
- 確保編譯之後的程式碼移除了Flow相關的語法。
- 在需要檢查的地方增加了Flow相關的型別註解。(類似與Java的Annotation機制)
接下來我們來一一說明以上三點的具體內容。碼友們邊閱讀邊操作即可。
將Flow增加到我們的專案中
安裝最新版本的Flow:
Npm:
npm install --save-dev flow-bin
安裝完成之後在package.json檔案中增加執行指令碼:
{
// ...
"scripts": {
"your-script-name": "flow",
// ...
},
// ...
}
然後初始化Flow:
npm run flow init
執行完成後,Flow會在終端輸出一下內容:
> yourProjectName@1.0.0 flow /yourProjectPath
> flow "init"
然後在根目錄下生成一個名為 .flowconfig 的檔案,開啟之後是這樣的:
[ignore]
[include]
[libs]
[lints]
[options]
[strict]
基本上,配置檔案沒有什麼特殊需求是不用去配置的,Flow預設涵蓋了當前目錄之後的所有檔案。[include]用於引入專案之外的檔案。例如:
[include]
../otherProject/a.js
[libs]
他會將和當前專案平級的otherProject/a.js 檔案納入進來。關於配置檔案請看這裡。
編譯之後的程式碼移除Flow相關的語法
Flow在JavaScript語法的基礎上增加了一些 註解(annotation)進行了擴充套件。因此瀏覽器無法正確的解讀這些Flow相關的語法,我們必須在編譯之後的程式碼中(最終釋出的程式碼)將增加的Flow註解移除掉。具體方法需要看我們使用了什麼樣的編譯工具。下面將說明一些React開發常用的編譯工具
Create React App
如果你的專案是使用Create React App直接建立的。那麼移除Flow語法的事項就不用操心了,Create React App已經幫你搞定了這個事,直接跳過這一小節吧。
Babel
在15.x版本之前入坑React的碼友應該絕大部分都用的Babel作為語法糖編譯器,那個時候畢竟Create React App完全沒有成熟。如果使用Babel我們還需要安裝一個Babel對於Flow的preset:
npm install --save-dev babel-preset-flow
然後,我們需要在專案根目錄Babel的配置檔案 .babelrc 中新增一個Flow相關的preset:
{
"presets": [
"flow",
//other config
]
}
其他方式
如果你既沒有使用Create React App也沒使用Babel作為語法糖編譯器,那麼可以使用 flow-remove-types 這個工具在釋出之前移除Flow程式碼。
執行Flow
完成上述步驟之後,就可以開始執行flow了:
npm run flow
然後會輸類似一下的內容:
> yourProjectName@1.0.0 flow /yourProjectPath
> flow
Launching Flow server for /yourProjectPath
Spawned flow server (pid=10705)
Logs will go to /tmp/flow/zSworkzSchkuizSone-big-website.log
Monitor logs will go to /tmp/flow/zSworkzSchkuizSone-big-website.monitor_log
No errors!
第一次執行會生成很多臨時檔案比較慢,之後會快許多。
增加Flow註解
如果你瞭解C++/C#的超程式設計或者Java的Annotation,那麼理解Flow的Annotation就會非常輕鬆。大概就是在檔案、方法、程式碼塊之前增加一個註解(Annotation)用來告知Flow的執行行為。
首先,Flow只檢查包含 // @flow 註解的檔案。所以如果需要檢查,我們需要這樣編寫我們的檔案:
// @flow
import React from `react`
class MyComponent extends React.Component {
render(){
return (<div>MyComponent</div>)
}
}
export default MyComponent
然後我們再執行Flow就變成這樣的風格了:
> yourProjectName@1.0.0 flow /yourProjectPath
> flow
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ dev/src/home/test.js:5:21
Cannot use property Component [1] with less than 1 type argument.
dev/src/home/test.js
2│
3│ import React from `react`
4│
5│ class MyComponent extends React.Component {
6│ render(){
7│ return (<div>MyComponent</div>)
8│ }
/tmp/flow/flowlib_cc1898a/react.js
[1] 26│ declare class React$Component<Props, State = void> {
到這裡,Flow已經算是安裝成功了,接下來的事是要增加各種註解以加強型別限定或者引數檢測。之後的內容將簡要介紹flow的相關語法規則。
React元件引數檢查
React元件引數檢查介紹了React通過PropType機制限定使用者使用元件傳遞的引數型別以及範圍,但是PropType是一種執行檢測機制,在程式跑起來之後獲取到具體資料才會執行檢查。而Flow是靜態檢查,是在程式碼編譯執行之前進行一次檢查,兩者相輔相成互不干擾。
Props引數檢查
承接上面 MyComponent 的例子,我們引入Flow的註解對程式碼進行檢查:
// @flow
// flow的例子,可以看看和PropType的差異在哪
import React from `react`
type Props = {
num : number,
text : ?string
}
//通過<>引入Flow型別檢查
//可以直接寫成 React.Component<{num : number, text ?: string}>這樣的形式
class MyComponent extends React.Component<Props> {
render(){
return (<div>{this.props.num}{this.props.text}</div>)
}
}
export default MyComponent
然後在執行Flow,輸出了No Error。
然後我們使用這個元件:
// @flow
// flow的例子,可以看看和PropType的差異在哪
import React from `react`
type Props = {
num : number,
text : ?string
}
class MyComponent extends React.Component<Props> {
render(){
this.props.myValue;
return (<div>{this.props.num}{this.props.text}</div>)
}
}
//void 表示 undefined 不傳遞引數
//這裡傳遞型別發生錯誤
const UseComponent = (props : void) =>(<MyComponent num="2" text={2}/>)
export default UseComponent
執行flow之後輸出:
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ dev/src/home/test.js:12:20
Cannot get this.props.myValue because property myValue is missing in Props [1].
9│
[1] 10│ class MyComponent extends React.Component<Props> {
11│ render(){
12│ this.props.myValue;
13│ return (<div>{this.props.num}{this.props.text}</div>)
14│ }
15│ }
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ dev/src/home/test.js:17:40
Cannot create MyComponent element because:
• string [1] is incompatible with number [2] in property num.
• number [3] is incompatible with string [4] in property text.
[2] 6│ num : number,
[4] 7│ text : ?string
:
14│ }
15│ }
16│
[1][3] 17│ const UseComponent = (props : void) =>(<MyComponent num="2" text={2}/>)
18│
19│ export default UseComponent
Found 3 errors
輸出內容可以看出一共有2個錯誤欄輸出:
- 第一欄表示myValue並沒有宣告。
- 第二欄[1]違反了[2]的限定,[3]違反了[4]的限定。我們將元件變更為<MyComponent num={2} text=”2″/>即可檢查通過。
增加對State的檢查
React的資料通過兩處控制——props 和 state。Flow也提供了state資料的檢查,我們在例子中增加state檢查:
// @flow
// flow的例子,可以看看和PropType的差異在哪
import React from `react`
type Props = {
num : number,
text : ?string
}
type State = {
count: number,
};
class MyComponent extends React.Component<Props, State> {
constructor(...props){
super(...props)
this.state = {count:`1`}
}
render(){
return (<div>{this.props.num}{this.props.text}</div>)
}
}
const UseComponent = (props : void) =>(<MyComponent num={2} text="2"/>)
export default UseComponent
此時執行Flow會輸出:
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ dev/src/home/test.js:17:29
Cannot assign object literal to this.state because string [1] is incompatible
with number [2] in property count.
[2] 11│ count: number,
12│ };
13│
14│ class MyComponent extends React.Component<Props, State> {
15│ constructor(...props){
16│ super(...props)
[1] 17│ this.state = {count:`1`}
18│ }
19│
20│ render(){
檢測出state.count在建構函式中賦值的型別錯誤。
元件預設值
使用Flow後一樣可以使用預設值,但是必須要注意預設值的型別要和註解宣告的一致:
import * as React from `react`;
type Props = {
foo: number,
};
class MyComponent extends React.Component<Props> {
static defaultProps = {
foo: 42,
};
}
函式型別的元件
除了使用Class關鍵字,使用函式同樣可以構造一個React元件,配合Flow使用:
import React from `react`;
type Props = {//引數檢查
foo: number,
bar?: string,
};
function MyComponent(props: Props) {
return <div>{props.bar}</div>;
}
MyComponent.defaultProps = {
foo: 42 //指定預設值
};
React事件、子元件、高階元件檢查擴充套件
除了對單個元件基本的檢查,Flow還提供了對React事件、refs、子元件、高階元件、Redux。本文就不一一介紹了,有需要的碼友可以按照下面的資源清單去了解相關的內容:
型別檢查擴充套件
Flow會檢查所有的JavaScript基礎型別——Boolean、String、Number、null、undefined(在Flow中用void代替)。除此之外還提供了一些操作符號,例如例子中的 text : ?string,他表示引數存在“沒有值”的情況,除了傳遞string型別之外,還可以是null或undefined。需要特別注意的是,這裡的沒有值和JavaScript的表示式的“非”是兩個概念,Flow的“沒有值”只有null、void(undefined),而JavaScript表示式的“非”包含:null、undefined、0、false。
除了前面的例子中給出的各種型別引數,Flow還有更豐富的檢查功能,檢視 這裡 以瞭解更多內容。
React資料型別參考
對於Flow來說,除了常規的JavaScript資料型別之外,React也有自己特有的資料型別。比如React.Node、React.Key、React.Ref<>等。需要詳細瞭解的,可以檢視官閘道器於React型別的說明。
需要特別說明的是,如果所要使用React的型別,在通過ES6引入React物件時需要使用這樣的方式:
import * as React from `react`
//替換 import React from `react`
//或者單獨引入一個型別
//import type {Node} from `react
兩者的差異在於ES6的星號import的特性,使用*號會將一個檔案中的所有 export 內容組合成一個物件返回,而不使用星號僅僅能獲取到exprot default 那個原型。而引入Flow後不會修改React的預設匯出型別,因為預設匯出不一定是一個物件,他會通過export為React擴充套件更多的型別。
比如我們用React.Node限制render方法的返回型別:
import * as React from `react`
class MyComponent extends React.Component<{}> {
render(): React.Node {
// ...
}
}
遇到的一些問題
我在使用的過程中目前遇到的問題之一是import 樣式資源 或 圖片時報 “./xxx.scss. Required module not found” 的異常,檢視官方文件瞭解Flow只支援.js、.jsx、.mjs、.json的檔案,如果需要匯入其他檔案需要並支援需要擴充套件options。在.flowconfig新增options:
[ignore]
[include]
[libs]
[lints]
[options]
module.file_ext=.scss
[strict]
此外,某些IDE對Flow的支援不是很好。我目前所使用的webstorm 2017.3.5相對還不錯,不過切記要到File->Setting->Languages&Frameworks->Javascript中將version設定為Flow。
寫在最後的使用心得
引入並按照Flow的規範去約束每一個元件會導致開發量增加不少(當然你引入不用是另外一回事,但是不用引入他做什麼?)。搭建好Flow的框架僅僅是開始,之後除了團隊成員要去了解flow的使用方法,早期還會遇到各種坑需要去解決。而且Flow也要比React的 PropTypes ”重“許多。
JavaScript本來是一個型別推導的原型語言,弄個Flow進來搞得越來越像Java這種強型別語言,也不知道是好是壞,而Java10又學JavaScript等加入了val這種可以型別推導的關鍵字….。
總的來說引入規範是有成本的,具體要看團隊規模以及專案大小,不是引入越多的技術棧就越有逼格。如果你獨立專案的前端開發人數並不多,或者程式碼膨脹(程式碼腐爛)速度也沒有讓你措手不及,建議慎重引入Flow。個人覺得Flow除了開發人員自檢還要整合到整個測試框架中,在整合測試或某個版本的程式碼釋出之前進行集中檢查。需要思考它在專案的開發、測試、模擬、上線迭代週期中扮演的角色,甚至整合到類似與CMMI之類的管理流程去反向量化考核程式碼質量。
相關文章
- ESLint 靜態程式碼檢查EsLint
- python程式碼檢查工具(靜態程式碼審查)Python
- java靜態程式碼檢測-pmdJava
- 程式碼靜態掃描規則——型別轉換檢查型別
- React的靜態型別檢查React型別
- 選擇靜態程式碼安全檢測工具指南
- 靜態程式碼檢測工具(SAST)有哪些作用AST
- 大家有沒有關於 Python 執行起來不錯的程式碼檢查工具?用於 CICD 裡面的靜態程式碼檢查Python
- Vue中的靜態型別檢查Vue型別
- Android 隱私合規靜態檢查Android
- 靜態程式碼塊
- 機器學習&惡意程式碼靜態檢測機器學習
- 如何高效實施靜態程式碼檢測工具SAST?AST
- Flow_JS靜態型別檢查器JS型別
- 靜態程式碼安全檢測服務包括哪些內容?
- 靜態代理程式碼示例
- Java靜態程式碼塊Java
- 鴻蒙高質量程式碼靜態檢測200條一鴻蒙
- 鴻蒙高質量程式碼靜態檢測200條二鴻蒙
- 鴻蒙高質量程式碼靜態檢測200條三鴻蒙
- 鴻蒙高質量程式碼靜態檢測200條四鴻蒙
- Android 隱私合規靜態檢查實現(二)Android
- Model Inspector — 軟體模型靜態規範檢查工具模型
- 三分鐘從零單排js靜態檢查JS
- React Native工程中TSLint靜態檢查工具的探索之路React Native
- 程式碼樣式檢查
- 使用puppeteer爬蟲,檢查頁面靜態資源丟失爬蟲
- Vue eslint 程式碼檢查配置VueEsLint
- JS程式碼檢查工具ESLintJSEsLint
- CSS程式碼檢查工具stylelintCSS
- golangci-lint 程式碼檢查GolangGC
- 動態圖和靜態圖的程式碼區別
- Flow靜態型別檢查及在Vue專案中的使用型別Vue
- 何為程式碼檢查服務的門禁級檢查
- petite-vue原始碼剖析-從靜態檢視開始Vue原始碼
- (十八)前端使用eslint程式碼檢查前端EsLint
- android 程式碼質量檢查工具Android
- java 程式碼編譯檢查工具Java編譯