不要再用 vue2 的思維寫 vue3 了

升級 Vue3 後,讓人最腦殼疼的應該是新的 Compostion API 語法,他的難點不是語法,而是他提供了全新的組織代碼的思維方式。

我剛從 Vue2 轉到 Vue3 時,代碼都嚴格的遵循 Compostion API 寫法,但是發現比 Option API 寫法維護性更差。

踩過的坑

1. 按技術類型劃分代碼

在日常開發中,前端一般會收到交互稿或設計稿後開始佈局,然後編寫邏輯代碼。在 Vue2 中,通常做法是響應數據放到data、邏輯方法放到methods,這樣的做法非常方便,也讓我們很容易組織代碼。

當使用 vue3 的 Compostion API 時,如果還是用 Vue2 的形式組織代碼,這不但不會提升代碼質量,反而因爲缺乏約束而降低可讀性。

我在 github 隨便找了一段代碼,你覺得這段代碼比 Vue2 簡潔嗎?

export default {
  setup () {
    const state = reactive({
      message: '',
      msgList: []
    })
    const router = useRouter()
    let username = ''
    onMounted(() => {
      username = localStorage.getItem('username')
      if (!username) {
        router.push('/login')
        return
      }
    })
    const onSendMessage = () => {
      const { message } = state
      if (!message.trim().length) return
      state.msgList.push({
        id: new Date().getTime(),
        user: username,
        dateTime: new Date().getTime(),
        message: state.message
      })
      state.message = ''
    }
    return {
      ...toRefs(state),
      onSendMessage
    }
  }
}
複製代碼

實際上我們過於關注語法層面改變,而忽略官方文檔提到一個詞叫:邏輯關注點!!!!!!, 邏輯關注點是指表達同一個業務的代碼內聚到一起,這也是單一職責的指導思想,我們內聚的不應該技術類型,而是業務邏輯,因爲觸發代碼變更的往往是業務需求,因此把相同變更理由的代碼放在一起,這纔不會導致散彈式修改。

2. 過於關注邏輯複用

compostion API 一個特點是提升邏輯複用,這是沒有錯的,但是當時我有一個錯誤觀點,就是隻有複用的邏輯才應該封裝到 hook 中。

我們還是回到 Vue 的官方例子,你會發現他把原來放在一個 vue 文件的邏輯拆分到composables目錄,目錄下分別定義一個文件,表示不同的邏輯關注點

官方文檔地址 | 參考代碼倉庫

這個文件夾的代碼強調的並不是邏輯複用,而是邏輯關注點分離,這也是 compostion API 最核心要解決的問題,因爲應用生命週期 60% 時間都是在維護的,而維護性體現在代碼是否符合單一職責原則,單一職責就是把相同的業務代碼內聚到一個地方。

所以你不要過於糾結代碼是否需要複用,應用適當的冗餘反而增加應用的維護性,《架構整潔之道》書中提到:對於大多數應用,可維護性比可重用性更加重要。

如果你的代碼真的具有很高的複用性,那可以提升到項目外層目錄,封裝到獨立的 hook 文件。

尤雨溪的看法

compostion API 在提案的時候,就有很多人持有不同意見,有反對有支持,實際上都沒有錯,只是大家碰到的場景不同而導致不同觀點。我通過閱讀 compostion API 的 RFC,找到了作者對一些問題的解答,整理了一些關鍵問題,內容不是完全翻譯,完整內容建議查看原文。原文地址

問題一:compostion api 根本沒有解決任何問題,只是追逐新玩意的東西

尤雨溪: 不同意這個觀點。Vue 最開始很小,但是現在被廣泛應用到不同級別複雜度的業務領域,有些可以基於 option API 很輕鬆處理,但是有些不可以。例如下面的場景:

  1. 有很多邏輯的大型組件(數百行)
  2. 在多個組件可複用的邏輯

對於問題 1,你需要把每個邏輯拆分到不同選項,例如,一段邏輯需要一些響應數據,一個計算屬性,一些監聽屬性還有方法。你去了解這段邏輯時,需要不斷上下移動閱讀,雖然你知道一些屬性是什麼類型,但是你並不知道他具體的作用。當一個組件包含多個邏輯,情況就更糟糕了。 如果用新的 API,可以將數據和邏輯組合在一起,最重要的是,你可以乾淨的把這些邏輯提取到一個函數,甚至一個單獨的文件中。

問題二:使用新 API 導致邏輯分散到不同地方,違背 "關注點分離"

尤雨溪: 這個問題和項目文件組織方式問題類似。我們很多人都同意按文件類型組織(佈局放 HTML,樣式 CSS,邏輯 JS)並不是正確的方式,因爲強制把相關代碼分割到三個文件,只是給人一種 “關注點分離” 的錯覺。這裏的關鍵是 “關注點” 不是由文件類型定義。相反,我們大多數選擇以功能或者職責來組織文件,這正是人們喜歡Vue單文件組件的原因。SFC 就是按功能組織代碼的方法,但諷刺的是當首次引入 SFC 時,許多人也是拒絕的,認爲它違反了關注點分離。

問題三:新的語法讓 Vue 失去簡單性,導致 "意大利麪條式代碼" 的出現,降低項目維護性。

尤雨溪: 正好相反,新的 API 就是爲了提高項目長期維護性的。

如果我們查看任何 javascript 項目,都會從入口文件開始閱讀,該文件的本質是你的應用啓動時被隱式調用的 "main" 函數。如果只有一個函數入口,會導致意大利麪條代碼,那所有的 js 項目都是意大利麪條代碼。顯然不是的,因爲開發人員通過代碼模塊化或者較小的函數來組織代碼。

另外,我同意新的 API 理論上會降低代碼質量的最低門檻。但是我們可以使用以往防止代碼變成意大利麪條的手段緩解這種情況。另一方面,新的 API 可以提升代碼質量的最高上限,相比 option api,你可以重構爲質量更高的代碼。而且,基於 Option api 你還得解決類似 mixins 的問題。

很多人認爲 "Vue 失去簡單性",實際上只是失去組件內代碼類型檢查能力(就是你不知道一個變量時 data、method、還是 computed)。但是用新的 API,實現一個類型檢測器也是非常容易實現以前的特性的。也就是說,你不應該被 option api 限制思維,而更多關注邏輯內聚問題。

總結

上面只是節選了 RFC 討論的幾個小問題,如果你對新 API 還有其他疑問,建議去 github 閱讀原文,原文討論了非常多問題,我就不一一總結了。

但是從討論的內容和我實戰的經驗,用新的 API,一定要注意轉變代碼組織思維,記住一個詞"邏輯關注點"

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://juejin.cn/post/6946387745208172558