VUE3:封裝一個評論回復組件

之前用React封裝的評論回復組件,里面有三個主要部分:CommentComponent作為主組件,CommentItem處理單個評論項,CommentInput負責輸入框。現在需要將這些轉換為Vue3的組件。

Vue3和React在狀態管理上有所不同,Vue3使用ref和reactive來管理響應式數據,而React用useState。我需要將React的useState轉換為Vue3的ref。比如activeReplyId和comments在React中是使用useState,在Vue3中應該用ref來聲明。

接下來,處理組件結構。在Vue3中,通常使用單文件組件(.vue文件),所以需要將每個部分拆分成單獨的組件。可能需要將CommentItem和CommentInput作為子組件,或者作為主組件的一部分。不過根據用戶的需求,可能需要保持組件的可復用性,所以拆分成單獨的組件是合理的。

然后,Props的處理。在React中使用PropTypes,而Vue3中使用defineProps。需要將原來的PropTypes轉換為Vue3的props定義。例如,initialComments和currentUser作為props傳入,需要在子組件中正確聲明。

遞歸渲染在Vue中如何處理?React中CommentItem遞歸調用自身,Vue中也需要類似的方式。在Vue的單文件組件中,組件可以在模板中直接引用自己,但需要注意組件名稱是否正確注冊。可能需要使用name屬性來允許遞歸調用。

另外,事件的處理。在React中,通過props傳遞回調函數,比如onReply、setActiveReplyId。在Vue中,子組件通過$emit觸發事件,父組件監聽這些事件。所以需要將原來的回調函數轉換為Vue的事件發射機制。例如,當用戶點擊回復按鈕時,CommentItem應該emit一個事件,父組件監聽到后更新activeReplyId。

狀態提升的問題。在React中,activeReplyId和comments狀態提升到主組件,Vue中同樣需要將這些狀態放在父組件中,通過props傳遞給子組件,或者使用provide/inject,但props更直接。這里應該保持狀態在主組件,通過props傳遞下去。

輸入框組件CommentInput需要處理提交和取消。在React中,通過父組件傳遞onSubmit和onCancel。在Vue中,子組件應該emit事件,比如submit和cancel,父組件監聽并處理。

樣式部分,用戶之前提供了CSS,現在需要將這些樣式轉換到Vue的單文件組件中的style標簽內。注意scoped屬性,避免樣式污染。或者用戶可能使用CSS預處理器,但用戶沒有特別說明,所以保持普通CSS。

然后,考慮TypeScript的支持。用戶沒有明確提到,但Vue3支持TS,所以如果需要,可以添加類型定義。不過用戶可能只需要基本的JS實現,所以暫時使用JS。

接下來,實現遞歸評論項。在Vue中,組件需要能夠引用自身,所以需要在組件內部使用name屬性,比如name: ‘CommentItem’,然后在模板中使用來遞歸渲染子評論。

測試一下功能是否完整:多級回復、最大深度限制、顯示回復對象、時間生成、當前用戶處理。確保在Vue中響應式數據更新正確,比如添加回復后,comments數組的更新會觸發視圖重新渲染。

最后,示例數據和用法。在Vue中,父組件傳入initial-comments和current-user作為props,確保props命名在Vue中使用kebab-case。比如initialComments在props中定義為initial-comments。

可能遇到的陷阱:遞歸組件的注冊,確保組件可以在模板中正確引用自己。另外,在Vue中,直接修改props是不允許的,所以所有狀態變更都應該通過父組件的事件來觸發,保持單向數據流。

總結步驟:轉換React組件結構到Vue3的單文件組件,處理響應式狀態,轉換事件處理機制,確保遞歸渲染正常工作,調整樣式,提供使用示例和props驗證。

以下是基于 Vue 3 封裝的評論回復組件實現(使用 Composition API + <script setup> 語法):

