最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue中defineProps、defineEmits、defineExpose使用深度解析

 更新時(shí)間:2026年07月22日 10:59:43   作者:星漢燦爛星河  
在企業(yè)級(jí)?Vue3?項(xiàng)目中,組件通信是高頻場(chǎng)景,也是最容易出現(xiàn)類型錯(cuò)誤、傳參錯(cuò)誤、事件名錯(cuò)誤的地方,<script?setup>?提供了三個(gè)編譯器宏,專門用于組件通信,這篇文章主要介紹了Vue中defineProps、defineEmits、defineExpose使用的相關(guān)資料,需要的朋友可以參考下

前言

本文深度解析Vue3 <script setup> 語法中的三個(gè)核心編譯器宏:defineProps、defineEmitsdefineExpose。

defineProps 用于定義組件屬性,支持多種驗(yàn)證方式和TypeScript類型聲明;

defineEmits 用于聲明組件事件,可與v-model集成;

defineExpose 則用于暴露組件實(shí)例的公共API。

文章詳細(xì)介紹了每個(gè)宏的基本用法、TypeScript支持、最佳實(shí)踐以及常見問題解答,并展示了三者如何配合使用構(gòu)建完整組件。

這些宏必須在<script setup>頂層使用,具有編譯時(shí)常量限制,但能顯著提升代碼簡潔性和類型安全性。

這三個(gè)都是 Vue 3 <script setup> 語法中的編譯器宏(編譯時(shí)處理的特殊函數(shù))。它們不需要導(dǎo)入,直接在 <script setup> 中使用。

1.defineProps- 定義組件屬性

基本用法

<script setup>
// 方式1:數(shù)組形式(簡單,無類型檢查)
const props = defineProps(['title', 'count'])

// 方式2:對(duì)象形式(推薦,支持完整驗(yàn)證)
const props = defineProps({
  title: {
    type: String,
    required: true,
    validator: (value) => value.length > 0
  },
  count: {
    type: Number,
    default: 0,
    validator: (value) => value >= 0
  },
  items: {
    type: Array,
    default: () => []
  },
  config: {
    type: Object,
    default: () => ({})
  }
})
</script>

TypeScript 用法

<script setup lang="ts">
// 方式1:純類型聲明(運(yùn)行時(shí)無驗(yàn)證)
interface Props {
  title: string
  count?: number
  items?: string[]
}

const props = defineProps<Props>()

// 方式2:類型聲明 + 默認(rèn)值(Vue 3.3+)
const props = withDefaults(defineProps<Props>(), {
  count: 0,
  items: () => []
})

// 方式3:復(fù)雜類型(聯(lián)合類型、自定義類型)
type Status = 'loading' | 'success' | 'error'

interface ComplexProps {
  id: number | string  // 聯(lián)合類型
  status: Status       // 自定義類型
  metadata?: Record<string, any>
}

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

響應(yīng)式處理 Props

<script setup>
import { computed, toRef, toRefs } from 'vue'

const props = defineProps({
  user: Object,
  active: Boolean
})

// ? 錯(cuò)誤:直接解構(gòu)會(huì)丟失響應(yīng)式
const { user, active } = props

// ? 正確:使用 toRefs 保持響應(yīng)式
const { user, active } = toRefs(props)

// ? 使用 computed 派生值
const userName = computed(() => props.user?.name || 'Unknown')

// ? 使用 toRef 處理單個(gè) prop(props 可能沒有該屬性)
const userId = toRef(props, 'id')  // 安全訪問,有默認(rèn)值
</script>

2.defineEmits- 定義組件事件

基本用法

<script setup>
// 方式1:數(shù)組形式(簡單)
const emit = defineEmits(['submit', 'update:value'])

// 方式2:對(duì)象形式(支持驗(yàn)證)
const emit = defineEmits({
  // 無驗(yàn)證
  submit: null,
  
  // 帶驗(yàn)證函數(shù)
  'update:value': (value) => {
    if (typeof value === 'string' && value.length > 0) {
      return true
    }
    console.warn('Invalid value')
    return false
  },
  
  // 多個(gè)參數(shù)的驗(yàn)證
  'form-submit': (data, timestamp) => {
    return data && typeof timestamp === 'number'
  }
})

// 觸發(fā)事件
const handleSubmit = () => {
  emit('submit', { id: 1, data: 'test' })
}

const updateValue = (value) => {
  emit('update:value', value)
}
</script>

TypeScript 用法

<script setup lang="ts">
// 方式1:類型字面量
const emit = defineEmits<{
  (e: 'submit', data: FormData): void
  (e: 'update:value', value: string): void
  (e: 'toggle'): void
}>()

