你还在为每个相同组件写相同的 prop 吗

307 阅读3分钟

我们在日常使用 Vue 一些组件库开发项目时,组件库中一些组件的初始 props 属性没有或者不是我们想要的,这就使得我们需要在具体使用组件的地方为其设置一些我们期望的值。这里以 Element 组件库为例,比如我们在项目中使用 Table 组件,整个项目的表格设计都是带边框的,但是 Table 组件默认是不带 border 的,所以我们需要在每个使用 Table 组件的地方手动将 border 设为 true。这么做或许有些许麻烦,因此我实现了一个插件: VueSetProps,使用它对于统一的属性值或者大多数需要设置的属性值你不需要在每一处使用时单独设置了,你只需要在初始化的时候统一设置,后面使用的所有组件都会生效。当然,如果你在某处不希望使用设置的全局默认属性,只需要在具体使用的时候设置值覆盖默认值即可。

下面是 VueSetProps 具体使用方式:

npm i vue-set-props -S
# or
yarn add vue-set-props
// main.js
import Vue from 'vue'
import ElementUI from 'element-ui'
import VueSetProps from 'vue-set-props'

Vue.use(ElementUI)
// 必须在组件库注册后使用
Vue.use(VueSetProps, {
  library: ElementUI,
  setProps: {
    Input: {
      clearable: true
    },
    Table: {
      border: true
    }
  }
})

// ...

经过上面的初始化设置,在使用 Input 组件时都会带上可清除的图标,Table 组件时都会带上表格边框,不需要在具体使用时单独设置。更多使用细节可以参考文档

其实 VueSetProps 的原理很简单,就是根据对应组件找到对应的 props 设置通过修改或添加 default 属性设置默认值。可以这么做得益于 Vue 内部对应 props 属性的规范化。这里还是以 Element 为例,在 Vue.use(ElementUI) 调用时,内部其实是调用 Vue.component() 将一个个组件挂载到全局,而调用此方法时,Vue 内部会使用 mergeOptions 进行选项的合并规范。我们都知道 Vue 为我们提供了多种 props 的写法,下面列出对应的写法即内部处理后的结果:

// 1. 一个数组
props: ['size', 'myMessage']
// 合并处理后
props: {
  size: { type: null },
  myMessage: { type: null }
}

// 2. 带有类型
props: {
  size: Number
}
// 合并处理后
props: {
  size: { type: Number }
}

// 3. 带有默认值
props: {
  size: {
    type: Number,
    default: 1
  }
}
// 合并处理后
props: {
  size: {
    type: Number,
    default: 1
  }
}

通过上面的分析,我们可以看出 props 的属性的对应值最后都被处理为一个对象,借助于此我们可以修改其对应的 default 或者手动添加 default 属性值,而不用去考虑兼容多种 props 的写法,这也是 VueSetProps 必须在组件库注册后使用的原因。

知道了上面这些,最后就是找到组件对应的 prop 属性。我们都知道,Vue 可以在组件自身中定义 props 选项,也提供了通过 mixins 方式来混入 props 选项,其内部会对两者做一个合并处理。所以 VueSetProps 内部在找对应的 prop 时会在组件本身 props 选项以及 mixins 选项中依次递归寻找,找到就修改或者设置其对应的 default 值,而对于设置的 defalut 类型是否符合类型,则交给 Vue 后续运行时去校验。

通过以上方式就实现了对应组件库组件的初始化默认值的设置,一处设置,全局使用。

Chat: 现在对于设置的 prop,如果没有在对应组件选项中找到,则会被忽略掉且在非生产环境打印一个警告,对于此,想到的另一个点是把没有找到的 prop 挂载到对于到组件到 $attrs 上,这样就能覆盖到更全的场景,但是 $attrs 是在具体组件使用时处理的。所以对于此,如果你有好的 idea 或者好的实现方式,👏欢迎来提交 issue,也希望大家🤪给个 star