Vue3 ref與props

ref?屬性?與?props

一、核心概念對比

特性ref?(標簽屬性)props
作用對象DOM 元素/組件實例組件間數據傳遞
數據流向父組件訪問子組件/DOM父組件 → 子組件
響應性直接操作對象單向數據流(只讀)
使用場景獲取 DOM/調用子組件方法組件參數傳遞
Vue3 變化不再自動數組形式需要?defineProps?聲明

二、ref?屬性的深度解析

1. 基礎用法
<!-- 獲取 DOM 元素 -->
<template><input ref="inputRef" type="text"><ChildComponent ref="childRef" />
</template><script setup>
import { ref, onMounted } from 'vue'// DOM 引用
const inputRef = ref(null)// 組件引用
const childRef = ref(null)onMounted(() => {inputRef.value.focus() // 操作 DOMchildRef.value.sayHello() // 調用子組件方法
})
</script>
2. 組件引用規范
// 子組件 ChildComponent.vue
<script setup>
// 必須暴露方法才能被父組件調用
defineExpose({sayHello: () => console.log('Hello from child!'),childData: ref('子組件數據')
})
</script>
3. 類型安全(TypeScript)
// 父組件中定義組件引用類型
import ChildComponent from './ChildComponent.vue'const childRef = ref<InstanceType<typeof ChildComponent>>()

三、props?的響應式處理

1. 基礎聲明
<!-- 父組件 -->
<ChildComponent :title="parentTitle" /><!-- 子組件 -->
<script setup>
const props = defineProps({title: {type: String,required: true}
})
</script>
2. 保持響應性
// 正確方式:使用 toRef
import { toRef } from 'vue'const title = toRef(props, 'title')// 錯誤!直接解構會失去響應性
// const { title } = props

四、聯合使用場景

場景:表單驗證組件
<!-- 父組件 -->
<template><ValidationForm ref="formRef" :rules="validationRules"@submit="handleSubmit"/>
</template><script setup>
const formRef = ref(null)
const validationRules = ref({/* 驗證規則 */})// 調用子組件方法
const validateForm = () => {formRef.value.validate()
}
</script>
<!-- 子組件 ValidationForm.vue -->
<script setup>
defineProps(['rules'])const validate = () => {// 執行驗證邏輯
}// 暴露方法給父組件
defineExpose({ validate })
</script>

五、核心差異總結

對比維度ref?(標簽屬性)props
數據方向父 → 子(操作子組件/DOM)父 → 子(數據傳遞)
可修改性可直接修改子組件暴露的內容只讀(需通過事件通知父組件修改)
響應式機制直接引用對象需要?toRef?保持響應性
典型應用DOM操作/調用子組件方法組件參數配置
組合式 API通過?ref()?創建引用通過?defineProps?聲明

六、最佳實踐指南

1.?ref?使用原則
  • 最小化暴露:只暴露必要的組件方法/數據

  • 避免直接修改:不要通過?ref?直接修改子組件狀態

  • 配合 TypeScript:使用類型定義確保安全訪問

2.?props?使用規范
  • 只讀原則:始終視?props?為不可變數據

  • 響應式轉換:使用?toRef?處理需要響應式的?props

  • 明確驗證:始終定義?props?的類型驗證


七、常見問題解決

Q1: 為什么通過?ref?訪問子組件屬性得到?undefined

原因:子組件未通過?defineExpose?暴露屬性
解決方案

// 子組件
defineExpose({publicMethod: () => {/* ... */}
})
Q2: 如何同時使用?ref?和?v-for
<template><ChildComponent v-for="item in list" :key="item.id":ref="setItemRef"/>
</template><script setup>
const itemRefs = ref([])const setItemRef = el => {if (el) itemRefs.value.push(el)
}
</script>
Q3: 如何類型安全地組合?ref?和?props
// 父組件
import ChildComponent from './ChildComponent.vue'type ChildComponentExpose = {validate: () => boolean
}const childRef = ref<ChildComponentExpose>()// 子組件
defineExpose<ChildComponentExpose>({validate: () => true
})

