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

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

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

前言

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

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

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

defineExpose 則用于暴露組件實例的公共API。

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

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

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

1.defineProps- 定義組件屬性

基本用法

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

// 方式2:對象形式(推薦,支持完整驗證)
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:純類型聲明(運行時無驗證)
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
})

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

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

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

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

2.defineEmits- 定義組件事件

基本用法

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

// 方式2:對象形式(支持驗證)
const emit = defineEmits({
  // 無驗證
  submit: null,
  
  // 帶驗證函數(shù)
  'update:value': (value) => {
    if (typeof value === 'string' && value.length > 0) {
      return true
    }
    console.warn('Invalid value')
    return false
  },
  
  // 多個參數(shù)的驗證
  '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>

多個 v-model(Vue 3.3+)

<!-- UserForm.vue -->
<script setup>
// 多個 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- 暴露組件實例

為什么需要?

默認(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++
}

// 計算屬性
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)  // ? 可以訪問(因為被暴露)
    
    // ? 無法訪問未暴露的
    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>

注意事項和最佳實踐

1.執(zhí)行時機

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

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

2.重復(fù)定義

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

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

3.與 Options API 混用

vue

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

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

4.使用限制

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

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

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

最佳實踐
defineProps1. 始終添加類型驗證
2. 為可選屬性設(shè)置默認(rèn)值
3. 使用 TypeScript 泛型獲得更好類型安全
4. 避免在子組件中修改 props
defineEmits1. 事件名使用 kebab-case
2. 為復(fù)雜事件添加驗證函數(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 })

// ? 錯誤: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')  // 返回一個 ref,即使 prop 未定義

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

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

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

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

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

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

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

總結(jié)

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

相關(guān)文章

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

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

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

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

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

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

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

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

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

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

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

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

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

    vue使用AES.js的步驟詳解

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

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

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

    Vue中的CRUD使用及說明

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

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

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

最新評論

张家口市| 嘉定区| 南澳县| 庄河市| 县级市| 固原市| 泰顺县| 白朗县| 珲春市| 嫩江县| 边坝县| 师宗县| 灵川县| 绍兴市| 习水县| 和平区| 肥城市| 石柱| 延安市| 东兰县| 舒兰市| 彭州市| 镇沅| 红安县| 安平县| 开鲁县| 恩施市| 新巴尔虎右旗| 平山县| 开原市| 荆门市| 内乡县| 兴义市| 会泽县| 云南省| 靖边县| 宝山区| 台州市| 白城市| 沙湾县| 多伦县|