我相信這是 Vue3 複用代碼的正確姿勢!

在 vue2 中代碼複用一般是 mixins[1] 混入來實現但使用 mixins 進行代碼複用會有一些問題

vue3 雖然對 mixins 進行了保留但不再推薦使用。

mixins 問題

不清晰的數據來源:當使用了多個 mixin 時,實例上的數據屬性來自哪個 mixin 變得不清晰,這使追溯實現和理解組件行爲變得困難。

命名空間衝突:多個來自不同作者的 mixin 可能會註冊相同的屬性名,造成命名衝突。

隱式的跨 mixin 交流:多個 mixin 需要依賴共享的屬性名來進行相互作用,這使得它們隱性地耦合在一起。

當然這些問題對於的程序員來說都是可以避免的,但最好的方法是換一種更好的方式。

在 vue3 中複用代碼

目前作者的認知裏寫 sfc 進行代碼複用依然需要使用 mixins 來實現。

所以這裏說的是使用 Composition API 組織代碼的情況下(vue2.7 也已經支持)

先了解下什麼是組合式 API?

組合式 API (Composition API) 是一系列 API 的集合,使我們可以使用函數而不是聲明選項的方式書寫 Vue 組件。它是一個概括性的術語,涵蓋了以下方面的 API:

這段話完全來自官網 更多的內容還是需要看官網

爲什麼要有組合式 API?

這裏官方文檔列出了幾點:更好的邏輯複用 、更靈活的代碼組織 、更好的類型推導 、更小的生產包體積。這裏已經出現了本文關注的重點即 更好的邏輯複用 我們來看下官方的說明

組合式 API 最基本的優勢是它使我們能夠通過組合函數 [5] 來實現更加簡潔高效的邏輯複用。在選項式 API 中我們主要的邏輯複用機制是 mixins,而組合式 API 解決了 mixins 的所有缺陷 [6]。

兩個結論 1. 組合式 API 解決了 mixins 的所有缺陷 2. 能夠通過組合函數來實現更加簡潔高效的邏輯複用。

什麼是 “組合式函數” ?

在 Vue 應用的概念中,“組合式函數”(Composables) 是一個利用 Vue 的組合式 API 來封裝和複用有狀態邏輯的函數。

一般會以 use 開頭進行函數命名,放到 composables 目錄中,其他約定 [7]。

組合函數內部可以使用 組合式api,一個組合式函數可以調用一個或多個其他的組合式函數。

這使得我們可以像使用多個組件組合成整個應用一樣,用多個較小且邏輯獨立的單元來組合形成複雜的邏輯。

當時我看到這裏時候其實有個疑問 爲什麼要強調是有狀態邏輯的函數(有狀態函數是指函數內部有自己的狀態更改),這個問題先留着,看完代碼示例自會理解。

一個例子

抽離後臺管理列表共用的邏輯,示例只是列表相關邏輯封裝,你甚至可以將增刪改查的邏輯封裝進去。

usePage.js

import { reactive, ref } from 'vue'
// 一個用於重置對象字段爲原始值的函數
import { resetObjToPrimitiveType } from '@/utils/tool'

/**
 * @description usePage 接收一個 opts 參數,返回列表所需數據
 * @param {Object} opts.searchForm - 默認查詢參數
 * @param {Function} opts.getListApi  - 獲取列表數據的接口
 * @param {Function} opts.customQueryParameters  - 自定義查詢參數
 * @param {Function} opts.getListFunc  - 執行完 getList 成功後執行的邏輯 有一個opts參數
 * @param {Function} opts.resetFunc  - 執行完 reset 後執行的邏輯
 * @param {Function} opts.sizeChangeFunc  - 執行完 sizeChange 後執行的邏輯
 * @param {Function} opts.currentChangeFunc  - 執行完 currentChange 後執行的邏輯
 */
export const usePage = (opts) => {
  // searchForm 由外部傳入,內部傳入導出的數據無法推導類型即無法知道對象裏有什麼也會失去代碼提示
  const {
    searchForm = {},
    getListApi,
    customQueryParameters = () => {},
    getListFunc = (opts) => {},
    resetFunc = () => {},
    sizeChangeFunc = () => {},
    currentChangeFunc = () => {}
  } = opts

  const reset = () => {
    Object.assign(searchForm, resetObjToPrimitiveType(searchForm))
    resetFunc()
    handleCurrentChange(1)
  }

  const page = reactive({
    pageSize: 10,
    pageNo: 1,
    total: 0
  })

  const tableData = ref([])
  const getList = () => {
    const opts = {
      ...page,
      ...searchForm,
      ...customQueryParameters()
    }

    getListApi(opts).then((res) => {
      if (res.code === 0) {
        tableData.value = res.data?.rows || []
        page.total = res.data?.total || 0

        getListFunc(opts)
      }
    })
  }

  const handleSizeChange = (size) => {
    page.pageSize = size
    sizeChangeFunc()
    getList()
  }

  const handleCurrentChange = (cur) => {
    page.pageNo = cur
    currentChangeFunc()
    getList()
  }

  return {
    searchForm,
    reset,
    page,
    tableData,
    handleSizeChange,
    handleCurrentChange
  }
}

組件內使用

import { reactive, ref, computed } from 'vue'
import { usePage } from '@/composables/usePage'
import testModel from '@/model/test'

// 查詢參數
const searchForm = reactive({
  createEndTime: '',
  createStartTime: ''
})

// 接收 查詢參數、獲取列表的接口 返回 列表所需要的數據、分頁參數、分頁函數等
const { reset, page, tableData, handleSizeChange, handleCurrentChange } = usePage({
  searchForm,
  getListApi: testModel.getList
})

// 首次獲取數據使用 reset方式即可 tableData 的數據自動更新
reset()

組合式函數其實就是一個函數接收一些參數返回一些東西,將邏輯進行封裝、共用。

如果函數依賴 store、router 等

import {useStore} from 'vuex'
import {computed} from 'vue'

export const useTest = () => {
    // 獲取store
  const store = useStore()
  
  const getOrgById = (id) => {
      // 使用
    const orgObj = computed(() => store.state.orgObj)
    return orgObj.value[id]
  }

  return {
    getOrgById
  }
}

總結

說明 vue2 使用 mixins 進行代碼複用所帶來的問題,引出了 vue3 如何進行代碼複用。

介紹 組合式 API (Composition API) 以及爲什麼需要 組合式 API, 什麼是 組合式函數 通過一個例子讓大家瞭解了 組合式函數 的用法。

Composition API組合式函數 用來解決 vue2 mixins 帶來的問題只是附帶,它可以做更多做的更好。

同時也是一把雙刃劍,取決於使用的人。就像有的人可以把代碼寫的一塌糊塗有的人卻可以把代碼寫的像詩一樣!

關於本文

作者:唐詩

https://juejin.cn/post/7160480314089799710

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/5k5XmvJVTjv-H2T_A-LhDw