// 方式2:使用接口(更清晰)
interface Emits {
  (e: 'submit', data: FormData): void
  (e: 'update:modelValue', value: any): void
  (e: 'click', event: MouseEvent): void
}

const emit = defineEmits<Emits>()

// 使用示例
const handleClick = (event: MouseEvent) => {
  emit('click', event)
}
</script>

與 v-model 集成

<!-- CustomInput.vue -->
<script setup>
// 支持 v-model
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])

const updateValue = (event) => {
  emit('update:modelValue', event.target.value)
}
</script>

<template>
  <input :value="modelValue" @input="updateValue" />
</template>

<!-- 父組件使用 -->
<template>
  <CustomInput v-model="username" />
</template>

多個(gè) v-model(Vue 3.3+)

<!-- UserForm.vue -->
<script setup>
// 多個(gè) v-model
const props = defineProps({
  firstName: String,
  lastName: String,
  age: Number
})

const emit = defineEmits([
  'update:firstName',
  'update:lastName', 
  'update:age'
])
</script>

<template>
  <input 
    :value="firstName" 
    @input="emit('update:firstName', $event.target.value)"
  />
  <input 
    :value="lastName"
    @input="emit('update:lastName', $event.target.value)"
  />
</template>

<!-- 父組件使用 -->
<template>
  <UserForm
    v-model:firstName="first"
    v-model:lastName="last"
    v-model:age="userAge"
  />
</template>

3.defineExpose- 暴露組件實(shí)例

為什么需要?

默認(rèn)情況下,<script setup> 中的變量是私有的,父組件無法訪問。defineExpose 用于顯式暴露組件的方法和屬性。

基本用法

<!-- ChildComponent.vue -->
<script setup>
import { ref, computed } from 'vue'

// 私有變量(父組件無法訪問)
const privateCount = ref(0)
const internalData = ref('secret')

// 公共方法
const publicMethod = () => {
  console.log('Public method called')
  privateCount.value++
}

// 計(jì)算屬性
const publicComputed = computed(() => privateCount.value * 2)

// 暴露給父組件
defineExpose({
  publicMethod,
  publicComputed,
  
  // 也可以直接暴露 ref
  count: privateCount,
  
  // 甚至暴露函數(shù)
  reset: () => { privateCount.value = 0 }
})
</script>

父組件使用

<!-- ParentComponent.vue -->
<script setup>
import { ref, onMounted } from 'vue'
import ChildComponent from './ChildComponent.vue'

const childRef = ref(null)

onMounted(() => {
  // 訪問暴露的屬性和方法
  if (childRef.value) {
    childRef.value.publicMethod()      // ? 可以調(diào)用
    console.log(childRef.value.publicComputed)  // ? 可以訪問
    console.log(childRef.value.count)  // ? 可以訪問(因?yàn)楸槐┞叮?
    
    // ? 無法訪問未暴露的
    console.log(childRef.value.internalData)  // undefined
    console.log(childRef.value.privateCount)  // undefined
  }
})
</script>

<template>
  <ChildComponent ref="childRef" />
</template>

TypeScript 類型支持

<!-- ChildComponent.vue -->
<script setup lang="ts">
import { ref } from 'vue'

const count = ref(0)
const name = ref('Vue')

const increment = () => {
  count.value++
}

// 定義暴露的類型
export interface ExposedAPI {
  count: number
  name: string
  increment: () => void
}

defineExpose<ExposedAPI>({
  count,
  name,
  increment
})
</script>

<!-- ParentComponent.vue -->
<script setup lang="ts">
import { ref } from 'vue'
import ChildComponent, { ExposedAPI } from './ChildComponent.vue'

const childRef = ref<ExposedAPI>()

// 現(xiàn)在有完整的類型提示
childRef.value?.increment()  // ? 類型安全
</script>

三者的關(guān)系與配合使用

完整組件示例

<!-- UserProfile.vue -->
<script setup>
import { ref, computed, watch } from 'vue'

// 1. 定義 Props
const props = defineProps({
  userId: {
    type: [String, Number],  // 支持多種類型
    required: true
  },
  editable: {
    type: Boolean,
    default: false
  }
})

// 2. 定義 Emits
const emit = defineEmits({
  'update:user': (userData) => {
    return userData && typeof userData.id === 'number'
  },
  'save': null,
  'cancel': null
})

// 3. 組件內(nèi)部狀態(tài)
const userData = ref(null)
const loading = ref(false)
const error = ref(null)

// 4. 方法
const fetchUser = async () => {
  loading.value = true
  try {
    const response = await fetch(`/api/users/${props.userId}`)
    userData.value = await response.json()
    emit('update:user', userData.value)
  } catch (err) {
    error.value = err
  } finally {
    loading.value = false
  }
}

