最近用React19写了一个笔记应用,发现React和Vue3 在 事件传递 方式上不一样:
• React :父组件定义方法,作为 props 传递给子组件,子组件在合适的时候调用这些方法(由父组件控制逻辑)
• Vue3:子组件使用 defineEmits 触发事件,父组件监听事件并执行对应的回调函数(由子组件通知父组件)
最近写的笔记应用,我们以这个搜索组件来举例React和Vue的区别
React事件传递方式
在React中,父组件通过props(属性)向子组件传递数据和事件处理函数,使得子组件能够与父组件通信。我们以上面截图的搜索框组件为例,其中父组件传递了多个事件处理函数(onChange、handleSearch、onClearSearch)到子组件。
父组件
tsx
import React, { useState } from 'react'
import SearchBar from './SearchBar'
const ParentComponent = () => {
const [searchQuery, setSearchQuery] = useState('')
// 处理搜索逻辑
const handleSearch = () => {
console.log('搜索:', searchQuery)
}
// 清空搜索框
const onClearSearch = () => {
setSearchQuery('')
}
return (
<SearchBar
value={searchQuery}
onChange={({ target }) => setSearchQuery(target.value)}
handleSearch={handleSearch}
onClearSearch={onClearSearch}
/>
)
}
export default ParentComponent
示例:子组件(SearchBar.tsx),样式用了tailwindcss v4
tsx
import React from 'react'
import { FaMagnifyingGlass } from 'react-icons/fa6'
import { IoMdClose } from 'react-icons/io'
interface SearchBarProps {
value: string
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void
handleSearch: () => void
onClearSearch: () => void
}
const SearchBar: React.FC<SearchBarProps> = ({
value,
onChange,
handleSearch,
onClearSearch,
}) => {
return (
<div className="w-80 flex items-center px-4 bg-slate-100 rounded-md">
<input type="text" placeholder="搜索笔记"
className="w-full text-xs bg-transparent py-[11px] outline-none"
value={value}
onChange={onChange} // 触发父组件的onChange方法
/>
{value && (
<IoMdClose
className="text-xl text-slate-500 cursor-pointer hover:text-black mr-3"
onClick={onClearSearch} // 触发父组件的 handleSearch方法
/>
)}
<FaMagnifyingGlass
className="text-slate-400 cursor-pointer hover:text-black"
onClick={handleSearch} // 触发父组件的handleSearch方法
/>
</div>
)
}
export default SearchBar
事件传递过程分析
- 父组件定义方法
- handleSearch(): 处理搜索逻辑
- onClearSearch(): 清空搜索框
- onChange(): 更新输入框内容
- 父组件通过props把这些方法传递给子组件
tsx
<SearchBar
value={searchQuery}
onChange={({ target }) => setSearchQuery(target.value)}
handleSearch={handleSearch}
onClearSearch={onClearSearch}
/>
- 子组件SearchBar接受props并在事件触发时调用这些方法
- 当输入框内容变化时,触发onChange(event), 更新searchQuery
- 当点击放大镜图标时,调用handleSearch,执行搜索
- 当点击关闭按钮时,调用onClearSearch,清空搜索框
- 为什么要这样做?
- 父组件控制数据状态,子组件只是一个UI组件(受控组件)
- 子组件无需管理数据,它只是接受value和事件处理函数
- 代码更清晰,可复用性更高,不同地方的搜索框可以使用相同的SearchBar组件
- 总结
✅ 父组件定义事件方法,并通过 props 传递给子组件
✅ 子组件调用 props 里的方法 来通知父组件(例如 onChange、handleSearch、onClearSearch)
✅ 父组件负责管理状态,子组件只是一个展示组件
Vue3事件传递方式
在Vue3中,子组件不会调用父组件的方法,而是通过emit事件方式通知父组件,父组件在模板中监听该事件并执行回调。
父组件
vue
<script setup>
import { ref } from 'vue'
import SearchBar from './SearchBar.vue'
const searchQuery = ref('')
// 监听子组件的事件
const handleSearch = () => {
console.log('搜索:', searchQuery.value)
}
const onClearSearch = () => {
searchQuery.value = ''
}
</script>
<template>
<SearchBar
v-model="searchQuery"
@search="handleSearch"
@clear="onClearSearch"
/>
</template>
子组件 SearchBar.vue
vue
<script setup>
import { defineEmits, defineProps } from 'vue'
const props = defineProps({
modelValue: String,
})
const emit = defineEmits(['update:modelValue', 'search', 'clear'])
const handleInput = (event) => {
emit('update:modelValue', event.target.value) // 触发 v-model
}
const handleSearch = () => {
emit('search') // 触发 search 事件
}
const handleClear = () => {
emit('update:modelValue', '') // 触发 v-model 清空输入框
emit('clear') // 触发 clear 事件
}
</script>
<template>
<div class="search-bar">
<input
type="text"
:value="modelValue"
@input="handleInput"
placeholder="搜索笔记"
/>
<button v-if="modelValue" @click="handleClear">清空</button>
<button @click="handleSearch">搜索</button>
</div>
</template>
上面的子组件我们可以用3.4版本的defineModel简化下代码,
vue
<script setup>
import { defineEmits } from 'vue'
const model = defineModel()
const emit = defineEmits(['search', 'clear'])
const handleSearch = () => {
emit('search') // 触发 search 事件
}
const handleClear = () => {
model.value = ''
emit('clear') // 触发 clear 事件
}
</script>
<template>
<div class="search-bar">
<input
type="text"
v-model="model"
placeholder="搜索笔记"
/>
<button v-if="model" @click="handleClear">清空</button>
<button @click="handleSearch">搜索</button>
</div>
</template>
对比项 | React (父传子) | Vue 3 (emit 子传父) |
---|---|---|
事件触发方式 | 子组件调用父组件的 props 方法 | 子组件 emit 事件,父组件监听 |
状态管理方式 | 父组件控制状态,通过 props 传递 | 子组件通知父组件,父组件修改数据 |
v-model 实现 | 需要 useState + onChange | defineProps + defineEmits |
灵活性 | 父组件完全控制,代码可读性更高 | 子组件可独立触发事件,但需要 emit |
总结
-
React 是 "父组件控制子组件"
• 逻辑和状态都在父组件
• 子组件只调用 props 传递的方法,不需要管理状态
-
Vue 3 是 "子组件通知父组件"
• 子组件使用 emit 触发事件
• 父组件监听事件并修改状态(比如 v-model)
两者设计理念不同,但都能够实现组件间的通信,React 更强调单向数据流 ,而 Vue 3 更灵活,支持 v-model 和 emit 来进行双向绑定。