『精』Vue 组件如何模块化抽离Props

『精』Vue 组件如何模块化抽离Props.jpg

『精』Vue 组件如何模块化抽离Props


一、为什么要抽离Props

在前端框架中封装一个组件是非常常见的,对于组件来讲,往往离不开组件之间属性的传递,通过属性能够控制组件的样式内容、处理逻辑类型、数据传递。假如有这么一个用于签名的组件,能够控制画笔的大小、颜色、画布背景颜色宽高、按钮的文案提示等,那么它的属性必定是有十几个选项,假设 800 行代码里可能就得花费 100 行去声明这些属性,这明显是不合适的。
这也是我为什么提议要抽离组件 Props 的原因,实际上这个签名组件也确实存在,源自于我在 Uniapp 插件市场上的一个提交
图 1-1
抽离Props的好处有这么几点:

  • 降低、隔离组件的复杂度,降低维护成本与提高程序可靠率。
  • 引入一个中间的、易于理解的抽象概览。
  • 隐藏非核心处理部分的实现细节,提高组件源码的可阅读性。
  • 促进代码的重用,包装一定的结构风范。
  • 属性的抽离,有助于自动化程序生成对应文档。

那么要怎么做才能抽离出 Props 呢,在 Vue 身上根据选项式 API 和组合式 API 写法的不同,做法也不一样。
一些简单的组件可能只有一两个属性,甚至是没有属性,对于这种情况下,可能不抽离 Props 会显得更方便一些,但一定要注意的是应当把组件以后要做的和其扩展维护性放在一起考虑,往往十几个属性堆叠的组件都是从一两个属性的组件开始的,既然以后会越加越多,那为何不提早抽离处理方便以后再加呢,当然具体怎么做取决于自己。


二、选项式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 编译宏,这个编译宏只能出现在 .vue 的文件中才能正确识别,那么对于 Hook 来说,抽离 Props 是行不通的。

3.1 TypeScript类型方式

defineProps 是支持 typescript 类型标注声明的,直接引入一个类型行不行得通的呢?关于这个问题要分 Vue3.3 版本之前与之后,Vue3.3 版本之前是不允许将类型接口以导入的形式传给 defineProps 泛型,在官方文档中也有对应的标注,相关讨论在这 issue
图 3-1
图 3-2

但这还有一个问题,typescript只能声明类型,如果要配置默认值的话需要搭配另外一个编译宏 withDefaults 去实现,且类型接口能导入还好,若是不能导入的话,本地类型接口又违背降低复杂度而抽离的初衷了,在我看来还不如不用。依据 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 文件分离方式

人们往往容易把简单的东西想复杂,将 Props 类型声明存放到一个单独文件里,只要确保 defineProps 在 .vue 文件中使用即可,这么做有个好处就是简单易用且在不依赖typescript的时候基本兼容 Vue3 各版本,对于我来讲,我更习惯于使用 文件分离式 进行 Props 的抽离。
这种方式与 Hook 不同的是,Hook需要一层函数包裹执行,而这样相当于直接引入配置文件中的一个变量。

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,只需在上面示例的基础上改造一下👇。

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: []
  }
};

// 导出 Props 类型
export type Props = ExtractPropTypes<typeof props>;

其实 ElementUI 的源码也是这么去做的,以 el-empty 组件为例,源码中对于属性的分离是这么写的。
图 3-3

3.3 对文件分离方式优化

从优化的角度上看,Vue 的Props声明方式都是一样的,同样的代码写多次显得有些冗余,那不妨将这些 Props 声明再次优化一下,编写一个公共的 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 };
};

// 联和类型属性
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>;


参考资料💘

🍅因发布平台差异导致阅读体验不同,源文贴出:《『精』Vue 组件如何模块化抽离Props》


推荐博文🍗

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
回答: Vue组件化和模块化开发是Vue.js中的两个重要概念。Vue组件化是将一个复杂的页面分解成多个小的组件,每个组件都是独立的个体,互不影响,这样可以提高代码的可维护性和重用性。我们可以通过Vue.component()方法注册全局组件,也可以通过Vue实例的components选项注册局部组件组件可以包括模板、脚本和样式,并且可以通过props和events实现组件间的通讯,实现更灵活的组件化开发。\[1\]\[2\]\[3\] 而模块化开发是一种将代码分解成独立的模块,每个模块都有自己的功能和责任,可以独立开发、测试和维护。在Vue.js中,我们可以使用ES6的模块化语法来实现模块化开发。通过将代码分解成多个模块,可以提高代码的可读性和可维护性,同时也方便了团队协作和代码复用。模块化开发可以帮助我们更好地组织和管理代码,提高开发效率。 #### 引用[.reference_title] - *1* *2* [vue组件化编程(模块与组件模块化组件化,非单文件和单文件组件)](https://blog.csdn.net/weixin_41549971/article/details/131656555)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Vue学习笔记(二)组件化和模块化](https://blog.csdn.net/qq_41404112/article/details/116759104)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值