八、綜合應用示例

父組件

<template><UserFormref="userForm":user-data="formData"@submit="handleSubmit"/><button @click="validateForm">驗證表單</button>
</template><script setup lang="ts">
import { ref } from 'vue'
import UserForm from './UserForm.vue'type UserFormExpose = {validate: () => booleanresetForm: () => void
}const userForm = ref<UserFormExpose>()
const formData = ref({ name: '', email: '' })const validateForm = () => {if (userForm.value?.validate()) {console.log('表單驗證通過')}
}const handleSubmit = (data) => {console.log('提交數據:', data)
}
</script>

子組件 UserForm.vue

<template><form @submit.prevent="submitForm"><input v-model="localData.name"><input v-model="localData.email"><button type="submit">提交</button></form>
</template><script setup lang="ts">
import { ref, toRefs } from 'vue'const props = defineProps<{userData: {name: stringemail: string}
}>()const emit = defineEmits(['submit'])// 本地副本(避免直接修改 props)
const localData = ref({ ...props.userData })const validate = () => {return localData.value.name.length > 0 && localData.value.email.includes('@')
}const resetForm = () => {localData.value = { name: '', email: '' }
}const submitForm = () => {emit('submit', localData.value)
}defineExpose({validate,resetForm
})
</script>

關鍵總結:

  1. ref?屬性:用于直接訪問 DOM/子組件實例,需要配合?defineExpose?使用

  2. props:用于父組件向子組件傳遞數據,需保持只讀特性

  3. 協作模式

    • 父組件通過?props?傳遞數據

    • 子組件通過事件通知父組件

    • 必要時通過?ref?調用子組件方法

  4. 類型安全:使用 TypeScript 類型定義確保可靠訪問


事件傳遞

在 Vue3 中,子組件向父組件傳遞數據主要通過?事件機制?實現。以下是 5 種主要實現方式及其使用場景:


一、基礎事件傳遞 (推薦)

實現方式
子組件觸發自定義事件 → 父組件監聽事件

<!-- 子組件 ChildComponent.vue -->
<script setup>
const emit = defineEmits(['sendData']) // 聲明事件const sendToParent = () => {emit('sendData', { message: 'Hello from child!' }) // 觸發事件
}
</script><template><button @click="sendToParent">發送數據</button>
</template>
<!-- 父組件 ParentComponent.vue -->
<template><ChildComponent @send-data="handleData" />
</template><script setup>
const handleData = (payload) => {console.log(payload.message) // 輸出:Hello from child!
}
</script>

最佳實踐

  • 使用?kebab-case?事件名(如?send-data

  • 通過 TypeScript 定義事件類型:

    const emit = defineEmits<{(e: 'sendData', payload: { message: string }): void
    }>()

二、v-model 雙向綁定 (表單場景推薦)

實現原理
v-model?是?:modelValue?+?@update:modelValue?的語法糖

<!-- 子組件 InputComponent.vue -->
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])const updateValue = (e) => {emit('update:modelValue', e.target.value)
}
</script><template><input :value="modelValue" @input="updateValue">
</template>
<!-- 父組件 -->
<template><InputComponent v-model="inputValue" />
</template><script setup>
const inputValue = ref('')
</script>

多 v-model 綁定

<ChildComponent v-model:name="userName"v-model:age="userAge"
/>

三、異步回調模式 (需要返回值時)

適用場景:需要等待父組件處理結果的異步操作

<!-- 子組件 -->
<script setup>
const emit = defineEmits(['request'])const handleClick = async () => {const response = await emit('request', { id: 123 })console.log('父組件返回:', response)
}
</script>
<!-- 父組件 -->
<template><ChildComponent @request="handleRequest" />
</template><script setup>
const handleRequest = async (payload) => {const data = await fetchData(payload.id)return data // 返回給子組件
}
</script>

四、Expose 方法調用 (需要直接訪問子組件)