const saveChanges = async () => {
  // 保存邏輯...
  emit('save', userData.value)
}

// 5. 暴露給父組件的方法
defineExpose({
  refresh: fetchUser,
  reset: () => {
    userData.value = null
    error.value = null
  }
})

// 6. 生命周期/監(jiān)聽
watch(() => props.userId, fetchUser, { immediate: true })
</script>

<template>
  <!-- 模板內(nèi)容 -->
</template>

注意事項(xiàng)和最佳實(shí)踐

1.執(zhí)行時(shí)機(jī)

// 這些編譯器宏必須在 <script setup> 的頂層作用域中使用
// ? 錯(cuò)誤:不能在函數(shù)內(nèi)使用
function setupProps() {
  defineProps({})  // 編譯錯(cuò)誤
}

// ? 正確:頂層使用
defineProps({})

2.重復(fù)定義

// ? 錯(cuò)誤:不能多次調(diào)用
defineProps({ title: String })
defineProps({ count: Number })  // 編譯錯(cuò)誤

// ? 正確:一次定義所有
defineProps({
  title: String,
  count: Number
})

3.與 Options API 混用

vue

<script>
// 可以在同一個(gè)組件中與 Options API 混用
export default {
  // Options API
  inheritAttrs: false,
  
  // 自定義選項(xiàng)
  customOption: 'value'
}
</script>

<script setup>
// Composition API
const props = defineProps({/* ... */})
</script>

4.使用限制

// 不能用在普通 <script> 中
<script>
// ? 錯(cuò)誤:不能在普通 script 中使用
defineProps({})  // 編譯錯(cuò)誤
</script>

// 不能動(dòng)態(tài)生成
const propName = 'title'
// ? 錯(cuò)誤:參數(shù)必須是編譯時(shí)常量
defineProps({ [propName]: String })

5.最佳實(shí)踐總結(jié)

最佳實(shí)踐
defineProps1. 始終添加類型驗(yàn)證
2. 為可選屬性設(shè)置默認(rèn)值
3. 使用 TypeScript 泛型獲得更好類型安全
4. 避免在子組件中修改 props
defineEmits1. 事件名使用 kebab-case
2. 為復(fù)雜事件添加驗(yàn)證函數(shù)
3. 使用 TypeScript 定義完整事件簽名
4. 傳遞最小必要數(shù)據(jù)
defineExpose1. 僅暴露必要的 API
2. 為暴露的 API 添加 TypeScript 接口
3. 避免暴露內(nèi)部狀態(tài)
4. 提供清晰的公共方法名

常見問題解答

Q1: 為什么需要.value訪問 props?

const props = defineProps({ count: Number })

// ? 錯(cuò)誤:props 本身不是 ref
props.count.value  // undefined

// ? 正確:直接訪問
console.log(props.count)

// ? 如果需要 ref,使用 toRef
import { toRef } from 'vue'
const countRef = toRef(props, 'count')

Q2: 如何訪問未定義的 props?

const props = defineProps({ definedProp: String })

// 安全訪問未定義的 prop
import { toRef } from 'vue'
const optionalProp = toRef(props, 'optionalProp')  // 返回一個(gè) ref,即使 prop 未定義

Q3: defineExpose 會(huì)暴露所有內(nèi)容嗎?

// 不會(huì)!defineExpose 是選擇性的
const publicData = ref('public')
const privateData = ref('private')

defineExpose({ publicData })
// 只有 publicData 被暴露,privateData 保持私有

Q4: 可以在組合式函數(shù)中使用這些宏嗎?

// ? 錯(cuò)誤:不能在組合式函數(shù)中使用
export function useFeature() {
  defineProps({})  // 編譯錯(cuò)誤
  return {}
}

// ? 正確:只能在組件頂層使用

這三個(gè)編譯器宏是 Vue 3 <script setup> 的核心,它們讓組件定義更加簡潔、類型安全,同時(shí)保持了良好的封裝性。掌握它們的使用是高效開發(fā) Vue 3 應(yīng)用的關(guān)鍵。

總結(jié)

