前言
結合由Facebook出品的靜態型別檢查工具Flow,和最近將Flow引入到一個Vue.js專案中的實踐經驗,本文想聊聊Flow型別檢查工具和它在Vue專案中實際使用的效果。
目錄
- 為什麼需要引入型別檢查?
- Flow是什麼?
- Flow的作用
- Flow在Vue專案中的使用
- Flow的配置過程
- 在Vue元件中使用Flow的幾個方法
- 型別檢查工具對團隊有什麼好處?
- 總結
為什麼需要引入型別檢查?
JS作為一個弱型別語言,一個著名的黑點是它很容易就寫出非常隱蔽的隱患程式碼,在編譯期甚至執行時看上去都不會報錯,但是可能會發生各種各樣奇怪的和難以解決的bug。
型別檢查是當前動態型別語言的發展趨勢,根據stateofjs的調查結果,JS的強型別超集TypeScript已經有了相當的知名度,吸引了大量開發者的學習興趣,並且大部分開發者計劃繼續瞭解或者使用。
所謂型別檢查,就是在編譯期儘早發現(由型別錯誤引起的)bug,又不影響程式碼執行(不需要執行時動態檢查型別),使編寫js具有和編寫Java等強型別語言相近的體驗。它可以:
- 使得大型專案可維護
- 增加程式碼的可讀性
- 通常會有更好的IDE支援
Flow是什麼?
Flow是一個由Facebook出品的JavaScript靜態型別檢查工具,它與Typescript不同的是,它可以部分引入,不需要完全重構整個專案,所以對於一個已有一定規模的專案來說,遷移成本更小,也更加可行。除此之外,Flow可以提供實時增量的反饋,通過執行Flow server不需要在每次更改專案的時候完全從頭執行型別檢查,提高執行效率。
Flow和Typescript都是給Javascript增加型別檢查的優秀解決方案,兩者的簡單對比如下:
工具 | Flow | Typescript |
---|---|---|
公司 | 微軟 | |
star | 12k+ | 23k+ |
文件支援程度 | 中等 | 更多 |
第三方庫支援工具 | Flow-typed | tsd |
IDE支援 | Webstorm自帶外掛支援 | Webstorm支援,Visual Studio原生支援 |
其他 | 自由度更高,老專案的遷移成本低 | 工程化強,社群活躍度和官方支援力度更高,適合新專案 |
兩者在程式碼語法上有大量相似的地方,除了對於一些資料型別的支援不一樣,具體請檢視Flow的文件。關於Flow和Typescript的比較,可以簡單總結為:對於新專案,可以考慮使用TypeScript或者Flow,對於已有一定規模的專案則建議使用Flow進行較小成本的逐步遷移來引入型別檢查。
Flow的作用
一個簡單的demo如下。
執行Flow命令,這個demo的執行結果如下圖所示:
對於需要使用 Flow 進行型別檢查的 js 檔案,在開頭加入 @Flow 註釋,即可引入Flow。通過demo可以看到,Flow可以幫助找出由於不合理的型別操作引起的錯誤,包括運算子操作,函式引數型別和返回值型別等。Flow也支援自定義型別宣告,泛型宣告等型別語言相關的操作,詳細的內容可以參考文件:Flow.org/en/docs/lan…
Flow在Vue專案中的使用
Flow在Vue專案中的具體使用價值有:
- 使用Flow可以在不需要重構整個Vue專案(如UI元件遷移成本)、不需要引入大量的工具鏈(eslint+babel)、不需要第三方庫一定支援的情況下引入靜態型別檢查
- Vue.js官方對TypeScript做了支援,但是專案所依賴的第三方庫不一定支援TypeScript,從全域性考慮TypeScript的遷移成本比較大
在嘗試Flow+Vue.js的實踐過程中,主要的步驟包括:1,使用Flow-typed工具(github.com/Flowtype/Fl… packages的支援;2,在一個由Vue cli (webpack + babel + eslint) 生成的腳手架專案中配置 Flow(見後文);3,Vue 的單檔案元件結構如何支援 Flow,在業務專案的實踐中前後使用了三種方案,也會在後文分別介紹這幾種方法和其優缺點。
Flow的配置過程
- 假設目前有一個從vue-cli命令列生成的專案:vue init webpack-simple Flow-vue-demo。關於Babel,Eslint(可選)和Flow,需要安裝所需的 npm packages,參考列表如下:
Babel:
- babel-plugin-syntax-Flow
- babel-plugin-transform-class-properties
- babel-plugin-transform-Flow-strip-types
Eslint: (可選)
- eslint
- babel-eslint
- eslint-plugin-html
- eslint-plugin-Flowtype-errors
- eslint-plugin-vue
- eslint-config-vue
Flow:
- Flow-bin
- Webstorm自帶了Flow的支援,需要開啟,結合eslint配置Flow相關的rules,在編輯時通過eslint即可自動報錯。
- 安裝Flow,執行Flow init && Flow check。配置.vue檔案為Flow的檢查範圍。
- 使用 Flow-typed 處理第三方的 npm packages 的型別宣告。
- 必要的話增加自定義的型別宣告檔案,如自定義的物件等,具體可以參考Flow文件。
在Vue元件中使用Flow的幾個方法
在前面的demo中已經展示了純JS檔案裡面怎麼用Flow,那麼在一個vue元件檔案中應該如何配置呢?有下面幾種方法。
方法一:直接在script標籤中,像純js檔案處理一樣新增Flow註釋,發現可以正常編譯執行,但是執行Flow check是無效的。
方法二:註釋掉template, style和script標籤,由於Vue的編譯器即使註釋了也會識別其中的<template>, <style> 和 <script>
標籤,而Flow檢查會忽略註釋,因此對於Flow來說可以當做一個javascript檔案進行處理。demo如下圖所示。
對於這樣處理的vue檔案,Flow命令能夠報出關於一般的函式宣告的型別檢查錯誤,但是對於繫結到Vue例項(this)上的方法是無效的。因此Flow型別檢查不是100%覆蓋。這種方法的主要問題在於程式碼和註釋混用不便於閱讀,目前Flow社群有一個open issue就是關於這個問題的,即不能自動檢測中檔案中的script標籤,請見:github.com/facebook/Fl…
方法三:Vue檔案引用外部的js檔案,將js部分單獨抽離出來進行型別檢查。該方法的優點在於可以用到Flow的所有功能,但是沒有了vue單檔案元件的結構,專案結構略顯臃腫。(每個元件都會有至少兩個檔案)。如下圖:
三種解決方法的優缺點對比如下表所示:
方法 | Pros | Cons |
---|---|---|
標籤中直接新增Flow | 程式碼新增量最小 | Flow 型別檢查無效. 不予考慮 |
註釋template中的標籤 | 1. 可以通過Flow check檢查出部分的型別錯誤 2. 最接近使用直覺. 目前是一個open issue | 1. 對於和元件無關的函式以及import. 可以正常工作. 但是不是100%覆蓋 2. 看上去樣式比較糟糕 |
Vue檔案引用外部的js檔案 | 1. 通過eslint中通過使用Flow外掛. 配置Flow規則. 可以在編輯時實時提示 2. 沒有影響檔案結構 3. 單獨的js檔案可以幾乎完全使用Flow的所有功能 | 1. method仍然不能自動識別. 由於Vue中的一些函式一般沒有return value. 需要手動判斷型別防止bug 2. 一個元件的程式碼被分拆到多個檔案. 不如單檔案元件那麼直觀 |
型別檢查工具對團隊有什麼好處?
通過在一個Vue技術棧的實際業務專案中引入Flow,我們大致獲得了這些收益:
- 幾乎消滅了由函式資料型別引起的bug
- 無需額外的關於變數、引數、返回值型別的註釋,可以讓讀者瞭解必要的附加資訊
- 大量減少由於使用第三方庫不當引起的型別錯誤
- 可以在CI系統中整合
- 工具鏈配置成本比較低,只需要很少的工作量即可達到這些效果
關於型別檢查工具,讀者可能需要考慮的問題,回答如下表所示。
Question | Answer |
---|---|
型別檢查可以讓我的程式碼bug free嗎? | 不能保證bug free,只能檢查型別錯誤 |
可以提高我的生產力嗎? | 需要多寫一些程式碼,但是相應地可以減少很多runtime debug的時間 |
將Flow引入我的專案,所需要的工作量大嗎? | 不大,可以逐步引入 |
我的專案需要長期維護? | 請使用Flow或者Typescript |
我的專案非常簡單? | 簡單專案不一定需要型別檢查,可能會有些多餘 |
我的專案需要重構? | 請引入型別檢查 |
我的專案對於bug free的要求非常高? | 請引入型別檢查,減少型別錯誤等難以發現的bug |
我的專案開發人員流動很頻繁? | 請引入型別檢查,增加專案可讀性 |
我的專案有大量的演算法計算? | 請引入型別檢查,減少隱蔽的型別轉換錯誤等 |
總結
- Flow或者TypeScript都是靜態型別檢查的優秀解決方案,能夠給有型別檢查需求的一定規模的專案帶來實際收益。
- Flow+Vue目前看來有些使用上的不便,期待儘早解決open issue,能夠自動識別Vue元件檔案的標籤,從而使得Flow在vue專案中的使用更加流暢。