<!-- 子組件 -->
<script setup>
const childData = ref('子組件數據')defineExpose({getData: () => childData.value,updateData: (newVal) => childData.value = newVal
})
</script>
<!-- 父組件 -->
<template><ChildComponent ref="childRef" />
</template><script setup>
const childRef = ref(null)const getChildData = () => {console.log(childRef.value?.getData()) // 輸出:子組件數據childRef.value?.updateData('新數據')
}
</script>

五、狀態管理方案 (跨組件通信)

適用場景:多層嵌套組件或兄弟組件通信

// store/counter.js
import { reactive } from 'vue'export const counterStore = reactive({count: 0,increment() {this.count++}
})
<!-- 子組件 -->
<script setup>
import { counterStore } from './store/counter'const updateCount = () => {counterStore.increment()
}
</script>
<!-- 父組件 -->
<script setup>
import { counterStore } from './store/counter'
</script><template>當前計數:{{ counterStore.count }}
</template>

方法對比表

方法適用場景優點缺點
基礎事件傳遞簡單數據傳遞直觀明確多層嵌套時需逐層傳遞
v-model 綁定表單輸入組件語法簡潔僅適用簡單雙向綁定
異步回調模式需要父組件響應結果支持異步交互邏輯復雜度稍高
Expose 方法需要直接操作子組件靈活性強破壞組件封裝性
狀態管理跨組件/復雜場景解耦組件關系需要額外學習成本

最佳實踐指南

  1. 優先使用事件傳遞:保持組件獨立性

  2. 復雜場景用狀態管理:Pinia 是 Vue3 官方推薦方案

  3. v-model 用于表單:保持雙向綁定的簡潔性

  4. 避免濫用 ref:防止組件過度耦合

  5. TypeScript 類型定義

    // 事件類型定義
    defineEmits<{(e: 'updateData', payload: { id: number }): void(e: 'cancel'): void
    }>()// Props 類型定義
    defineProps<{userId: numberuserName: string
    }>()

完整示例:購物車組件交互

<template><div class="cart-item"><span>{{ item.name }}</span><input type="number" :value="item.quantity"@input="updateQuantity($event.target.value)"><button @click="emit('remove', item.id)">刪除</button></div>
</template><!-- 子組件 CartItem.vue -->
<script setup>
const props = defineProps({item: {type: Object,required: true}
})const emit = defineEmits(['update:quantity', 'remove'])const updateQuantity = (newQty) => {emit('update:quantity', {id: props.item.id, qty: newQty})
}
</script>
<template><CartItem v-for="item in cartItems":key="item.id":item="item"@update:quantity="handleUpdate"@remove="handleRemove"/>
</template><!-- 父組件 ShoppingCart.vue -->
<script setup>
const cartItems = ref([{ id: 1, name: '商品A', quantity: 2 },{ id: 2, name: '商品B', quantity: 1 }
])const handleUpdate = ({ id, qty }) => {const item = cartItems.value.find(i => i.id === id)if (item) item.quantity = Number(qty)
}const handleRemove = (id) => {cartItems.value = cartItems.value.filter(i => i.id !== id)
}
</script>

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/77025.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/77025.shtml
英文地址,請注明出處:http://en.pswp.cn/web/77025.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

視頻匯聚平臺EasyCVR賦能高清網絡攝像機:打造高性價比視頻監控系統

在現代視頻監控系統中&#xff0c;高清網絡攝像機作為核心設備&#xff0c;其性能和配置直接影響監控效果和整體系統的價值。本文將結合EasyCVR視頻監控的功能&#xff0c;探討如何在滿足使用需求的同時&#xff0c;優化監控系統的設計&#xff0c;降低項目成本&#xff0c;并提…

【C++】 —— 筆試刷題day_21

一、愛麗絲的人偶 題目解析 現在存在n個玩偶&#xff0c;每個玩偶的身高是1、2、3......n&#xff1b; 現在我們要對這些玩偶進行排序&#xff08;如果x人偶&#xff0c;它左右兩邊的玩偶一個比x高、一個比x矮&#xff0c;那這個玩偶就會爆炸&#xff09;。 我們不想要任何一個…