到此這篇關(guān)于Vue中defineProps、defineEmits、defineExpose使用的文章就介紹到這了,更多相關(guān)Vue defineProps、defineEmits、defineExpose內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 如何使用el-table+el-tree+el-select動(dòng)態(tài)選擇對(duì)應(yīng)值

    如何使用el-table+el-tree+el-select動(dòng)態(tài)選擇對(duì)應(yīng)值

    小編在做需求時(shí),遇到了在el-table表格中加入多條數(shù)據(jù),并且每條數(shù)據(jù)要通過el-select來選取相應(yīng)的值,做到動(dòng)態(tài)選擇,下面這篇文章主要給大家介紹了關(guān)于如何使用el-table+el-tree+el-select動(dòng)態(tài)選擇對(duì)應(yīng)值的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • Vue 實(shí)現(xiàn)可視化拖拽頁面編輯器

    Vue 實(shí)現(xiàn)可視化拖拽頁面編輯器

    這篇文章主要介紹了Vue 實(shí)現(xiàn)可視化拖拽頁面編輯器的方法,幫助大家更好的理解和使用vue,感興趣的朋友可以了解下
    2021-02-02
  • Vue表單數(shù)據(jù)修改與刪除功能實(shí)現(xiàn)

    Vue表單數(shù)據(jù)修改與刪除功能實(shí)現(xiàn)

    本文通過實(shí)例代碼介紹了Vue表單數(shù)據(jù)修改與刪除功能實(shí)現(xiàn),結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友跟隨小編一起看看吧
    2023-10-10
  • vue中如何創(chuàng)建多個(gè)ueditor實(shí)例教程

    vue中如何創(chuàng)建多個(gè)ueditor實(shí)例教程

    這篇文章主要給大家介紹了關(guān)于vue中如何創(chuàng)建多個(gè)ueditor的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-11-11
  • 深入理解Vue.js3中Reactive的實(shí)現(xiàn)

    深入理解Vue.js3中Reactive的實(shí)現(xiàn)

    reactive是Vue 3的Composition API中的一個(gè)函數(shù),它允許你創(chuàng)建一個(gè)響應(yīng)式的數(shù)據(jù)對(duì)象,本文主要介紹了深入理解Vue.js3中Reactive的實(shí)現(xiàn),感興趣的可以了解一下
    2024-01-01
  • vue如何調(diào)用攝像頭實(shí)現(xiàn)拍照上傳圖片、本地上傳圖片

    vue如何調(diào)用攝像頭實(shí)現(xiàn)拍照上傳圖片、本地上傳圖片

    這篇文章主要給大家介紹了關(guān)于vue如何調(diào)用攝像頭實(shí)現(xiàn)拍照上傳圖片、本地上傳圖片的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-07-07
  • vue使用AES.js的步驟詳解

    vue使用AES.js的步驟詳解

    AES對(duì)數(shù)據(jù)傳輸加密、解密處理---AES.js,下面分步驟給大家介紹vue使用AES.js的示例代碼,感興趣的朋友跟隨小編一起看看吧
    2021-10-10
  • vue項(xiàng)目中應(yīng)用ueditor自定義上傳按鈕功能

    vue項(xiàng)目中應(yīng)用ueditor自定義上傳按鈕功能

    這篇文章主要介紹了vue項(xiàng)目中應(yīng)用ueditor自定義上傳按鈕功能,文中以vue-cli生成的項(xiàng)目為例給大家介紹了vue項(xiàng)目中使用ueditor的方法,感興趣的朋友跟隨腳本之家小編一起學(xué)習(xí)吧
    2018-04-04
  • Vue中的CRUD使用及說明

    Vue中的CRUD使用及說明

    在Vue中進(jìn)行CRUD前端跨域操作時(shí),可以通過后端設(shè)置跨域來解決,通常有三種方法:使用`@CrossOrigin`注解、在`mvc`的XML配置文件中設(shè)置以及自定義類進(jìn)行配置,作者分享了這三種方法,并希望大家參考和使用
    2026-03-03
  • Vite創(chuàng)建項(xiàng)目的實(shí)現(xiàn)步驟

    Vite創(chuàng)建項(xiàng)目的實(shí)現(xiàn)步驟

    隨著 Vite2 的發(fā)布并日趨穩(wěn)定,現(xiàn)在越來越多的項(xiàng)目開始嘗試使用它。本文我們就介紹了Vite創(chuàng)建項(xiàng)目的實(shí)現(xiàn)步驟,感興趣的可以了解一下
    2021-07-07

最新評(píng)論

栖霞市| 会同县| 洮南市| 卓资县| 嘉定区| 格尔木市| 北安市| 漳平市| 菏泽市| 富裕县| 枝江市| 余干县| 兴海县| 漠河县| 莫力| 西乌珠穆沁旗| 金堂县| 宜兴市| 新余市| 玛曲县| 长治县| 桂林市| 慈溪市| 吉木萨尔县| 锦屏县| 大庆市| 连云港市| 华池县| 宁武县| 莲花县| 阜新市| 上杭县| 东城区| 延安市| 乌海市| 平遥县| 响水县| 江达县| 石首市| 宣化县| 政和县|