<!-- CommentComponent.vue -->
<template><div class="comment-container"><!-- 主評論輸入框 --><CommentInput @submit="addComment" /><!-- 評論列表 --><div class="comments-list"><CommentItemv-for="comment in comments":key="comment.id":comment="comment":depth="0":current-user="currentUser":active-reply-id="activeReplyId"@reply="handleReply"@set-active-reply="setActiveReplyId"/></div></div>
</template><script setup>
import { ref } from 'vue'
import CommentItem from './CommentItem.vue'
import CommentInput from './CommentInput.vue'const props = defineProps({initialComments: {type: Array,default: () => []},currentUser: {type: String,default: '匿名用戶'}
})const comments = ref([...props.initialComments])
const activeReplyId = ref(null)// 添加新評論/回復
const addComment = (content, parentId = null) => {const newComment = {id: Date.now(),author: props.currentUser,content,time: new Date().toLocaleString(),replies: []}if (!parentId) {comments.value.push(newComment)} else {const updateComments = (items) => items.map(item => {if (item.id === parentId) {return {...item,replies: [...item.replies, newComment]}}return {...item,replies: updateComments(item.replies)}})comments.value = updateComments(comments.value)}
}// 處理回復事件
const handleReply = ({ parentId, content }) => {addComment(content, parentId)activeReplyId.value = null
}const setActiveReplyId = (id) => {activeReplyId.value = id
}
</script><style scoped>
/* 樣式與 React 版本保持一致 */
.comment-container {max-width: 800px;margin: 0 auto;padding: 20px;
}
</style>
<!-- CommentItem.vue -->
<template><div class="comment-item":style="{ marginLeft: depth * 32 + 'px' }"><div class="comment-header"><span class="comment-author">{{ comment.author }}</span><span v-if="parentAuthor" class="reply-to">回復 @{{ parentAuthor }}</span><span class="comment-time">{{ comment.time }}</span></div><div class="comment-content">{{ comment.content }}</div><button v-if="canReply"class="reply-btn"@click="setActiveReply(comment.id)">回復</button><!-- 回復輸入框 --><CommentInputv-if="activeReplyId === comment.id"@submit="handleSubmit"@cancel="setActiveReply(null)"/><!-- 遞歸渲染子評論 --><CommentItemv-for="reply in comment.replies":key="reply.id":comment="reply":depth="depth + 1":current-user="currentUser":active-reply-id="activeReplyId":parent-author="comment.author"@reply="$emit('reply', $event)"@set-active-reply="$emit('set-active-reply', $event)"/></div>
</template><script setup>
import { computed } from 'vue'
import CommentInput from './CommentInput.vue'const props = defineProps({comment: Object,depth: Number,currentUser: String,activeReplyId: Number,parentAuthor: String
})const emit = defineEmits(['reply', 'set-active-reply'])const maxDepth = 4
const canReply = computed(() => props.depth < maxDepth)const handleSubmit = (content) => {emit('reply', { parentId: props.comment.id,content })
}const setActiveReply = (id) => {emit('set-active-reply', id)
}
</script><style scoped>
/* 保持與 React 版本相同的樣式 */
.comment-item {margin: 16px 0;padding: 12px;border: 1px solid #eee;border-radius: 4px;
}.comment-header {display: flex;align-items: center;margin-bottom: 8px;color: #666;
}.comment-author {font-weight: bold;margin-right: 8px;
}.reply-to {color: #999;margin-right: 8px;
}.comment-time {font-size: 0.9em;
}.reply-btn {background: none;border: none;color: #1890ff;cursor: pointer;padding: 4px 8px;
}
</style>
<!-- CommentInput.vue -->
<template><div class="comment-input-container"><textareav-model="content"placeholder="輸入評論內容..."rows="3"></textarea><div class="input-buttons"><button @click="handleSubmit">提交</button><button @click="$emit('cancel')">取消</button></div></div>
</template><script setup>
import { ref } from 'vue'const content = ref('')
const emit = defineEmits(['submit', 'cancel'])const handleSubmit = () => {if (content.value.trim()) {emit('submit', content.value)content.value = ''}
}
</script><style scoped>
.comment-input-container {margin: 16px 0;
}.comment-input-container textarea {width: 100%;padding: 8px;margin-bottom: 8px;
}.input-buttons button {margin-right: 8px;padding: 4px 12px;
}
</style>

組件特性說明:

  1. Vue3 實現特點
  • 使用 Composition API + <script setup> 語法
  • 通過 defineProps 定義組件參數
  • 通過 defineEmits 聲明自定義事件
  • 響應式系統使用 refcomputed
  • 樣式使用 scoped CSS
  1. 使用方式
<template><CommentComponent:initial-comments="comments"current-user="當前用戶"/>
</template><script setup>
import CommentComponent from './components/CommentComponent.vue'const comments = [{id: 1,author: '用戶A',content: '第一條評論',time: '2023-08-01 10:00',replies: [{id: 2,author: '用戶B',content: '第一條回復',time: '2023-08-01 10:05'}]}
]
</script>
  1. 與 React 版本的差異處理
  • 狀態管理:用 ref 替代 useState
  • 事件處理:用 emit 替代 props 回調
  • 遞歸組件:通過組件自身名稱實現遞歸
  • 響應式更新:直接操作數組觸發響應式更新
  • 樣式處理:使用 scoped CSS 避免樣式污染
  1. 擴展建議
