Vue 組件如何模塊化抽離Props
一、為什么要抽離Props
在前端框架中封裝一個組件是非常常見的,對于組件來講,往往離不開組件之間屬性的傳遞,通過屬性能夠控制組件的樣式內(nèi)容、處理邏輯類型、數(shù)據(jù)傳遞。假如有這么一個用于簽名的組件,能夠控制畫筆的大小、顏色、畫布背景顏色寬高、按鈕的文案提示等,那么它的屬性必定是有十幾個選項,假設(shè) 800 行代碼里可能就得花費 100 行去聲明這些屬性,這明顯是不合適的。
這也是我為什么提議要抽離組件 Props 的原因,實際上這個簽名組件也確實存在,源自于我在 Uniapp 插件市場上的一個提交。
抽離Props的好處有這么幾點:
- 降低、隔離組件的復(fù)雜度,降低維護成本與提高程序可靠率。
- 引入一個中間的、易于理解的抽象概覽。
- 隱藏非核心處理部分的實現(xiàn)細節(jié),提高組件源碼的可閱讀性。
- 促進代碼的重用,包裝一定的結(jié)構(gòu)風范。
- 屬性的抽離,有助于自動化程序生成對應(yīng)文檔。
那么要怎么做才能抽離出 Props 呢,在 Vue 身上根據(jù)選項式 API 和組合式 API 寫法的不同,做法也不一樣。
一些簡單的組件可能只有一兩個屬性,甚至是沒有屬性,對于這種情況下,可能不抽離 Props 會顯得更方便一些,但一定要注意的是應(yīng)當把組件以后要做的和其擴展維護性放在一起考慮,往往十幾個屬性堆疊的組件都是從一兩個屬性的組件開始的,既然以后會越加越多,那為何不提早抽離處理方便以后再加呢,當然具體怎么做取決于自己。
二、選項式API方式抽離
選項式 API 適用于 Vue2 和 Vue3,需要使用到 Mixins 混合方法。Mixins 不只是能抽離 Props,它能做到更多,我相信大部分對這個功能會比較熟悉,有興趣的可以參考此文檔 Mixins。
import type { PropType } from "vue"; export default { props: { prop1: { type: String, required: true }, prop2: { type: Array as PropType<string[]>, default: ["a", "b", "c"] }, prop3: { type: Array as PropType<Record<string, unknown>[]>, defalue: [] } } };
<script lang="ts"> import props from "./props"; export default { mixins: [props] }; </script>
三、組合式API方式抽離
組合式 API 屬性的聲明依賴于 defineProps
編譯宏,這個編譯宏只能出現(xiàn)在 .vue 的文件中才能正確識別,那么對于 Hook 來說,抽離 Props 是行不通的。
3.1 TypeScript類型方式
defineProps
是支持 typescript 類型標注聲明的,直接引入一個類型行不行得通的呢?關(guān)于這個問題要分 Vue3.3 版本之前與之后,Vue3.3 版本之前是不允許將類型接口以導(dǎo)入的形式傳給 defineProps
泛型,在官方文檔中也有對應(yīng)的標注,相關(guān)討論在這 issue。
但這還有一個問題,typescript只能聲明類型,如果要配置默認值的話需要搭配另外一個編譯宏 withDefaults
去實現(xiàn),且類型接口能導(dǎo)入還好,若是不能導(dǎo)入的話,本地類型接口又違背降低復(fù)雜度而抽離的初衷了,在我看來還不如不用。依據(jù) Vue3.3 版本之后,可以參考下列示例:
export interface Props { prop1: string; prop2?: string[]; prop3?: Record<string, unknown>[]; } export const propsDefault = { prop1: "默認值", prop2: () => ["a", "b", "c"] };
<template> <div>{{ $props.prop1 }}</div> <div>{{ $props.prop2 }}</div> </template> <script lang="ts" setup> import { type Props, propsDefault } from "./props"; const $props = withDefaults(defineProps<Props>(), propsDefault); </script>
3.2 文件分離方式
人們往往容易把簡單的東西想復(fù)雜,將 Props 類型聲明存放到一個單獨文件里,只要確保 defineProps
在 .vue 文件中使用即可,這么做有個好處就是簡單易用且在不依賴typescript的時候基本兼容 Vue3 各版本,對于我來講,我更習慣于使用 文件分離式 進行 Props 的抽離。
這種方式與 Hook 不同的是,Hook需要一層函數(shù)包裹執(zhí)行,而這樣相當于直接引入配置文件中的一個變量。
import type { PropType } from "vue"; export const props = { prop1: { type: String, required: true }, prop2: { type: Array as PropType<string[]>, default: ["a", "b", "c"] }, prop3: { type: Array as PropType<Record<string, unknown>[]>, defalue: [] } };
<template> <div>{{ $props.prop1 }}</div> <div>{{ $props.prop2 }}</div> </template> <script lang="ts" setup> const $props = defineProps(props); </script>
對于 TypeScript 如果需要獲取 Props 的類型,那就需要用到 Vue 的一個輔助類型 ExtractPropTypes
,對于外部引用(父組件)的話使用 ExtractPublicPropTypes
,只需在上面示例的基礎(chǔ)上改造一下??。
import type { PropType, ExtractPropTypes } from "vue"; export const props = { prop1: { type: String, required: true }, prop2: { type: Array as PropType<string[]>, default: ["a", "b", "c"] }, prop3: { type: Array as PropType<Record<string, unknown>[]>, defalue: [] } }; // 導(dǎo)出 Props 類型 export type Props = ExtractPropTypes<typeof props>;
其實 ElementUI 的源碼也是這么去做的,以 el-empty 組件為例,源碼中對于屬性的分離是這么寫的。
3.3 對文件分離方式優(yōu)化
從優(yōu)化的角度上看,Vue 的Props聲明方式都是一樣的,同樣的代碼寫多次顯得有些冗余,那不妨將這些 Props 聲明再次優(yōu)化一下,編寫一個公共的 Props 輔助工具進行聲明。
"use strict"; /** * 組件屬性輔助 * ?? 能夠使用更少的代碼編寫,有助于減少包體積 */ import type { PropType } from "vue"; export const unknownProp = null as unknown as PropType<unknown>; export const numericProp = [Number, String]; export const truthProp = { type: Boolean, default: true as const }; export const lieProp = { type: Boolean, default: false as const }; export const makeRequiredProp = <T>(type: T) => { return { type, required: true as const }; }; export const makeNumericProp = <T>(defVal: T) => { return { type: numericProp, default: defVal }; }; export const makeStringProp = <T>(defVal: T) => { return { type: String as unknown as PropType<T>, default: defVal }; }; export const makeNumberProp = <T>(defVal: T) => { return { type: Number as unknown as PropType<T>, default: defVal }; }; export const makeArrayProp = <T>(defVal: T[]) => { return { type: Array as PropType<T[]>, default: () => defVal }; }; export const makeObjectProp = <T>(defVal: T) => { return { type: Object as PropType<T>, default: () => defVal }; }; export const makeFuncProp = <T>(defVal: T) => { return { type: Function as PropType<T>, default: defVal }; }; // 聯(lián)和類型屬性 export const makeUniteProp = <T, V>(type: T[], defVal: V) => { return { type, default: () => defVal }; };
import type { PropType, ExtractPropTypes } from "vue"; import {makeArrayProp, makeRequiredProp} from "@/utils/props" export const props = { prop1: makeRequiredProp(String), prop2: makeArrayProp<string>(["a", "b", "c"]), prop3: makeArrayProp<Record<string, unknown>[]>([]) }; export type Props = ExtractPropTypes<typeof props>;
參考資料??
官方手冊:
到此這篇關(guān)于Vue 組件如何模塊化抽離Props的文章就介紹到這了,更多相關(guān)Vue 模塊化抽離Props內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue3組合式函數(shù)Composable實戰(zhàn)ref和unref使用
這篇文章主要為大家介紹了Vue3組合式函數(shù)Composable實戰(zhàn)ref和unref使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-06-06Vue3中ref和reactive的基本使用及區(qū)別詳析
這篇文章主要給大家介紹了關(guān)于Vue3中ref和reactive的基本使用及區(qū)別的相關(guān)資料,需要的朋友可以參考下2022-07-07解決Vue2跨域報錯AxiosError?net::ERR_FAILED、?Network?Error、ERR_N
這篇文章主要給大家介紹了關(guān)于解決Vue2跨域報錯AxiosError?net::ERR_FAILED、?Network?Error、ERR_NETWORK的相關(guān)資料,文中通過圖文介紹的非常詳細,需要的朋友可以參考下2022-11-11vue3使用自定義指令實現(xiàn)el dialog拖拽功能示例詳解
這篇文章主要為大家介紹了vue3使用自定義指令實現(xiàn)el dialog拖拽功能示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09監(jiān)聽element-ui table滾動事件的方法
這篇文章主要介紹了監(jiān)聽element-ui table滾動事件的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-03-03