基於 TypeScript 的 Weex 優化實踐

一、背景

Weex 作爲一種成熟的跨平臺程序框架被運用到許多產品中,有贊也不例外。有贊零售移動端團隊從 2018 年就開始使用 Weex 構建頁面,據不完全統計,有贊零售移動端有超過300個頁面使用到了 Weex 開發!顯然,這是一個巨大的開發工程,同時我們也發現基於 JavaScript 的 Weex 開發給我們帶來了諸多痛點:

  • 缺少類型約束,編程時代碼提示全憑記憶,要拓展新功能也束手束腳。
  • 手誤寫錯某個變量名,只能在聯調、測試階段才能發現。
  • 面對複雜業務邏輯,代碼層面可控性、擴展性較差。
  • 總會遇到 xxxisundefined 的空指針問題。

這些問題我們都在 TypeScript 找到了答案。

二、什麼是TypeScript

TypeScript 是微軟開源的編程語言,它建立在 JavaScript 的基礎上,是 JavaScript 的超集,可以編譯成 JavaScript。它有以下特點:

1.始於JavaScript,歸於JavaScript

TypeScript 從今天數以百萬計的 JavaScript 開發者所熟悉的語法和語義中拓生而來,所使用的是通用的 JavaScript 代碼,包括流行的 JavaScript 庫,從 JavaScript 代碼中調用 TypeScript 代碼輕而易舉。TypeScript 可以編譯出純淨、 簡潔的 JavaScript 代碼,並能運行在任何支持 ES3 及以上的 JavaScript 引擎中。

2.強大的工具構建

類型允許 JavaScript 開發者在開發 JavaScript 應用程序時使用高效的開發工具和常用操作,比如靜態檢查和代碼重構。類型是可選的,類型推斷讓一些類型的註釋與你的代碼的靜態驗證有很大的不同。類型讓你能自主定義軟件組件之間的接口和洞察現有 JavaScript 庫的行爲.

3.進階的JavaScript

TypeScript 提供最新的和不斷髮展的 JavaScript 特性,包括那些來自 ES2015 和未來提案中的特性,比如異步功能和裝飾器,以幫助建立健壯的組件。

三、爲什麼要使用TypeScript

1. 降低維護成本,提升健壯性、穩定性

1)代碼即文檔,好的接口、函數定義可直接代替文檔,代碼可讀性更高。

2)靜態類型檢查,提早發現問題代碼。

2. 提高開發效率

1)對代碼重構和補全提示友好。

2)多人協作降低溝通成本,不再需要頻繁閱讀文檔或細究實現細節。

3)類型可選,讓你在不編寫額外代碼的情況下獲得很多功能。

4)有很多先進的高級特性可以使用。

3. 成熟度高

1)編輯器或 IDE 集成度高。

2)社區龐大,周邊工具豐富。

3)最受歡迎的編程語言排行榜中已躍升至第 10 名,話題度高。

4)多個團隊全面使用 TypeScript 重構代碼(Vue、React 、Angular),甚至連 Facebook 自家的產品(Jest、Yarn等等)都在從 Flow 向 TypeScript 遷移。

4. 接入成本低

1)幾乎沒有接入成本,對當前工程改造小。

2)可以和 JavaScript 混合開發、編譯成 JavaScript 在各端運行。

3)支持多種工具鏈。

5. 學習成本低

幾乎沒有學習成本,移動端各自開發的語言本身就有類型系統,並且 Swift、kotlin 也有可選類型,語法也和 TypeScript 類似。

四、如何使用 TypeScript 進行 Weex 開發

隨着 Vue2.x 對 TypeScript 的支持,Weex 也能快速接入 TypeScript。同時 Vue3.0 將使用 TypeScript 重寫,重寫後的 Vue3.0更能發揮 TypeScript 的特點。

1.接入TypeScript

