Skip to content

编译期宏函数特点:不用导入、只在 setup 语法糖里用、打包会删掉

defineProps

接收父组件传参

vue
<script setup>
// 简单版
const props = defineProps(['name','age'])

// 严谨版
const props = defineProps({
  name: String,
  age: {
    type: Number,
    default: 18,
    required: false
  }
})
</script>

useAttrs

useAttrs() 函数用于接收父组件向子组件传递但是并没有被 defineProps 接收的属性

父组件

vue
<template>
  <!-- 传了class、data-id、title,子组件props只接了title -->
  <MyCard 
    title="我是标题" 
    class="red-card" 
    data-id="666"
  />
</template>

子组件

vue
<template>
  <!-- 把attrs所有属性绑到根div上 -->
  <div v-bind="attrs">{{ props.title }}</div>
</template>

<script setup>
import { useAttrs } from 'vue'

// 只接收 title
const props = defineProps(['title'])

// 剩下 class/data-id 全都进 attrs
const attrs = useAttrs()

console.log(attrs) 
// { class: "red-card", dataId: "666" }
</script>

知识点

  1. 属性会自动驼峰:data-iddataId
  2. inheritAttrs: true(默认):Vue 自动把 attrs 挂到根标签;设为false就要手动 v-bind="attrs"

defineEmits

声明自定义事件

vue
<script setup>
const emit = defineEmits(['submit','update:selectedAnswers'])

// 触发
emit('update:selectedAnswers', [1,2,3])
</script>

defineModel

Vue3.4+ 极简 v-model

vue
<script setup>
// 默认 v-model
const val = defineModel()

// 命名参数 → 对应 v-model:selectedAnswers
const selectedAnswers = defineModel('selectedAnswers',{
  default: ()=>[]
})

// 直接改,自动双向同步
selectedAnswers.value.push(1)
</script>

defineExpose

暴露子组件 属性/方法 给父 ref

vue
<script setup>
const count = ref(0)
const add = ()=>count.value++

defineExpose({ count, add })
</script>

defineOptions

配置组件原生选项

vue
<script setup>
defineOptions({
  name: "QuestionComp", // 组件名
  inheritAttrs: false  // 关闭class/style自动透传
})
</script>

defineSlots

TS 专属:约束插槽类型

vue
<script setup lang="ts">
defineSlots<{
  // 默认插槽:必须传 row、index 参数
  default: (props: { row: string; index: number }) => any
  // header插槽:可选,无参数
  header?: () => any
  // footer插槽:可选,带count参数
  footer?: (props: { count: number }) => any
}>()
</script>

useSlots

useSlots() 函数用于在 JS 代码里,看父组件有没有传某个插槽

父组件

vue
<template>
  <MyCard>
    <!-- 传了header插槽 -->
    <template #header>自定义头部</template>
    默认内容
  </MyCard>
</template>

子组件

vue
<template>
  <div>
    <!-- 有header插槽就渲染,没有就显示默认 -->
    <div v-if="slots.header">
      <slot name="header"></slot>
    </div>
    <div v-else>默认头部标题</div>

    <slot></slot>
  </div>
</template>

<script setup>
import { useSlots } from 'vue'

// 获取所有插槽对象
const slots = useSlots()

// JS里判断:是否存在header插槽
console.log(!!slots.header) // true
</script>