<!-- 增加點贊功能示例 -->
<template><button class="like-btn":class="{ liked: comment.liked }"@click="toggleLike">{{ comment.likes || 0 }} 👍</button>
</template><script setup>
// 在 CommentItem 中添加
const toggleLike = () => {context.emit('like', props.comment.id)
}
</script>

這個 Vue3 實現保留了 React 版本的核心功能,同時遵循 Vue 的響應式模式和組件通信方式,更適合 Vue 技術棧項目使用。

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

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

相關文章

制作一款打飛機游戲27:精靈編輯器UI

繼續開發我們的編輯器——Sprit Edit。我們已經創建了這個小編輯器&#xff0c;它可以顯示很多精靈&#xff08;sprites&#xff09;&#xff0c;并且我們可以遍歷所有精靈。這真的很棒&#xff0c;我們可以創建新的精靈&#xff0c;這也不錯。但是&#xff0c;唉&#xff0c;我…

k8s(9) — zookeeper集群部署(親和性、污點與容忍測試)

一、部署思路 1、前期設想 zookeeper集群至少需要運行3個pod集群才能夠正常運行&#xff0c;考慮到節點會有故障的風險這個3個pod最好分別運行在&#xff13;個不同的節點上(為了實現這一需要用到親和性和反親和性概念)&#xff0c;在部署的時候對zookeeper運行的pod打標簽加…

WXT+Vue3+sass+antd+vite搭建項目開發chrome插件

WXTVue3sassantdvite搭建項目開發chrome插件 前言一、初始化項目二、項目配置調整三、options頁面配置四、集成antd五、集成sass六、環境配置七、代碼注入 vue3&#xff1a;https://cn.vuejs.org/ axios&#xff1a;https://www.axios-http.cn/docs/api_intro antd&#xff1a;…

JSAPI2.4——正則表達式

一、語法 const str 一二三四五六七八九十 //判斷內容 const reg /二/ //判斷條件 console.log(reg.test(str)); //檢查 二、test與exec方法的區別 test方法&#xff1a;用于判斷是否符合規則的字符串&#xff0c;返回值是布爾值 exec方法&…

燃氣用戶檢修工考試精選題

燃氣用戶檢修工考試精選題&#xff1a; 我國國家標準規定民用天然氣中硫化氫含量最高允許濃度是&#xff08; &#xff09;。 A. 20mg/m B. 15mg/m C. 5mg/m D. 50mg/m 答案&#xff1a;A 城市燃氣應具有可以察覺的臭味&#xff0c;當無毒燃氣泄漏到空氣中&#xff0c;達到爆炸…

【前端】1h 搞定 TypeScript 教程_只說重點

不定期更新&#xff0c;建議關注收藏點贊。 目錄 簡介使用基本類型、類型推斷和類型注解接口、類型別名、聯合類型類與繼承泛型GenericsReact 與 TS 進階高級類型裝飾器Decorators模塊系統TypeScript 編譯選項 簡介 TypeScript&#xff08;簡稱 TS&#xff09;是一種由微軟開發…

MyBatis 參數綁定

一、MyBatis 參數綁定機制 1.1 核心概念 當 Mapper 接口方法接收多個參數時&#xff0c;MyBatis 提供三種參數綁定方式&#xff1a; 默認參數名&#xff1a;arg0、arg1&#xff08;Java 8&#xff09;或 param1、param2Param 注解&#xff1a;顯式指定參數名稱POJO/DTO 對象…

【解決方案】Linux解決CUDA安裝過程中GCC版本不兼容

Linux解決CUDA安裝過程中GCC版本不兼容 目錄 問題描述 解決方法 安裝后配置 問題描述 Linux環境下安裝 CUDA 時&#xff0c;運行sudo sh cuda_10.2.89_440.33.01_linux.run命令出現 “Failed to verify gcc version.” 的報錯&#xff0c;提示 GCC 版本不兼容&#xff0c;查…

人工智能數學基礎(一):人工智能與數學

在人工智能領域&#xff0c;數學是不可或缺的基石。無論是算法的設計、模型的訓練還是結果的評估&#xff0c;都離不開數學的支持。接下來&#xff0c;我將帶大家深入了解人工智能數學基礎&#xff0c;包括微積分、線性代數、概率論、數理統計和最優化理論&#xff0c;并通過 P…

Shell腳本-嵌套循環應用案例

