Props

来源: 2024-05-25 16:31:23 播报

一个组件需要显式声明它所接受的 props,这样 Vue 才能知道外部传入的哪些是 props,哪些是透传 attribute。

Props 声明

script setup 的单文件组件中,props 可以使用 defineProps() 声明。

示例:

<script setup>
const props = defineProps(['foo'])

console.log(props.foo)
</script>

在 setup方法 的组件中,prop 可以使用 props 选项来声明。

示例:

export default {
  props: ['foo'],
  setup(props) {
    // setup() 接收 props 作为第一个参数
    console.log(props.foo)
  }
}

注意:传递给 defineProps() 的参数和提供给 props 选项的值是相同的。

使用对象形式来声明 prop。

示例:

// 使用 <script setup>
defineProps({
  title: String,
  likes: Number
})
// 非 <script setup>
export default {
  props: {
    title: String,
    likes: Number
  }
}
// 搭配 TypeScript 使用
<script setup lang="ts">
defineProps<{
  title?: string
  likes?: number
}>()
</script>

以对象形式声明的每个属性,key 是 prop 的名称,而值则是该 prop 预期类型的构造函数。

传递 prop 的细节

Prop 名字格式

应使用 camelCase 形式。

示例:

defineProps({
  greetingMessage: String
})

<span>{{ greetingMessage }}</span>

在向子组件传递 props 时使用 camelCase 形式 (使用 DOM 内模板时例外),但实际上为了和 HTML attribute 对齐,我们通常会将其写为 kebab-case 形式:

<MyComponent greeting-message="hello" />

静态 vs. 动态 Prop

静态的props。

示例:

<BlogPost title="My journey with Vue" />

动态的props(使用 v-bind 或缩写)。

示例:

<!-- 根据一个变量的值动态传入 -->
<BlogPost :title="post.title" />

<!-- 根据一个更复杂表达式的值动态传入 -->
<BlogPost :title="post.title   ' by '   post.author.name" />

传递不同的值类型

1、String

2、Number

3、Boolean

4、Array

5、Object

使用一个对象绑定多个 prop

使用没有参数的 v-bind,即只使用 v-bind 而非 :prop-name。

示例:

const post = {
  id: 1,
  title: 'My Journey with Vue'
}
<BlogPost v-bind="post" />
<BlogPost :id="post.id" :title="post.title" />

上面的模板props 传值是相同的。

单向数据流

所有的 props 都遵循着单向绑定原则,props 因父组件的更新而变化,自然地将新的状态向下流往子组件,而不会逆向传递。

prop 被用于传入初始值。

子组件中新定义一个局部数据属性,从 props 上获取初始值。

示例:

const props = defineProps(['initialCounter'])

// 计数器只是将 props.initialCounter 作为初始值
// 像下面这样做就使 prop 和后续更新无关了
const counter = ref(props.initialCounter)

需要对传入的 prop 值做进一步的转换。

子组件中最好是基于该 prop 值定义一个计算属性。

示例:

const props = defineProps(['size'])

// 该 prop 变更时计算属性也会自动更新
const normalizedSize = computed(() => props.size.trim().toLowerCase())

更改对象 / 数组类型的 props

子组件无法更改 props 绑定,但仍然可以更改对象或数组内部的值。这是因为 JavaScript 的对象和数组是按引用传递,但不建议这么改,因为这样操作有很大的性能损耗。

Prop 校验

要声明对 props 的校验,可以向 defineProps() 提供一个带有 props 校验选项的对象。

示例:

defineProps({
  // 基础类型检查
  // (给出 `null` 和 `undefined` 值则会跳过任何类型检查)
  propA: Number,
  // 多种可能的类型
  propB: [String, Number],
  // 必传,且为 String 类型
  propC: {
    type: String,
    required: true
  },
  // 必传但可为空的字符串
  propD: {
    type: [String, null],
    required: true
  },
  // Number 类型的默认值
  propE: {
    type: Number,
    default: 100
  },
  // 对象类型的默认值
  propF: {
    type: Object,
    // 对象或数组的默认值
    // 必须从一个工厂函数返回。
    // 该函数接收组件所接收到的原始 prop 作为参数。
    default(rawProps) {
      return { message: 'hello' }
    }
  },
  // 自定义类型校验函数
  // 在 3.4  中完整的 props 作为第二个参数传入
  propG: {
    validator(value, props) {
      // The value must match one of these strings
      return ['success', 'warning', 'danger'].includes(value)
    }
  },
  // 函数类型的默认值
  propH: {
    type: Function,
    // 不像对象或数组的默认,这不是一个
    // 工厂函数。这会是一个用来作为默认值的函数
    default() {
      return 'Default function'
    }
  }
})

运行时类型检查

校验选项中的 type 可以是下列这些原生构造函数。

1、String

2、Number

3、Boolean

4、Array

5、Object

6、Date

7、Function

8、Symbol

9、Error

type 也可以是自定义的类或构造函数,将其作为一个 prop 的类型。Vue 将会通过 instanceof 来检查类型是否匹配。

示例:

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }
}

defineProps({
  author: Person
})

可为空的类型

如果该类型是必传的但可为空,你可以用一个包含 null 的数组语法。

示例:

defineProps({
  id: {
    type: [String, null],
    required: true
  }
})

Boolean 类型转换

示例:

defineProps({
  disabled: Boolean
})
<!-- 等同于传入 :disabled="true" -->
<MyComponent disabled />

<!-- 等同于传入 :disabled="false" -->
<MyComponent />