摘要:一個帶提示的最后對于開發(fā)同學來說,就算不使用,也強烈建議使用提供注解,它會通過一些類型推導來檢查你的代碼的正確性,可以減少很多開發(fā)過程中的。相對于對象,它保證了輸入的類型你定義的對象可能某一天不再只有類型的,不再需要額外的類型判斷。
作者:陳達孚
香港中文大學研究生,《移動Web前端高效開發(fā)實戰(zhàn)》作者之一,《前端開發(fā)者指南2017》譯者之一,在中國前端開發(fā)者大會,中生代技術大會等技術會議發(fā)表過主題演講, 專注于新技術的調(diào)研和使用.
本文為原創(chuàng)文章,轉載請注明作者及出處
最近在做公司內(nèi)部的一個的一個SDK的重構,這里總結一些經(jīng)驗分享給大家。
類型檢查和智能提示作為一個SDK,我們的目標是讓使用者能夠減少查看文檔的時間,所以我們需要提供一些類型的檢查和智能提示,一般我們的做法是提供JsDoc,大部分編輯器可以提供快捷生成JsDoc的方式,我們比較常用的vscode可以使用Document This。
另一種做法是使用Flow或者TypeScript,選擇TypeScript的主要原因是自動生成的JsDoc比較原始,我們?nèi)匀恍枰谏厦孢M行編輯,所以JsDoc維護和代碼開發(fā)是脫離的,往往會出現(xiàn)代碼更新了,JsDoc忘記更新的情況。
除此之外開發(fā)過程中我們無法享受到類型檢查等對SDK開發(fā)比較重要的特性,TypeScript可以讓我們減少犯錯,減少調(diào)試的時間,另一方面這次開發(fā)的SDK在提供出去的時候就會進行一次相對簡單的壓縮,保證引入后的體積,所以會希望壓縮掉JsDoc,而TypeScript可以通過在tsconfig.json中將declaration設置為true多帶帶的d.ts文件。
一個帶提示的SDK:
最后,對于開發(fā)同學來說,就算不使用TypeScript,也強烈建議使用vscode提供//@ts-check 注解,它會通過一些類型推導來檢查你的代碼的正確性,可以減少很多開發(fā)過程中的bug。
還有一個小技巧,如果你使用的庫沒有提供智能提示,你可以通過NPM/yarn的-D安裝@types/{pkgname},這樣你開發(fā)過程中就能夠享受到vscode提供的智能提示,而-D安裝到devDependencies中,也不會增加你在構建時的代碼體積。
接口既然提到了TypeScript,就提一下TypeScript的語法,基礎類型沒有必要贅述,而一些曾經(jīng)的高級語法現(xiàn)在ES6也都能支持,這里提幾點常用但是JavaScript開發(fā)者不太習慣使用的語法。
很多人在開始使用TypeScript的時候,會很迷戀使用any或者默認的any,推薦在開發(fā)中打開tsconfig中的strict和noImplicitAny來保證盡量少的any使用,要知道,濫用any就等于你的類型檢查并沒有實質(zhì)效果。
對一些暫時不能確定內(nèi)容的對象的類型,可以使用{[key: string]: any},而不要直接使用any,后期可以慢慢擴展這個接口直到完全消除any,同時TypeScript的類型支持繼承,在開發(fā)過程中,可以拆解接口,利用組合繼承的方式減少重復定義。
但是接口也會帶來一個小痛點,目前vscode的智能提醒不能很好的對應到接口,當你輸入到對應變量的時候,雖然會高亮,但是高亮的也只是一個定義了名字的接口。沒有辦法直接看到接口里定義了什么。但是當你輸入了接口里面定義的key的部分時,vscode會給你完整key的提示。雖然這對開發(fā)過程中有一點不夠友好,但是vscode開發(fā)團隊表示這是他們故意設計的,所以在API參數(shù)上可以選擇將一些必要(重要)參數(shù)用基礎類型直接使用,而將一些配置放入一個定義為接口的對象中。
枚舉你有在代碼中使用過:
const Platform = { ios: 0, android: 1 }
那你在TypeScript中就應該使用枚舉:
enum Platform { ios, android }
這樣在函數(shù)中你就可以為某個參數(shù)設置類型為number,然后傳入Platform.ios這樣,枚舉可以增加代碼的維護性,它可以利用智能提示保證你輸入的正確,不再會出現(xiàn)魔數(shù)(magic number)。相對于對象,它保證了輸入的類型(你定義的對象可能某一天不再只有number類型的value),不再需要額外的類型判斷。
裝飾器對于裝飾器其實很多開發(fā)者既熟悉又陌生,在redux,mobx比較流行的現(xiàn)在,在代碼中出現(xiàn)裝飾器的調(diào)用已經(jīng)很普遍,但是大多數(shù)開發(fā)者并沒有將自己代碼邏輯抽成裝飾器的習慣。
比如在這個SDK的開發(fā)中,我們需要提供一些facade來兼容不同的平臺(iOS, Android或者Web),而這個facade會通過插件的形式讓開發(fā)者自己注冊,SDK會維護一個注入后的對象,常規(guī)的使用方法是到了使用函數(shù)后判斷環(huán)境再判斷對象中有沒有想有的插件,有就使用插件。
實際來看,插件就是一個攔截器,我們只要阻止真正的函數(shù)運行就可以,大概的邏輯是這樣的:
export function facade(env: number) { return function( target: object, name: string, descriptor: TypedPropertyDescriptor) { let originalMethod = descriptor.value; let method; return { ...descriptor, value(...args: any[]): any { let [arg] = args; let { param, success, failure, polyfill } = arg; // 這部分可以自定義 if ((method = polyfill[env])) { method.use(param, success, failure); return; } originalMethod.apply(this, args); } }; }; }
在SDK的開發(fā)過程中另一個常會遇到的就是很多參數(shù)的校驗和再封裝,我們也可以使用裝飾器去完成:
export function snakeParam( target: object, name: string, descriptor: TypedPropertyDescriptor泛形) { let callback = descriptor.value!; return { ...descriptor, value(...args: any[]): any { let [arg, ...other] = args; arg = convertObjectName(arg, ConvertNameMode.toSnake); callback.apply(this, [arg, ...other]); } }; }÷
泛形可以根據(jù)用戶的輸入決定輸出,最簡單的例子是
function identity(arg: T): T { return arg; }
當然它沒有什么特別的意義,但是它表明了返回是根據(jù)arg的類型,在一般開發(fā)過程中,你逃不開范型的是Promise或者前面的TypedPropertyDescriptor這種內(nèi)建的需要類型輸入的地方,不要草率的使用any,如果你的后端返回是一個標準結構體類似:
export interface IRes { status: number; message: string; data?: object; }
那么你可以這樣使用Promise:
function example(): Promise{ return new Promise ... }
當然泛形有很多高級應用,例如泛形約束,泛型創(chuàng)建工廠函數(shù),已經(jīng)超出了本文的范圍,可以去官方文檔了解。
構建如果你的構建工具是Webpack,在SDK的開發(fā)中,盡量使用node方式調(diào)用(即webpack.run執(zhí)行),因為SDK的構建往往會應對很多不同的參數(shù)變化,node方式相比純配置方式可以更加靈活的調(diào)整輸入輸出的參數(shù),也可以考慮使用rollup,rollup的構建代碼更加面向編程方式。
需要注意的是,在Webpack3和rollup中構建中可以使用ES6模塊化的方式構建,這樣業(yè)務代碼引入你的SDK后,可以通過解構引入的方式減少最終業(yè)務代碼的體積,如果你只是提供了commonjs的包,那么構建工具的tree sharking是無法生效的,如果使用babel的話注意關閉module的編譯。
另外一種減少單個包體積的方式,可以使用lerna在一個git倉庫里構建多個NPM包,比起拆倉庫可以更方便的使用公共部分的代碼,但是也需要注意對公共部分代碼的修改不要影響到別的包。
其實對于大多數(shù)的SDK的來說,Webpack3和rollup使用感受是差不多的,比較常用的插件都有幾乎同名的對應。不過rollup有兩個優(yōu)勢,一個是rollup的構建更細化,rollup.rollup接受inputOptions生成bundle,還可以generate生成sourcemap,write生成output,在這個過程中我們可以做一些細致的工作。
第二點是rollup.rollup會返回一個promise,也就意味著我們可以使用async的方式來寫構建代碼,而webpack.run還是使用的回調(diào)函數(shù),雖然開發(fā)者可以封裝成promise,但是個人覺得還是rollup的寫法還是更爽一點。
單元測試上周我同事做了一個在線的分享,我發(fā)現(xiàn)很多同學都對單測很感興趣也很疑惑,在前端開發(fā)中,對涉及UI的業(yè)務代碼開發(fā)單測試比較困難的,但是對于SDK,單元測試肯定是準出的一個充要條件。當然其實我也很不喜歡寫單測,因為單測往往比較枯燥,但是不寫單測肯定會被老司機們“教育”的~_~。
一般的單測使用mocha作為測試框架,expect作為斷言庫,使用nyc提供單測報告,一個大概的單測如下:
describe("xxx api test", function() { // 注意如果要用this調(diào)用mocha,不要用箭頭函數(shù) this.timeout(6000); it("xxx", done => { SDK.file .chooseImage({ count: 10, cancel: () => { console.log("選擇圖片取消----"); } }) .then(res => { console.dir(res); expect(res).to.be.an("object"); expect(res).to.have.keys("ids"); expect(res.ids).to.be.an("array"); expect(res.ids).to.have.length.above(0); uploadImg(res.ids); done(); }); }); });
同樣你可以用TypeScript寫單測,當然在執(zhí)行過程中,不需要再編譯了,我們可以直接給mocha注冊ts-node來直接執(zhí)行,具體方式可以參考Write tests for TypeScript projects with mocha and chai?—?in TypeScript!。但是有一點需要提醒你,寫單測的時候盡量依賴文檔而不是智能提示,因為你的代碼出錯,可能會導致你的智能提示也是錯誤的,你根據(jù)錯誤的智能提示寫的單測肯定也是。。。
對于網(wǎng)絡請求的模擬可以使用nock這個庫,需要在it之前增加一個beforeEach方法:
describe("proxy", () => { beforeEach(() => { nock("http://test.com") .post("/test1") .delay(200) .reply(200, { // body test1: 1, test2: 2 }, { "server-id": "test" // header }); }); it(... }
最后我們用一個npm script加上nyc在mocha前面,就可以獲得我們的單測報告了。
這里我還提了幾個TypeScript使用中的小tips給大家參考。
tips: 如何在非發(fā)包情況下給內(nèi)部庫添加聲明這個SDK在開發(fā)過程會依賴一個內(nèi)部NPM包,為了讓這個NPM支持TypeScript調(diào)用,我們有幾種做法:
給原包添加d.ts文件,然后發(fā)布.
發(fā)布@types包,需要注意的是NPM不支持@types/@scope/{pkgname}這種寫如果是私庫包,可以使用@types/scope_{pkgname}這種寫法.
這次使用的標注一個文件夾存放對應的d.ts文件,這種方式適合開發(fā)中進行,如果你覺得你寫的d.ts還不夠完美,或者這個d.ts文件目前只有這個SDK有需要,可以這么使用,在tsconfig.json中修改:
"baseUrl": "./", "paths": { "*": ["/type/*"] }tips: 如何處理resolve和reject不同類型的promise回調(diào)
默認的reject返回的參數(shù)類型是any,不一定能滿足我們的需要,這里給一個解決方案,并非最佳,作為拋磚引玉:
interface IPromise{ then ( onfulfilled?: | ((value: T) => TResult1 | PromiseLike ) | undefined | null, onrejected?: | ((reason: U) => TResult2 | PromiseLike ) | undefined | null ): IPromise ; catch ( onrejected?: | ((reason: U) => TResult | PromiseLike ) | undefined | null ): Promise ;
iKcamp原創(chuàng)新書《移動Web前端高效開發(fā)實戰(zhàn)》已在亞馬遜、京東、當當開售。
iKcamp官網(wǎng):https://www.ikcamp.com
包含:文章、視頻、源代碼
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://m.hztianpu.com/yun/90231.html
摘要:我打算把一些上使用的高級技巧寫成你不知道的這一系列的博文,希望大家一起學習學習。然而,這還不是最嚴重的,因為的語法比較寬松和隨意,所以同一功能多種寫法,各種奇葩都有。總結前端在調(diào)試代碼的時候,知道開發(fā)工具上的小技巧,可以提高查找問題的效率。 Web前端開發(fā)過程中必然會用到Chrome瀏覽器自帶的開發(fā)者工具Chrome DevTools,使用它作為Web前端開發(fā)性能調(diào)試的必備工具。就連隔...
摘要:本文已獲得原作者授權同意,翻譯以及轉載原文鏈接作者譯文鏈接更小更快構建的奇淫技巧翻譯人上個月,我有機會在發(fā)表演講。禁用更新構建下一個技能是在中禁用更新構建。它將使每小時檢查庫的新版本,并增加構建時間。 本文已獲得原作者授權同意,翻譯以及轉載原文鏈接:Build your Android app Faster and Smaller than ever作者:Jirawatee譯文鏈接:...
摘要:本文已獲得原作者授權同意,翻譯以及轉載原文鏈接作者譯文鏈接更小更快構建的奇淫技巧翻譯人上個月,我有機會在發(fā)表演講。禁用更新構建下一個技能是在中禁用更新構建。它將使每小時檢查庫的新版本,并增加構建時間。 本文已獲得原作者授權同意,翻譯以及轉載原文鏈接:Build your Android app Faster and Smaller than ever作者:Jirawatee譯文鏈接:...
摘要:在正式前端一些小細節(jié)前端掘金英文原文,翻譯未來的太讓人興奮了一方面,是全新的頁面布局方式另一方面,是酷炫的濾鏡顏色等視覺效果。老司機教你更好的進行編程個技巧前端掘金并不總是容易處理。 CSS3 實現(xiàn)文字流光漸變動畫 - 前端 - 掘金來自百度前端技術學院的實踐任務:有趣的鼠標懸浮模糊效果,參考:http://ife.baidu.com/course/d...,用CSS3實現(xiàn)了一下,順便...
閱讀 3157·2023-04-25 17:22
閱讀 1718·2019-08-30 15:54
閱讀 1418·2019-08-30 15:53
閱讀 1969·2019-08-30 15:43
閱讀 3303·2019-08-29 12:29
閱讀 1373·2019-08-26 11:37
閱讀 3484·2019-08-23 18:02
閱讀 1780·2019-08-23 14:15