Vue 道具类型验证对象模式

Vue prop type validation object schema

在 React 中,您可以将道具定义为一个对象,然后还可以定义该对象属性的类型,即形状。在 Vue 中,检查对象是否具有特定形状的唯一方法似乎是使用验证器函数。这是目前推荐的策略吗?我确定我可以使用其他库来处理该验证,但看起来 Vue 应该能够处理。

你绝对应该使用 TypeScript. What you are expecting is called "type annotation", you will get a quick overview on TypeScript Quick start tutorial

Vue.js 开箱即用地支持 TypeScript。参见 https://vuejs.org/v2/guide/typescript.html

我要借用@Clorichel 的回答,因为 TypeScript 为我创造了奇迹。这是一个迟到的答案,但我一直需要和你一样的东西:对象模式验证,而不仅仅是“is this prop a string or number?". TypeScript offers the usage of an interface,它在合同上将对象的形状绑定到该类型(接口)。所以如果我有

interface GreetingSchema {
  message: string;
}

执行 let myGreeting = <GreetingSchema>{ message: 1234 } 会抛出错误

Type '{ message: number; }' cannot be converted to type 'GreetingSchema'.
  Types of property 'message' are incompatible.
    Type 'number' is not comparable to type 'string'.

要通过将其与 Vue 集成来进一步扩展它,您可以在编译时利用接口对从父级传递给子级的组件道具执行对象模式验证。例如,子 Greeting 组件 (Greeting.vue) 可以与将传递给它的道具的对象模式相结合(我正在使用嵌套模式来增加趣味性):

<template>
  <div>
    {{message}}{{
      speaker
        ? ` from ${speaker.firstName}${
            speaker.lastName ? ` ${speaker.lastName}` : ''
          }
        : ''
    }}
  </div>
</template>

<script lang="ts">
export default {
  name: 'greeting',
  props: ['message', 'speaker'],
}

interface SpeakerSchema { // this will be a nested schema
  firstName: string;
  lastName?: string; // last name of speaker is optional
}

export interface GreetingSchema {
  message: string;
  speaker?: SpeakerSchema; // speaker is optional
}
</script>

然后您可以将 Greeting 组件及其相应的 GreetingSchema 导入父级,以确保每当父级将其 greetingData 传递给子级时 Greeting对于 v-bindgreetingData 将受 GreetingSchema

中定义的形状约束
<template>
  <div>
    This greeting is being displayed from the parent:
    <greeting v-bind="greetingData" /> 
  </div>
</template>

<script lang="ts">
import Greeting, {GreetingSchema} from './Greeting' // path to Greeting.vue

export default {
  name: 'parent',
  components: {Greeting},
  data () {
    return {
      greetingData: <GreetingSchema>{
        message: 'hi there',
        speaker: {
          firstName: 'myFirstName',
          lastName: 1234 // error because it's not a string
        },
      }
    }
  },
}
</script>

很棒的是,即使在 .vue 文件中,错误也可以在具有正确扩展名的文本编辑器中直接显示。我正在使用 Visual Studio Code, which has TypeScript support out of the box, with the vetur extension. What's even awesomer is that TypeScript's home page features a presentation in which TypeScript is used with Vue's single file components!

仅 TypeScript 解决方案的问题在于它仅在 编译时 检查类型并且仅限于类型检查。如果您要传递动态对象或需要额外的数据约束,这在 运行-time 时对您没有帮助。

我最近发现了一个用于 运行 时间验证的库,但它不太适合与 Vue 2.x 或 Babel 编译器一起使用,因为它使用了 JavaScript 保留字作为函数名称。所以我分叉了这个项目,重构了代码以支持 TypeScript 和 JavaScript,并专门添加了一个 asSuccess() 函数来支持 VueJS 对象属性的验证。

所以如果你有一个像 ...

这样的 TypeScript 接口
interface Pet {
  name: string;
  species: string;
  age?: number;
  isCute?: boolean;
}

您可以使用 JavaScript 或 TypeScript 代码验证 Pet 属性 作为 ...

import { tObject, tString, tNumber, tBoolean, optional } from 'runtime-validator'

// VueJs component
export default {
  name: 'MyPet',
  props: {
    pet: {
      type: Object,
      required: true,
      validator: tObject({
        name: tString(),
        species: tString(),
        age: optional(tNumber()),
        isCute: optional(tBoolean())
      }).asSuccess
    }
  },
  ...
}

tObject() 将接受具有已定义字段的附加字段的对象。如果您需要禁止其他字段,请使用 tObjectStrict().

您还可以添加值约束...

export default {
  name: 'MyPet',
  props: {
    pet: {
      type: Object,
      required: true,
      validator: tObject({
        name: tString(),
        species: oneOf("cat", "dog", "bird"),
        age: optional(range(0, 150)),
        isCute: optional(tBoolean())
      }).asSuccess
    }
  },
  ...
}

它比属性具有更广泛的应用,因为它还可以用于单元测试、vuex 操作和突变、服务器端有效负载等中的数据验证。

完整文档在这里...