詳解.vscode 下的json .vscode文件夾下各個文件的作用

1.背景 看一些開源項目的時候,總是看到vscode先有不同的json文件,再次做一下總結方便之后查看 settings.json肯定不用多說了 vscode 編輯器分為 全局用戶配置 和 當前工作區配置 那么.vscode文件夾下的settings.json文件夾肯定就是當前工作區配置了 在此文件對單個的項目進行配…

手動實現legend 與 echarts圖交互 通過js事件實現圖標某項的高亮 顯示與隱藏

通過html實現legend的樣式 提供調用echarts的api實現與echarts圖表交互的效果 實現餅圖element實現類似于legend與echartstu表交互效果 效果圖 配置代碼 <template><div style"height: 400px; width: 500px;background-color: #CCC;"><v-chart:opti…

Spring Boot 配置源詳解(完整版)

Spring Boot 配置源詳解&#xff08;完整版&#xff09; 一、配置源加載順序與優先級 配置源類型優先級順序&#xff08;從高到低&#xff09;對應配置類/接口是否可覆蓋典型文件/來源命令行參數&#xff08;--keyvalue&#xff09;1&#xff08;最高&#xff09;SimpleComman…

【無人機】無人機遙控器設置與校準,飛行模式的選擇,無線電控制 (RC) 設置

目錄 1、遙控器校準 1.1、校準步驟 2、飛行模式選擇&#xff0c;遙控器通道映射 2.1、配置步驟 1、遙控器校準 在校準無線電系統之前&#xff0c;必須連接/綁定接收器和發射器。綁定發射器和接收器對的過程是特定于硬件的&#xff08;有關說明&#xff0c;請參閱 RC 手冊&…

Redis 有序集合 ZSet 深度解析教程

Redis-ZSet 引言一、 ZSet 核心概念與特性1.1 什么是 ZSet&#xff1f;1.2 ZSet 與 Set、List 的本質區別 二、 ZSet 典型應用場景2.1 排行榜 (Leaderboards)2.2 帶權重的任務隊列 / 延遲隊列2.3 時間軸 (Timeline)2.4 范圍查找 三、 ZSet 底層實現3.1 ziplist (壓縮列表)3.2 s…

【SpringBoot】HttpServletRequest獲取使用及失效問題(包含@Async異步執行方案)

目錄 1. 在 Controller 方法中作為參數注入 2.使用 RequestContextHolder &#xff08;1&#xff09;失效問題 &#xff08;2&#xff09;解決方案一&#xff1a; &#xff08;3&#xff09;解決方案二&#xff1a; 3、使用AutoWrite自動注入HttpServletRequest 跨線程調…

mfc學習(一)

mfc為微軟創建的一個類qt框架的客戶端程序&#xff0c;只不過因為微軟目前有自己 的親身兒子C#&#xff08;.net&#xff09;,所以到2010沒有進行維護。然后一些的工業企業還在繼續進行維護相關的內容。我目前就接手一個現在這樣的項目&#xff0c;其實本質與qt的思路是差不多的…

HarmonyOS:一多能力介紹:一次開發,多端部署

概述 如果一個應用需要在多個設備上提供同樣的內容&#xff0c;則需要適配不同的屏幕尺寸和硬件&#xff0c;開發成本較高。HarmonyOS 系統面向多終端提供了“一次開發&#xff0c;多端部署”&#xff08;后文中簡稱為“一多”&#xff09;的能力&#xff0c;可以基于一種設計…

秒出PPT推出更強版本,AI PPT工具進入新紀元!

在現代職場中&#xff0c;PPT是我們溝通和展示信息的重要工具。無論是做產品演示&#xff0c;還是準備工作匯報&#xff0c;一份精美的PPT能大大提升演示效果。然而&#xff0c;傳統的PPT制作往往需要消耗大量時間&#xff0c;尤其是在排版、設計和內容調整上。如今&#xff0c…

