工程化治理主要分為以下幾個方面:
- 靜態(tài)檢查:typescript + ESLint
- 開發(fā)體驗: 打包工具和Mono-repo管理
- 代碼質量: 測試
靜態(tài)檢查:
TS 和ESLint這些工具本質上是對代碼做靜態(tài)檢查,盡早發(fā)現(xiàn)隱藏的bug
TS和ESLint的區(qū)別: TS有ESLint沒有的類型檢查
相同點:都具有語法錯誤檢查的能力
TS + ES相結合:ES對代碼風格做一個規(guī)范,TS主要負責對代碼語法和語義上的錯誤進行靜態(tài)檢查
從AnyScript到TypeScript
用TS,一個很重要的區(qū)別就是有沒有在配置中打開strict 選項
沒有的話束凑,其實使用的是AnyScript丹允,在類型上沒有約束局嘁,和JS沒有太大區(qū)別川无,如果是從 JS 遷移到 TS 的項目喇喉,這個選項應該關閉务荆,因為老的 JS 代碼沒有寫類型妆距。但如果是全新的純 TS 項目,strict 是一定要打開的『埃現(xiàn)在 CRA 這樣的腳手架創(chuàng)建的項目也是默認開啟了 strict 模式的娱据。
下面說說一些 strict 模式下的常見問題以及一些類型的技巧:
noImplicitAny
場景1: 函數(shù)的入?yún)]有定義類型,含有隱式any, strict模式不允許
場景2: 沒有顯示聲明對象的index signiture
const props = {
foo: "bar"
}
props['foo'] = 'bar';
// Element implicitly has an 'any' type because expression
// of type 'string' can't be used to index type
修改
interface Props {
foo: string;
[key:string]:Props[keyof Props];
// 在key值確定的情況下盅惜,可以用keyof來獲取一個接口的所有key 組成的聯(lián)合類型
}
const props: Props = {
foo: "bar",
}
props["foo"] = "bar"; // ok
props["bar"] = 'bar"; // error
strictNullChecks
場景: 獲取一個可能是null或undefined變量上面的方法或屬性
導致的原因: GUI場景(全局變量)中剩,如:this.graph忌穿,很多的成員變量是會在組件初始化之后才有值的,初始值這個時候就是undefined
解決方案:對于 strict 模式下的 strictNullChecks结啼,我們可以用 type guards(if(this.graph)this.graph.on()
)掠剑,type assertion(!)郊愧,optional chaining(朴译?) 三種辦法去告訴編譯器,這里的操作是安全的属铁。
關鍵是眠寿,Optional 的值在 GUI 編程中是很正常的,我們要學會去處理和面對這些情況焦蘑,把 undefined 和 null 作為一個單獨的類型來對待盯拱。
如何查看第三方庫是否有類型定義
方法1: 查找項目中的package.json中是否有types字段
方法2: 類型定義可能是一個單獨的類型包,如: @types/react
沒有類型的庫最好不要在TS項目中使用
高級類型
除了 Type Guard例嘱,交叉類型狡逢,聯(lián)合類型和上文提到的可以為 null 的類型之外,最關鍵的是: Mapped types(映射類型)蝶防、Conditional Types(條件類型)甚侣、Index types(索引類型)
在使用泛型時,這些技巧可以讓我們對類型進行“編程”间学,想象一下對類型變量使用 殷费?三元表達式或者 Array.prototype.map 這樣的方法。
exp: 條件類型
T extends U ? X : Y // 如果T包含U所以的屬性低葫,這個類型是X, 否則就是Y
function process(text:string|null):string|null{
return text && text.replace(/f/g, "p")
}
process("foo").toUpperCase(); // Type error
// 解決方案: 條件類型
function process<T extends string | null >(
text: T
): T extends string ? string : null {
return text && text.replace(/f/g, "p")
}
ESLint 和 Prettier
ESLint + TypeScript: 取代TSLint的新方案
TSLint 在 2019 年宣布未來項目將會廢棄详羡。TS 官方推薦 ESLint 作為 Linter
如何配置讓ESLint支持解析TS文件?
@typescript-eslint/parser
配套還有 @typescript/eslint-plugin 作為 ESLint 下針對 TS 訂制的 Lint 規(guī)則
JS + TS混合項目 ESLint 配置
在又有 JS 又有 TS 文件的情況下嘿悬,ESLint 需要只在 TS 文件上实柠,執(zhí)行 TS 相關規(guī)則的校驗,不然在校驗 JS 時很多 TS 規(guī)則也會生效善涨,這樣就造成了困擾窒盐。
解決方案就是使用 ESLint 的 override:
"overrides": [
{
"files": "**/*.ts",
"extends": [
"eslint-config-airbnb",
"plugin:@typescript-eslint/recommended",
"prettier/@typescript-eslint",
"prettier",
"prettier/react"
],
}
]
// 只有處理TS文件時才加入TS的相關規(guī)則
// 另外,有一些 JS 規(guī)則在 TS 文件上使用時也會出現(xiàn)問題钢拧,比如:
// https://github.com/eslint/eslint/issues/8813蟹漓。解決方案也是使用 override。
Pre commit hook
Pre commit hook: 是指設置一個 Git hook源内,在提交之前運行葡粒。前端項目一般利用這個機會運行靜態(tài)代碼檢查和代碼格式化,比如 ESLint,Prettier嗽交。也可以運行測試或者 TS 編譯等等檢查卿嘲。
具體設置的流程可以參考:Configuring Pre-commit Hooks for Prettier and Linting on a TypeScript Project。
commit hook 也可以用 -n 跳過夫壁,所以還應該在 CI 時加上 ESLint拾枣,來保證不規(guī)范的代碼提交被立刻發(fā)現(xiàn)。
開發(fā)體驗
目標:一鍵配置掌唾,一鍵升級
打包工具
關于模塊的格式放前,我們聽過 AMD,CommonJS糯彬,UMD,ES Module 等等葱她。
Nodejs 的 CommonJS
Webpack 這樣的打包工具也只兼容 CommonJS 模塊
ES Module撩扒,這個標準是未來瀏覽器支持的標準,Nodejs 也會支持吨些。
目前的構建工具都支持原生的 ES Module 格式(之前需要用 babel 轉為 CommonJS)
構建 ES module:Rollup/Babel
我們只需要把 package.json 的 module 字段指向打包出的 es module 格式的文件搓谆,構建工具就會使用 module 字段而不是 main 字段進行構建了。
打包工具選擇:
Webpack 目前不支持輸出 ES module豪墅,排除
Rollup 和 Babel 是可行的兩種方案泉手。
Rollup 是目前最流行 JS 庫打包工具,React偶器,Vue 之類的開源項目都在使用 Rollup斩萌。Rollup 支持輸出 CommonJS,UMD屏轰,ES Module 在內的主流格式颊郎,并可以通過插件支持 CSS 等靜態(tài)資源的處理。
Rollup 和 Webpack 的主要區(qū)別就是 Rollup 是以構建 JS 為核心的霎苗,并且從一開始就是基于 ES Module 的姆吭,如果要兼容 CommonJS 代碼,需要引入額外的插件唁盏。Webpack 更關注的是所有資源的構建内狸,并且強調 Code Splitting 的能力,專注于 Web 應用的打包厘擂。Rollup 更輕量和專注昆淡,而且支持 ES Module 的輸出,所有在 JS 庫打包這個方面 Rollup 是首選驴党。
Babel其實本身只是一個轉譯工具瘪撇。但 Babel 可以通過插件支持 TS 代碼的轉譯,還有 JSX 的轉譯(老本行),所以如果是簡單的 TS 庫倔既,可以直接用 Babel 進行轉譯恕曲,輸出的就是原汁原味的 ES Module(因為 Babel 壓根沒有去解析模塊,只是單純的轉譯代碼)渤涌。需要注意的是 Babel 的 TS 轉譯只是轉譯佩谣,不是編譯,所以類型錯誤是不會報出的实蓬,需要額外跑 tsc 來對 TS 代碼進行類型校驗茸俭。其他的靜態(tài)資源也是一樣的,需要單獨跑 task安皱。
專注與 JS 庫打包的 Father
Father 可以簡單理解為是 JS 庫領域的 CRA 或者 Umi调鬓。Father 封裝了 Rollup 和 Babel 兩套工具鏈。
在最簡單的情況下:我們只需要告訴 Father 需要什么格式的輸出就可以構建成功酌伊,比如:
father build --esm --cjs --umd --file bar src/foo.js
mono-repo 管理:Lerna
Lerna 是用于管理擁有多個 npm package 的 mono repo 的工具腾窝。mono repo 就是指多個項目的源碼放在同一個倉庫下進行管理。
簡單的說居砖,Lerna 的功能就是一鍵在多個 package 中同時運行一些命令虹脯。而且運行的時候還會根據(jù) package 之間的依賴拓撲關系,對命令的啟動順序進行編排奏候。同時 Lerna 的 bootstrap 命令可以把 package 之間相互的依賴循集,自動 link 到 package 自己的 node_modules 里面。這可以說是最大的一個賣點蔗草。Lerna 之前如果要在本地開發(fā)多個相互依賴的 npm 包咒彤,那就要敲一堆的 npm link,而且還容易出問題蕉世。
mono-repo 這種方式本身也是為了提升多個 npm package 的情況下蔼紧,管理源代碼的效率,以及共享基礎設施狠轻。因此 Lerna 其實是提升了開發(fā)者開發(fā)基于 mono repo 的前端項目的體驗奸例。
前端的組件庫一類的項目,用 Lerna 是非常合適的向楼。
代碼質量
這里的代碼質量主要是指測試
React 組件測試技術選型: jest 測試框架 + Enzyme/ react-testing-library DOM Util(用于組建渲染和DOM操作)
在 React 16 下查吊,Enzyme 有一些問題,比如 shallow 模式下不支持 useEffect湖蜕。
react testing library 是在 React 官方的 test util 基礎上包裝的逻卖,要更輕量一些。
常見的測試技巧
react testing library 的測試套路
我們只需要調用 render 昭抒,把組件渲染出來就行了:
asTragment
queryByText
rerender
queryByTextId
act()
fireEvent
Mock 瀏覽器事件
Canvas 測試
如果測試的目標中有 Canvas评也,情況分兩種:
Canvas 上的內容是組件用到的圖表庫一類的渲染結果炼杖,和組件本身的正確性無關
Canvas 上的內容就是測試的目標,比如給圖表庫寫測試
如果是前者盗迟,我們可以 Mock 掉 Canvas坤邪,使用 jest-canvas-mock 可以很方便的一鍵 Mock。
如果是后者罚缕,我們可以用 jest-electron 去運行一個真實的瀏覽器艇纺,來測試 Canvas 的繪制結果。
覆蓋率
Jest 配置了 collectCoverage: true 之后就會在本地生成測試覆蓋率報表邮弹。用 http-server 起一個本地服務器就可以看到
補充:
ESLint + TS 這方面的資料很多黔衡,Using ESLint and Prettier in a TypeScript Project 這篇文章講了如何從 TSLint 遷移到 ESLint。
還有以下的文章腌乡,都講解了相關的配置(ESLint + TS):
Integrating Prettier + ESLint + Airbnb Style Guide in VSCode
Setting up ESLint with Prettier, TypeScript, and Visual Studio Code
From ESLint to TSLint and Back Again
關于 TSLint 到 ESLint 的切換的背景盟劫,可以看 typescript-eslint 這個項目的 README,講的非常詳細
使用 ESLint 的好處就是:可以背靠 ESLint 的生態(tài)导饲,像 Airbnb 這樣的規(guī)則集就可以直接用于 TS 項目捞高。上面列舉的博客就有講如何配置 Airbnb + typescript-eslint + prettier 三種規(guī)則集。讓項目可以用 typescript-eslint 來規(guī)范 TS 代碼(TS 特有的 Lint 規(guī)則)渣锦,用 Airbnb 來規(guī)范 React 和 JS 代碼(TS 是 JS 的超集),用 Prettier 相關規(guī)則來關閉前兩個規(guī)則中和 Prettier 代碼風格沖突的規(guī)則氢哮。三者集合就是目前比較完善袋毙,好用的 Lint 規(guī)則了。
Airbnb 中有一些規(guī)則冗尤,比如要求 React 組件聲明 PropTypes听盖,是不適用于 TS 項目的,所以需要在 ESLint 配置文件里關掉裂七。其他類似的配置有很多皆看,我們不用死板的遵守 Lint 規(guī)則,而是關閉不合適的規(guī)則背零,只取其精華
在 React 16 下腰吟,Enzyme 有一些問題,比如 shallow 模式下不支持 useEffect徙瓶。詳見:https://github.com/airbnb/enzyme/issues/2086
如果對 react testing library 不熟悉毛雇,可以看官網(wǎng)和這篇教程。
https://testingjavascript.com/ 這個測試教程網(wǎng)站侦镇,可以了解到測試相關技術的大圖灵疮,如果對測試的分類和作用不太清楚可以看一下這個網(wǎng)站。