在Shell腳本編程中&#xff0c;嵌套循環是一種強大的工具&#xff0c;可以用于處理復雜的任務和數據結構。通過在一個循環內部再嵌套另一個循環&#xff0c;我們可以實現對多維數組、矩陣操作、文件處理等多種高級功能。本文將通過幾個實際的應用案例來展示如何使用嵌套循環解決…

勘破養生偽常識,開啟科學養生新篇

?在養生潮流風起云涌的當下&#xff0c;各種養生觀點和方法層出不窮。但其中有不少是缺乏科學依據的偽常識&#xff0c;若不加分辨地盲目跟從&#xff0c;不僅難以實現養生目的&#xff0c;還可能損害健康。因此&#xff0c;勘破這些養生偽常識&#xff0c;是邁向科學養生的關…

Nacos-3.0.0適配PostgreSQL數據庫

&#x1f9d1; 博主簡介&#xff1a;CSDN博客專家&#xff0c;歷代文學網&#xff08;PC端可以訪問&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移動端可微信小程序搜索“歷代文學”&#xff09;總架構師&#xff0c;15年工作經驗&#xff0c;精通Java編…

機器學習第三篇 模型評估(交叉驗證)

Sklearn:可以做數據預處理、分類、回歸、聚類&#xff0c;不能做神經網絡。原始的工具包文檔&#xff1a;scikit-learn: machine learning in Python — scikit-learn 1.6.1 documentation數據集:使用的是MNIST手寫數字識別技術&#xff0c;大小為70000&#xff0c;數據類型為7…

如何在 IntelliJ IDEA 中編寫 Speak 程序

在當今數字化時代&#xff0c;語音交互技術越來越受到開發者的關注。如果你想在 IntelliJ IDEA&#xff08;一個強大的集成開發環境&#xff09;中編寫一個語音交互&#xff08;Speak&#xff09;程序&#xff0c;那么本文將為你提供詳細的步驟和指南。 一、環境準備 在開始編…

AI大模型學習十四、白嫖騰訊Cloud Studio AI環境 通過Ollama+Dify+DeepSeek構建生成式 AI 應用-接入DeepSeek大模型

一、說明 需要閱讀 AI大模型學習十三、白嫖騰訊Cloud Studio AI環境 通過OllamaDifyDeepSeek構建生成式 AI 應用-安裝-CSDN博客https://blog.csdn.net/jiangkp/article/details/147580344?spm1011.2415.3001.5331 我們今天干點啥呢&#xff0c;跟著官網走 模型類型 在 Dify…

《Astro 3.0島嶼架構讓內容網站“脫胎換骨”》

內容優先的網站越來越成為主流。無論是新聞資訊、知識博客&#xff0c;還是電商產品展示&#xff0c;用戶都希望能快速獲取所需內容&#xff0c;這對網站的性能和體驗提出了極高要求。而Astro 3.0的島嶼架構&#xff0c;就像是為內容優先網站量身定制的一把神奇鑰匙&#xff0c…

在 UniApp 中實現 App 與 H5 頁面的跳轉及通信

在移動應用開發中&#xff0c;內嵌 H5 頁面或與外部網頁交互是常見需求。UniApp 作為跨平臺框架&#xff0c;提供了靈活的方式實現 App 與 H5 的跳轉和雙向通信。本文將詳細講解實現方法&#xff0c;并提供可直接復用的代碼示例。 文章目錄 一、 App 內嵌 H5 頁面&#xff08;使…

springboot 實現敏感信息脫敏

記錄于2025年4月28號晚上--梧州少帥 1. 定義枚舉類&#xff1a; public enum DesensitizeType {NAME, EMAIL } 2. 創建自定義注解&#xff1a; 用于標記需要脫敏的字段及其類型。 Retention(RetentionPolicy.RUNTIME) JacksonAnnotationsInside JsonSerialize(using Desen…

SNMP協議之詳解(Detailed Explanation of SNMP Protocol)

SNMP協議之詳解 一、前言 SNMP&#xff0c;被形象地喻為網絡世界大的工具箱&#xff0c;使他們能的“智慧守護者”&#xff0c;它為網絡管理員裝備了一套功能強夠實現對網絡設備狀態的實時監控、性能數據的全面收集、遠程配置的靈活管理以及故障事件的即時響應。借助SNMP&…

SpeedyAutoLoot

SpeedyAutoLoot自動拾取插件 SpeedyAutoLoot.lua local AutoLoot CreateFrame(Frame)SpeedyAutoLootDB SpeedyAutoLootDB or {} SpeedyAutoLootDB.global SpeedyAutoLootDB.global or {}local BACKPACK_CONTAINER BACKPACK_CONTAINER local LOOT_SLOT_CURRENCY LOOT_SLOT…