Godot開發2D冒險游戲——第二節:主角光環整起來!

變量的作用域 全局變量&#xff0c;局部變量&#xff0c;導出變量&#xff08;可以在檢查器當中快速查看&#xff09; 為玩家添加移動動畫 現在游戲的玩家還只是在滑行&#xff0c;我們需要再添加玩家每個方向上的移動效果 刪除原先的Item節點&#xff0c;創建一個動畫精靈…

顛覆傳統NAS體驗:耘想WinNAS讓遠程存儲如同本地般便捷

在當今數據爆炸的時代&#xff0c;網絡附加存儲(NAS)已成為許多企業和個人用戶的必備設備。然而&#xff0c;傳統硬件NAS解決方案存在諸多限制&#xff0c;如高額成本、復雜設置和有限的遠程訪問能力。耘想WinNAS以其創新的軟件解決方案&#xff0c;徹底改變了這一局面&#xf…

新市場環境下新能源汽車電流傳感技術發展前瞻

新能源革命重構產業格局 在全球碳中和戰略驅動下&#xff0c;新能源汽車產業正經歷結構性變革。國際清潔交通委員會&#xff08;ICCT&#xff09;最新報告顯示&#xff0c;2023年全球新能源汽車滲透率突破18%&#xff0c;中國市場以42%的市占率持續領跑。這種產業變革正沿著&q…

STM32之DHT11溫濕度傳感器---附代碼

DHT11簡介 DHT11的供電電壓為 3&#xff0d;5.5V。 傳感器上電后&#xff0c;要等待 1s 以越過不穩定狀態在此期間無需發送任何指令。 電源引腳&#xff08;VDD&#xff0c;GND&#xff09;之間可增加一個100nF 的電容&#xff0c;用以去耦濾波。 DATA 用于微處理器與DHT11之間…

#define STEUER_A_H {PWM_A_ON}

目錄 一、括號的區別 二、實例講解 三、注意事項 四、總結 五、補充 一、括號的區別 大括號 {}: 在 C/C 中&#xff0c;大括號一般用于表示一個代碼塊或結構體、集合等。例如&#xff1a; 用于定義函數體、控制結構&#xff08;如 if、for&#xff09;的代碼塊。用于初始化…

Redis 緩存—處理高并發問題

Redis的布隆過濾器、單線程架構、雙寫一致性、比較穿透、擊穿及雪崩、緩存更新方案及分布式鎖。 1 布隆過濾器 是一種高效的概率型數據結構&#xff0c;用于判斷元素是否存在。主要用于防止緩存穿透&#xff0c;通過攔截不存在的數據查詢&#xff0c;避免擊穿數據庫。 原理&…

【玩轉全棧】—— 無敵前端究極動態組件庫--Inspira UI

目錄 Inspira UI 介紹 配置環境 使用示例 效果&#xff1a; Inspira UI 學習視頻&#xff1a; 華麗優雅 | Inspira UI快速上手_嗶哩嗶哩_bilibili 官網&#xff1a;https://inspira-ui.com/ Inspira UI 介紹 Inspira UI 是一個設計精美、功能豐富的用戶界面庫&#xff0c;專為…

【OpenCV圖像處理實戰】從基礎操作到工業級應用

目錄 前言技術背景與價值當前技術痛點解決方案概述目標讀者說明 一、技術原理剖析核心概念圖解核心作用講解關鍵技術模塊說明技術選型對比 二、實戰演示環境配置要求核心代碼實現&#xff08;6個案例&#xff09;案例1&#xff1a;圖像基本操作案例2&#xff1a;邊緣檢測案例3&…

fastjson使用parseObject轉換成JSONObject出現將字符特殊字符解析解決

現象&#xff1a;將字符串的${TARGET_VALUE}轉換成NULL字符串了問題代碼&#xff1a; import com.alibaba.fastjson.JSON;JSONObject config JSON.parseObject(o.toString()); 解決方法&#xff1a; 1.更換fastjson版本 import com.alibaba.fastjson2.JSON;或者使用其他JS…