vue項目配置使用flow類型檢查
你是否經常在debug那些簡單可避免的bug?可能你給函數傳參的時候搞錯了參數的順序,或者本來應該傳個Number類型的參數,你傳了一個String類型?JavaScript的弱類型是這一類bug的罪魁禍首,靜態類型語言中不存在此類bug。Flow就是JavaScript的靜態類型檢查工具,該庫的目標在於檢查JavaScript中的類型錯誤,開發者通常不需要修改代碼即可使用,故使用成本很低。同時,它也提供額外語法支持,使得開發者能更大程度地發揮Flow的作用。
一、flow的安裝
flow可以直接通過npm或者yarn安裝。
這裏以npm為例:npm install --save-dev flow-bin
安裝完成後在package.json中加入下面的腳本
"scripts": {
"flow":"flow check"
}
同時還要安裝babel編譯器,將flow的類型檢查代碼從代碼中剝離,轉變成正常的js代碼
npm install --save-dev babel-cli babel-preset-flow
在babel配置文件.babelrc中加入
{
"presets": ["flow"]
}
二、flow使用
1、配置flow
npm run flow init
生成flow配置文件.flowconfig
[ignore]
.*/node_modules/.*
. */test/.*
.*/build/.*
.*/config/.*
[include]
[libs]
[options]
module.file_ext=.vue
module.file_ext=.js
[ignore]:Flow 默認檢查項目目錄下所有文件,但是有很多文件必定是我們不想檢查的,就像 node_modules、build 等目錄下的文件,所以我們需要在將這些目錄寫在 ignore 配置下。
[include]:所謂的項目目錄其實是 .flowconfig 目錄,並不是真正的項目目錄,如果我們在這個項目中的某個目錄下創建一個 .flowconfig,那麽 .flowconfig 所在的目錄也會變為一個 Flow 項目。那麽,如果我們想對當前 Flow 項目以外的文件或者目錄進行檢查,需要把它們寫在 include 配置項中。
[libs]:在項目中,我們可能會用到很多自定義的類型,比如說要記錄對象的結構,它可能在每個文件中都會被運用到,我們將其抽取為全局的類型或數據結構,在任何文件都可以使用。為了管理方便,我們將全局類型都定義在一個或多個單純的目錄中統一管理。這裏存放的有可能是一個定義好的數據結構,存放的也有可能是根據項目中某個類對應的數據類型。我們將這些文件或目錄寫在 libs 配置項中,這個配置對於我們使用 Flow 來說很重要。
[options]:這裏填寫對 Flow 項目的一些配置,配置項以key=value的形式,每行寫一個。所有的配置見官方文檔
[lints]:官網中沒有提到 lints 相關的配置
2、新建一個文件index.js
// @flow
let a:number = ‘3‘;
// @flow
或者 /* @flow */,
告訴flow檢查這個文件
如果不願意那麽麻煩,想檢查全部文件,那麽可以修改配置文件.flowconfig,在[options]配置項中添加 all=ture。
[ignore]
[include]
[libs]
[options]
all=true
[lints]
[libs] 配置項中的文件不需要添加// @flow,它們都將被檢查。
註:在vue單文件組件使用flow需要額外配置:
(1)在.flowconfig文件的[options]中配置.vue文件擴展名,module.file_ext=.vue
(2)在.vue文件中需註釋掉template script styled標簽
輸入npm run flow 執行類型檢查。
註:完成設置之後,在終端輸入以下命令可以在你的項目根目錄以及任何子目錄文件夾下進行專門的類型檢查:npm run flow check,
但是,這並不是最高效的使用方式,因為每次Flow都會重新檢查整個項目的所有文件。開發過程中,推薦啟動Flow服務:Flow服務的工作方式是增量檢查,也就是說它只檢查變化的部分。在終端輸入以下命令來啟動Flow服務:npm run flow,
首次運行該命令時,服務啟動並且顯示最初類型檢查結果。這保證了Flow更高效的增量式工作流。然後接下來每次想要知道檢測結果,只要輸入flow
命令即可。開發結束之後,輸入npm run flow stop
停止服務。
Flow的類型檢查是可選的,並不需要一次性檢查所有代碼。你可以選擇你想要檢查的文件,只要在對應的JavaScript文件最前面加上帶有@flow
標識的註釋即可:/*@flow*/,
當你想在已有項目中加入Flow的時候,該特性特別有幫助。因為你可以一一選擇並檢測你要的文件,然後修正錯誤。
三、類型推斷
通常,類型檢查分為以下兩種方式:
通過註釋:事先註釋好我們期待的類型,Flow就會基於這些註釋來評估
通過代碼推斷:通過變量的使用上下文來推斷出變量類型,然後根據這些推斷來檢查類型
第一種方式,我們需要額外編寫只在開發階段起作用的代碼,最後在代碼編譯打包的階段被剔除。顯然,這種額外添加類型註釋的方式增加了工作量。
第二種方式,不需要任何代碼修改即可進行類型檢查,最小化開發者的工作量。它不會強制你改變開發習慣,因為它會自動推斷出變量的類型。這就是所謂的類型推斷,Flow最重要的特性之一。
我們來通過一個例子來說明這個特性:
/*@flow*/
function foo(x) {
return x.split(‘ ‘);
}
foo(34);
當你在終端運行npm run flow
命令的時候,上述代碼會報錯,因為函數foo()
的期待參數是字符串,而我們輸入了數字。錯誤信息類似如下:
index.js:4
4: return x.split(‘ ‘);
^^^^^ property `split`. Property not found in
4: return x.split(‘ ‘);
^ Number
上述信息清楚地指出了出錯位置和錯誤原因。我們只要將參數變成字符串,即可修正錯誤。該例想說明的是,因為split()
方法只適用於string
類型的變量,所以x
應該是string
,這就是類型推斷。
四、空類型
Flow處理null
的方式與其他類型庫不同。它不會忽略null
,這樣可以防止了因給變量傳了null
而導致程序崩潰的錯誤。
/*@flow*/
function stringLength (str) {
return str.length;
}
var length = stringLength(null);
Flow會報錯。為了防止出錯,我們需要單獨處理null
。
/*@flow*/
function stringLength (str) {
if (str !== null) {
return str.length;
}
return 0;
}
var length = stringLength(null);
代碼中我們引入對null
的檢查,確保代碼能在任何情況下都正常且正確運行。上述代碼可以通過Flow的類型檢查。
五、類型註釋
如上所述,類型推斷是Flow最有用的特性之一,不需要編寫類型註釋就能獲取有用的反饋。但在某些特定的場景下,添加類型註釋可以提供更好更明確的檢查依據。考慮以下代碼:
/*@flow*/
function foo(x, y){
return x + y;
}
foo(‘Hello‘, 42);
Flow檢查上述代碼時檢查不出任何錯誤,因為+
即可以用在字符串上,也可以用在數字上,我們並沒有明確指出add()
的參數必須為數字。
在這種情況下,我們可以借助類型註釋來指明期望的類型。類型註釋是以冒號:
開頭,可以在函數參數,返回值,變量聲明中使用。如果我們在上段代碼中添加類型註釋,就會變成如下:
/*@flow*/
function foo(x : number, y : number) : number {
return x + y;
}
foo(‘Hello‘, 42);
現在Flow就能檢查出錯誤,因為函數參數的期待類型為數字,而我們提供了字符串。Flow報錯信息類似如下:
index.js:7
7: foo(‘Hello‘, 42);
^^^^^^^ string. This type is incompatible with the expected param type of
3: function foo(x : number, y : number) : number{
^^^^^^ number
如果傳入的參數是數字,就不會有錯誤。類型註釋在大型復雜的JavaScript文件中也很有用,它能保證代碼按照預期進行。
六、Flow能支持的其他更多類型註釋。
其實說到底就是類似java那種強語言類型的寫法,給每個變量聲明是什麽類型,給每個函數聲明返回值類型,給每個數組元素聲明類型等,就是仿造java的寫法,哈哈,java傳值不對時就會給你報錯一樣的道理
1、函數
/*@flow*/
function add(x : number, y : number) : number {
return x + y;
}
add(3, 4);
上述代碼展示了變量類型註釋以及函數類型註釋。函數add()
的參數,以及函數的返回值,期待類型為數字。如果傳入其他類型參數,Flow就會檢測到錯誤。
2、數組
var foo : Array<number> = [1,2,3];
數組類型註釋的格式是Array<T>
,T
表示數組中每項的數據類型。在上述代碼中,foo
是每項均為數字的數組。
3、類
下面展示了類和對象的類型註釋模型。唯一需要註意的是,可以在兩個類型之間使用或邏輯,用|
來間隔。變量bar1
添加了必須為Bar
類的類型註釋。
class Bar{
x:string; // x should be string
y:string | number; // y can be either a string or a number
constructor(x,y){
this.x=x;
this.y=y;
}
}
var bar1 : Bar = new Bar("hello",4);
4、對象字面量
對象的類型註釋類似於類,指定對象屬性的類型。
var obj : {a : string, b : number, c: Array<string>, d : Bar} = {
a : "hello",
b : 42,
c : ["hello", "world"],
d : new Bar("hello",3)
}
5、Null
若想任意類型,T
可以為null
或者undefined
,只需類似如下寫成 ?T
的格式即可。
/*@flow*/
var foo : ?string = null;
此時,foo
可以為字符串,也可以為null
。
目前我們只對Flow的類型註釋做了很淺的探索。一旦你習慣了使用這些基本類型,建議在Flow官網上的類型文檔深入了解所有的類型。
七、庫定義
我們經常需要引入第三方庫,Flow檢查時就會拋出錯誤。但這並不是我們期待的錯誤。
慶幸的是,我們不需要修改庫源碼去防止這些報錯。我們只需創建一個庫定義(libdef)。libdef是包含第三方庫聲明的JS文件簡稱。觀察下面的例子:
/* @flow */
var users = [
{ name: ‘John‘, designation: ‘developer‘ },
{ name: ‘Doe‘, designation: ‘designer‘ }
];
function getDeveloper() {
return _.findWhere(users, {designation: ‘developer‘});
}
Flow會檢查出以下錯誤:
interfaces/app.js:9
9: return _.findWhere(users, {designation: ‘developer‘});
^ identifier `_`. Could not resolve name
由於Flow並不認識_
,所以會報錯。要解決這個問題,我們需要引入Underscore的庫定義
1、使用flow-typed
flow-typed倉庫包含了眾多流行的第三方庫的libdef。只需在項目根目錄下創建一個名為flow-typed
的文件夾,並且下載相關的定義文件即可。為了進一步簡化,可以用npm的命令行方式一鍵獲取和安裝libdef文件:npm install -g flow-typed
安裝成功之後, 運行flow-typed install
來檢查package.json
文件,並且下載所有項目中用到的第三方庫的libdef。
2、自定義libdef
如果你用的庫並不在flow-typed倉庫,你可以創建你自己的libdef。本文不會細談自定義libdef,因為很少會有人遇到,感興趣可以查看此文檔。
八、剔除類型註釋
由於額外添加的類型註釋不是正確的JavaScript語法,打包編譯的時候需要在源碼中剔除。可以通過flow-remove-types來剔除,或者如果你已經用Babel來轉譯JS,你可以使用Babel preset來移除。我們只討論第一種方法。
首先需要安裝flow-remove-types作為項目依賴庫:npm install --save-dev flow-remove-types
然後在package.json
文件中添加另一個script
入口:
"scripts": {
"flow": "flow",
"build": "flow-remove-types src/ -D dest/",
}
上述命令將剔除src
文件夾下的所有類型註釋,在dist
文件夾中保存編譯後的版本。編譯後的文件就是普通的能運行於瀏覽器的JavaScript文件。
vue項目配置使用flow類型檢查