雖然市面上關於 Weex 支持 TypeScript 的資料比較少,但關於 Vue 如何接入 TypeScript 的文章鋪天蓋地,這裏做個簡單總結:

  • 添加 TypeScript 依賴,根據所需升級相關依賴或者有影響的包(當使用第三方庫時,我們需要引用它的聲明文件,才能獲得對應的代碼補全、接口提示等功能)。
  • ts-loader可選,如果之前的項目工程對Babel依賴比較重,可以保留 babel-loader(Babel>=7)。Babel 已和TypeScript 官方展開了合作,解決了部分之前不能被正常編譯的問題。或者通過使用兩個編譯器,搭配 ts-loaderbabel-loader 來接入 TypeScript。
  • 添加 tsconfig.json ,並加入相關你需要的自定義配置。
  • 官方對 ESLint 做了支持,提供瞭解析 TypeScript 代碼的編譯器,可以把語法樹轉成 ESLint 所期望對 ESTree,使用 @typescript-eslint 即可。
  • 添加必要的聲明文件,Weex 目前還沒有官方的聲明文件,大家可按需添加。

2.聲明文件

Weex 官方目前沒有對 TypeScript 提供優秀的支持,需要自行添加聲明文件。

比如:

const platform = weex.config.env.platform

在 TypeScript 中,編譯器並不知道 Weex 是什麼東西。這時我們需要對其聲明

聲明文件必需以 .d.ts 爲後綴。一般來說,TypeScript 會解析項目中所有的 *.ts 文件,當然也包含以.d.ts結尾的文件。

例:weex.d.ts

declare interface IWeexGlobal {
    config: {
        platform: 'Android' | 'iOS' | 'Web'
    }
}
declare const weex: IWeexGlobal

Typescript 默認不能識別 .vue 文件,導致在引用時,會提示加載錯誤。所以需要自己新建一個 .d.ts 聲明文件文件添加以下內容。這是爲了告訴 Typescript 以 .vue 結尾的導入的任何東西都與 Vue 構造函數本身具有相同的形狀。注意引用 vue 組件時需要補全 .vue

例:vue.d.ts

declare module '*.vue' {
    import Vue from 'vue'
    export default Vue
}

關於聲明文件詳細內容,具體可參考官方文檔。

3.類組件

要讓 TypeScript 正確推斷 Vue 組件選項中的類型,需要使用類組件。在Vue 2.x 中,通常使用基於 Vue Class Component 裝飾器來用使用類組件。

Vue Class Component 對 Vue 組件進行了一層封裝,讓 Vue 組件語法在結合了 TypeScript 語法之後更加扁平化,代碼可讀性更高。

使用組類組件有以下差異:

  • @Component 修飾符註明了此類爲一個 Vue 組件
  • 初始數據可以直接聲明爲實例的 property
  • 計算屬性可以直接使用 getter / setter
  • 組件方法也可以直接聲明爲實例的方法
  • 所有 Vue 生命週期也可以直接聲明爲實例方法,但是你不能在實例本身上調用它們。聲明自定義方法時,應避免使用這些保留名稱
  • 其他接口描述對象可以傳遞給裝飾器函數或者 Vue.extend

其他接口描述對象在類組件的使用:

TypeScript 的類組件和 JavaScript 的接口描述組件導出有些差異:

  • 類組件導出的是 Vue 類
  • 接口描述組件導出的是 ComponentOptions接口

所以在入口文件對Vue進行初始化上也會有些區別:

4.裝飾器

TypeScript 支持裝飾器這一特性,Javascript 裏的裝飾器目前處在建議徵集的第二階段。若要使用裝飾器特性,需要在 tsconfig.json 裏啓用 experimentalDecorators 編譯器選項。裝飾器的好處如下:

1)使語法更加扁平化。

2)對業務代碼無侵入。

3)解耦業務邏輯、輔助功能邏輯。

除了上節提到的 @ComponentVue Property DecoratorVuex Class 提供了更多的裝飾器用於使用。裝飾器可以用於修飾類、方法和屬性等。

Vue Property Decorator

  • @Prop
  • @PropSync
  • @Model
  • @Watch
  • @Provide
  • @Inject
  • @ProvideReactive
  • @InjectReactive
  • @Emit
  • @Ref
  • @Component (由 vue-class-component 提供)

