Skip to content

Vue 3 中 <script setup> 使用 Props 指南

问题描述

在 Vue 3 的 <script setup> 语法糖中,很多开发者不清楚如何正确声明和使用 props。在传统的 Options API 中,我们使用 props 选项来定义组件属性,但在 Composition API 的 <script setup> 中,需要使用不同的方法。

原问题中的代码示例展示了常见的困惑:

html
<script setup>
import TopNavbar from '@/layout/TopNavbar.vue'
import { defineProps, reactive } from 'vue'

defineProps({
  no: String
})

const init = async () => {
  // 如何在这里使用 props?
  // const { data } = await getRoomByNo(props.no)
}
</script>

解决方案

基础用法:声明和使用 Props

<script setup> 中,使用 defineProps() 宏来声明组件属性:

html
<script setup>
import { defineProps } from 'vue'

// 声明 props
const props = defineProps({
  no: String,
  // 更多属性定义
  title: {
    type: String,
    required: true
  },
  count: {
    type: Number,
    default: 0
  }
})

// 在代码中使用 props
console.log(props.no)
console.log(props.title)
</script>

在异步函数中使用 Props

要解决原始问题中的异步函数使用 props 的场景:

html
<script setup>
import { defineProps, reactive } from 'vue'

const props = defineProps({
  no: String
})

const state = reactive({
  room: {}
})

const init = async () => {
  // 正确使用 props.no
  const { data } = await getRoomByNo(props.no)
  console.log(data)
  state.room = data
}

init()
</script>

使用 toRefs 保持响应性

TIP

如果需要保持 props 的响应性,可以使用 toRefs 进行解构:

html
<script setup>
import { defineProps, toRefs } from 'vue'

const props = defineProps({
  no: String,
  myIdOfSomething: Number
})

const { no, myIdOfSomething } = toRefs(props)

console.log(no.value) // 使用 .value 访问
console.log(myIdOfSomething.value)
</script>

TypeScript 支持

基本类型定义

对于 TypeScript 用户,可以使用类型注解来获得更好的类型安全:

html
<script setup lang="ts">
interface Props {
  no: string
  disabled: boolean
  loading?: boolean
}

const props = defineProps<Props>()
</script>

带默认值的类型定义

WARNING

使用类型注解方式时,如果需要默认值,必须使用 withDefaults 辅助函数。

html
<script setup lang="ts">
interface Props {
  disabled: boolean
  loading: boolean
  bordered: boolean
  label?: string
}

const props = withDefaults(defineProps<Props>(), {
  label: "Button Label",
})
</script>

替代方案:选项式类型定义

另一种在 TypeScript 中定义 props 的方式:

html
<script setup lang="ts">
import { PropType } from 'vue'

const props = defineProps({
  color: {
    type: String as PropType<'primary'|'info'|'success'|'error'|'warning'>,
    default: 'primary'
  }
})
</script>

完整示例

子组件 CircleImage.vue

html
<template>
  <div class="px-4 w-8/12 sm:w-3/12">
    <img :src="src" :alt="alt" class="border-none rounded-full h-auto max-w-full align-middle" />
  </div>
</template>

<script setup>
const props = defineProps({
  src: String,
  alt: String,
})
</script>

父组件 MyView.vue

html
<template>
  <div class="flex flex-wrap justify-center">
    <CircleImage src="/file1.jpg" alt="one" />
    <CircleImage src="/file2.svg" alt="two" />
  </div>
</template>

<script setup>
import CircleImage from '@/components/CircleImage.vue'
</script>

注意事项

  1. 自动导入definePropswithDefaults<script setup> 中自动可用,无需显式导入
  2. 响应性:通过 defineProps 声明的 props 已经是响应式的
  3. 默认值:在非 TypeScript 环境下,可以直接在选项对象中定义默认值
  4. 只读性:Props 是只读的,不应在子组件中直接修改

总结

Vue 3 的 <script setup> 语法提供了简洁的 props 声明方式:

  • 使用 defineProps() 宏声明 props
  • 对于 TypeScript,可以使用类型注解增强类型安全
  • 需要默认值时,使用 withDefaults() 辅助函数
  • 保持响应性可使用 toRefs() 解构 props

这种方法使得组件代码更加简洁,同时保持了完整的类型支持和响应性特性。