Vue3實現元件級基類的幾種方法

金色海洋(jyk)發表於2023-04-28

Vue3的元件有三種程式碼組織方式

  1. 純Option API (不含setup)
  2. option API + setup
  3. 純 setup (即composition API)

對於這三種形式,設定基類的方法也略有不同。

使用 mixins、extends

vue3提供了 mixins和extends,但是嘗試之後發現這兩種方法只支援純OptionAPI,設定的data會被識別,但是設定的setup裡return 的 reactive,完全無效,setup也沒有被執行。
所以這種方式只能使用於第一種方式。

使用 hooks (function、class)

既然官方沒有提供,那麼我們自己來想想辦法。我們先觀察一下元件的程式碼(第二種情況):

<template>
  <!--模板-->
  舉例
</template>

<script lang="ts">
  import { defineComponent } from 'vue'

  export default defineComponent({
    name: 'ui-core-',
    components: {
      // 註冊共用元件
    },
    props: {
      // 定義共用屬性
    },
    setup(props, context) {
      // 各種共用操作
      _logger()
      _setTitle()
      // 共用成員
      const foo = reactive ({})
      return {
        foo
      }
    }
  })
</script>

defineComponent 方法接收一個物件,物件需要有特定的幾個屬性,比如name、components、props、setup等。
那麼也就是說,我們可以做一個函式返回這樣的物件即可。
比如我們先建立一個js(或則ts)檔案:

export function base (name, callback) {
  return {
    name: 'ui-' + name,
    components: {
      // 註冊共用元件
    },
    props: {
      // 定義共用屬性
    },
    setup(props, context) {
      // 各種共用操作
      _logger()
      _setTitle()
      // 共用成員
      const foo = reactive ({})

      // 執行其他操作
      const re = callback(props, context)
      return {
        foo,
        ...re
      }
    }
  }
}

有點像模板模式。

傳入name和一個回撥函式,props, context作為引數進行傳遞。內部成員也可以作為引數傳遞。
這樣一個簡單的基類就做成了,如果你覺得function不好看,那麼可以換成class。

export default class BaseComponent {
  name: string
  components: any
  props: any
  setup: any

  constructor (name: string, callback: (props: any, context: any) => any) {
    this.name = name
    this.components = {}
    this.props = {}
    this.setup = (props: any, context: any) => {
      // 各種共用操作
      _logger()
      _setTitle()

      // 執行其他操作
      const re = callback(props, context)

      return {
        ...re
      }
    }
    
  }
}

有了class之後,還可以設定子類,不過感覺有點繁瑣。總之,反正可以實現就對了。

script setup怎麼辦

上述這種方法應該也是可以支援純composition API的,但是有點小問題,defineProps 和 defineEmits 並不是普通 js 函式,而是一種“宏”。
引用官網的解釋:

defineProps 和 defineEmits 都是隻能在 <script setup> 中使用的編譯器宏。他們不需要匯入,且會隨著 <script setup> 的處理過程一同被編譯掉。
也就是說 defineXXX系列 只有在 <script setup> 標籤內部才會被識別,如果在單獨的js檔案裡面,不會被識別。

這就導致 defineProps 和 defineEmits 無法做成基類的形式。
如果需要的基類不涉及 defineProps 和 defineEmits 的話,那麼還是可以在單獨的js檔案裡面定義一個function或者class的,(即做一個綜合的hooks)。

如果涉及 defineProps 和 defineEmits,那麼,我也沒想出來辦法。(只能第二種方式)

相關文章