對常用對 @Prop@Watch 舉個例:

關於其他裝飾器如何使用,具體參考官方文檔。

Vuex Class

  • @State
  • @Getter
  • @Action
  • @Mutation

關於如何使用,具體參考官方文檔。

開發工具

1) Visual Studio CodeWeb Storm 都能做到開箱即用,不需要裝配額外的插件。

2)對 ZWeex ToolKit 擴展能力,目前已經支持了創建 TypeScript 的頁面

五、落地 TypeScript 提升系統穩定性

我們來對之前遇到的問題做個拆解,看看 TypeScript 是如何幫我們解決痛點。

1.減少Bug

1)類型錯誤

TypeScript 的類型保護、聯合類型、類型推導等特性,可以避免發生低級類型錯誤問題。比如在開發中約定函數的參數是 number 數字類型,如果使用時不慎使用了 string 類型的參數,那麼 IDE 會有 error 警告並會在編譯時報錯。

2)空指針

TypeScript 會進行嚴格非空檢查可以幫助我們避免空指針問題。

比如函數的參數定義是允許出現空指針的情況,那麼在使用這些不安全的參數時,IDE 和編譯器都會提醒你這塊兒地方注意了,如果沒有處理邊界會給予提示。

添加了判斷空指針進行處理異常邊界之後,可以通過編譯。

3)原生 module 類型約束

有贊零售使用有近 20 個原生 module,在之前開發過程中因爲沒有類型約束出現過不少寫錯 module/方法/參數名、使用錯參數類型的情況。使用 TypeScript 的類型聲明可以解決這些問題。

舉個例子,有以下幾個原生 module,我們對其類型聲明

declare interface IWeexNativeModules {
    foo: {
        fun(a: number, b?: string): void
    }

    foo1: {
        oops(): void
    }

    foo2: {
        oops(): void
    }
}

使用時,IDE會有代碼補全提示。如果寫錯 IDE 和編譯器同樣報錯提示。

調用方法和參數時也會有類型約束。

通過使用 TypeScript 有效的避免了類型問題,減少 Bug 量。一篇倫敦大學和微軟研究院聯合署名的論文中提到:

通過對 Github 上開源項目的公開 Bug 統計發現:15% 的 Bug 都可以通過 TypeScript 來規避。

2.增強架構設計

TypeScript 比 JavaScript 多了接口、抽象類、範型、訪問權限等,可以方便的落地架構設計。

面向接口(協議)編程在移動端應用是非常廣泛的,使用 TypeScript 之後也可以進行一些架構設計。

之前我們在使用 Weex 進行開發時,往往會把所有邏輯代碼往組件內部塞,使得組件後續維護起來非常麻煩。我們引入了和原生一樣的規範:增加Model、Service 層,通過工具自動生成相應目錄結構,在開發中得到了非常好的約束。

效果

我們在 Q2 完成了 TypeScript 的遷移,開發效率顯著提升、系統穩定性明顯提高。

在對供應鏈單據頁模版化的項目中,使用 TypeScript 進行了大範圍的重構。我們發現聯調期間的溝通顯著減少,不需要頻繁查閱接口文檔,代碼可讀性更高了,節省了很多 debug 成本。在測試環節僅出現 個位數 的 Bug,發佈線上之後也沒問題發生。

六、平滑遷移 TypeScript

遷移工程不需要一蹴而就,你可以先用 JSDoc 註釋現有的 JavaScript,然後遷移幾個文件交由 TypeScript 檢查,隨着時間的推移,當你的代碼庫準備好了之後,代碼庫遷移到 TypeScript 自然就水到渠成了。

參考鏈接

本文轉載自公衆號有贊coder(ID:youzan_coder)。

原文鏈接

https://mp.weixin.qq.com/s?__biz=MzAxOTY5MDMxNA==&mid=2455761139&idx=1&sn=a4cde112969fb5d3737ca4032bf12a53&chksm=8c6876d6bb1fffc05cb047840c618cb30e105442bd35bd06a765a045dc9698cc9a0f769e943c&scene=27#wechat_redirect

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章