我是一个vue开发者,然后最近公司,有一个新的桌面端项目使用的技术栈有react,所以最近就开始学了一些。 本文主要是写一点vue中的常用功能在react应该如何去写。
项目结构与入口文件对比
Vue3的项目入口
在Vue3中,项目的入口一般是main.js文件,在这里我们创建Vue应用实例并挂载到DOM元素上:
// Vue3 的 main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
createApp(App)
.use(router)
.use(store)
.mount('#app')
React的项目入口
而在React中,入口文件通常是index.js(或index.tsx如果使用TypeScript),结构如下:
// React的 index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import store from './store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>
);
React的index.js相当于Vue的main.js,都是应用的启动点,但React使用了不同的API进行DOM渲染。单从这个看起来貌似React使用起来好像更加的复杂一点(可能是我看vue看习惯了,哈哈)
React的"三件套"vs Vue的单文件组件
Vue3的单文件组件(.vue)
Vue使用单文件组件(SFC)将HTML、CSS和JavaScript封装在一个.vue文件中:
<!-- Vue3的组件结构 -->
<template>
<div class="greeting">{{ message }}</div>
</template>
<script setup>
import { ref } from 'vue';
const message = ref('Hello Vue!');
</script>
<style scoped>
.greeting {
color: blue;
}
</style>
React的组件结构
React则通常将组件定义在.js或.jsx文件中,并使用JSX将HTML结构嵌入到JavaScript中:
// React的组件结构
import React, { useState } from 'react';
import './Greeting.css'; // 导入CSS文件
function Greeting() {
const [message, setMessage] = useState('Hello React!');
return (
<div className="greeting">{message}</div>
);
}
export default Greeting;
CSS通常分离到单独的文件中,或使用CSS-in-JS解决方案(这个还没有研究,只是知道有这个东西):
/* Greeting.css */
.greeting {
color: blue;
}
Vue的写法更贴近传统前端开发,将HTML、CSS和JavaScript以类似我们最初学习web开发时的方式组织在一起。而React采用的组件化思路则将这三者分离处理,特别是通过JSX将HTML结构直接融入JavaScript中,好在我有点点jsx基础,不然还不好搞。
生命周期对比
Vue3的组合式API生命周期
Vue3中使用组合式API管理生命周期:
import { onMounted, onUpdated, onUnmounted, onBeforeUnmount } from 'vue';
export default {
setup() {
onMounted(() => {
console.log('组件已挂载');
});
onUpdated(() => {
console.log('组件已更新');
});
onBeforeUnmount(() => {
console.log('组件即将卸载');
});
onUnmounted(() => {
console.log('组件已卸载');
});
}
};
React的生命周期
React的函数组件使用useEffect钩子来模拟生命周期行为:
import React, { useEffect, useState } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
// 相当于componentDidMount和componentDidUpdate
useEffect(() => {
console.log('组件已挂载或更新');
// 返回的函数相当于componentWillUnmount
return () => {
console.log('组件即将卸载或更新前的清理');
};
});
// 仅在挂载和卸载时执行(空依赖数组)
useEffect(() => {
console.log('组件已挂载');
return () => {
console.log('组件即将卸载');
};
}, []);
// 仅在count变化时执行
useEffect(() => {
console.log('count已更新:', count);
}, [count]);
return <div>Count: {count}</div>;
}
React的useEffect钩子通过不同的依赖项配置,可以灵活地模拟各种生命周期行为,这一点比Vue的生命周期钩子更统一,这个我比较喜欢。
响应式数据处理
Vue3的响应式系统
Vue3使用ref和reactive创建响应式数据:
import { ref, reactive, computed } from 'vue';
// 在setup中
const count = ref(0);
const user = reactive({
name: 'Alice',
age: 25
});
// 修改响应式数据
const increment = () => {
count.value++;
};
const updateUser = () => {
user.age = 26;
user.name = 'Bob';
};
// 计算属性
const doubleCount = computed(() => count.value * 2);
React的响应式状态管理
React使用useState和useReducer钩子来管理组件状态:
import React, { useState, useMemo } from 'react';
function Counter() {
// 创建状态
const [count, setCount] = useState(0);
const [user, setUser] = useState({
name: 'Alice',
age: 25
});
// 修改状态
const increment = () => {
setCount(count + 1);
// 或者使用函数式更新
// setCount(prevCount => prevCount + 1);
};
const updateUser = () => {
// React中对象更新需要创建新对象
setUser({
...user, // 保留原有属性
age: 26,
name: 'Bob'
});
};
// 类似Vue的computed - useMemo
const doubleCount = useMemo(() => count * 2, [count]);
return (
<div>
<p>Count: {count}</p>
<p>Double: {doubleCount}</p>
<p>User: {user.name}, {user.age}</p>
<button onClick={increment}>增加</button>
<button onClick={updateUser}>更新用户</button>
</div>
);
}
React与Vue处理状态的主要区别:
- React使用
setState函数更新状态,而不是直接修改 - 对象和数组更新需要创建新引用(不可变数据模式)
- 没有自动的响应式系统,需要显式指定依赖
- 使用
useMemo作为计算属性的替代方案
对复杂对象的更新尤其需要注意,React强调不可变性,每次更新都是替换而非修改。
计算属性与数据监听
Vue3的计算属性和侦听器
import { ref, computed, watch, watchEffect } from 'vue';
const count = ref(0);
// 计算属性
const doubleCount = computed(() => count.value * 2);
// 侦听器
watch(count, (newValue, oldValue) => {
console.log(`count从${oldValue}变为${newValue}`);
});
// 副作用
watchEffect(() => {
console.log(`当前count: ${count.value}`);
});
React中的等效实现
React使用useMemo实现计算属性,使用useEffect模拟侦听器:
import { useState, useMemo, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// 计算属性 - useMemo
const doubleCount = useMemo(() => count * 2, [count]);
// 侦听器 - useEffect + 特定依赖
useEffect(() => {
console.log(`count变化为: ${count}`);
}, [count]);
// 副作用 - 类似watchEffect
useEffect(() => {
console.log(`当前count: ${count}`);
// 其他依赖于count的逻辑
}, [count]); // 指定依赖项以模拟watchEffect
return (
<div>
<p>Count: {count}</p>
<p>Double: {doubleCount}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
条件渲染与列表
Vue3的实现
使用v-if和v-else进行条件渲染,使用v-for进行列表渲染
<template>
<div v-if="showMessage">欢迎!</div>
<ul>
<li v-for="(item, index) in items" :key="item.id">
{{ index }}: {{ item.text }}
</li>
</ul>
</template>
<script setup>
import { ref } from 'vue';
const showMessage = ref(true);
const items = ref([
{ id: 1, text: '学习Vue' },
{ id: 2, text: '学习React' },
{ id: 3, text: '构建项目' }
]);
</script>
React的实现
这里主要是jsx的写法。
import React, { useState } from 'react';
function ExampleComponent() {
const [showMessage, setShowMessage] = useState(true);
const [items, setItems] = useState([
{ id: 1, text: '学习Vue' },
{ id: 2, text: '学习React' },
{ id: 3, text: '构建项目' }
]);
return (
<div>
{showMessage && <div>欢迎!</div>}
<ul>
{items.map((item, index) => (
<li key={item.id}>
{index}: {item.text}
</li>
))}
</ul>
</div>
);
}
React使用JavaScript的逻辑运算符和数组方法来实现条件渲染和列表渲染,而不是Vue的特殊指令。
表单处理
Vue3的双向绑定
<template>
<input v-model="message" />
<p>Message: {{ message }}</p>
</template>
<script setup>
import { ref } from 'vue';
const message = ref('');
</script>
React中的表单处理
React没有内置的双向绑定,需要手动处理,使用setData:
import React, { useState } from 'react';
function FormExample() {
const [message, setMessage] = useState('');
const handleChange = (e) => {
setMessage(e.target.value);
};
return (
<div>
<input
value={message}
onChange={handleChange}
/>
<p>Message: {message}</p>
</div>
);
}
这个模块,个人感觉使用起来没有vue快捷。而且在这里我还踩了一个坑,就是我使用input的时候,开始使用onChange方法,结果发现得到的值,会显示中文拼音,然后我就换了blur方法,但是这样的话文本内容就不会改变,因为它更新值是需要手动set嘛,然后blur的话只在最后才set,但是输入期间没有set的过程,所以文本框的内容一直不变,最后是通过onchange里面set一次,然后blur的时候再次set,才得到文本框的最终值(我不知道这种写法,是不是标准写法,有大佬懂的,评论区教教我,谢谢啦)。
组件通信
Vue3的props和emit
父组件:
<template>
<ChildComponent
:message="parentMessage"
@update="handleUpdate"
/>
</template>
<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';
const parentMessage = ref('来自父组件的消息');
const handleUpdate = (newValue) => {
console.log('子组件发送事件:', newValue);
};
</script>
子组件:
<template>
<div>
<p>{{ message }}</p>
<button @click="sendToParent">发送事件</button>
</div>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue';
const props = defineProps({
message: String
});
const emit = defineEmits(['update']);
const sendToParent = () => {
emit('update', '子组件的新数据');
};
</script>
React的props和状态提升
父组件:
import React, { useState } from 'react';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const [parentMessage, setParentMessage] = useState('来自父组件的消息');
const handleUpdate = (newValue) => {
console.log('子组件发送事件:', newValue);
};
return (
<ChildComponent
message={parentMessage}
onUpdate={handleUpdate}
/>
);
}
子组件:
import React from 'react';
function ChildComponent({ message, onUpdate }) {
const sendToParent = () => {
onUpdate('子组件的新数据');
};
return (
<div>
<p>{message}</p>
<button onClick={sendToParent}>发送事件</button>
</div>
);
}
export default ChildComponent;
两者在组件通信方面的概念类似,但语法不同。Vue使用:prop和@event语法,而React使用{prop}和回调函数。目前体验上来,感觉react就是,将父组件的函数直接传了过去,子组件使用这个函数,将值带给父组件。
总结对比
| 特性 | Vue3 | React |
|---|---|---|
| 项目入口 | main.js | index.js |
| 组件文件 | 单文件组件(.vue) | JSX(.js/.jsx) + CSS |
| 响应式数据 | ref, reactive | useState, useReducer |
| 计算属性 | computed | useMemo |
| 侦听器 | watch, watchEffect | useEffect |
| 生命周期 | onMounted等钩子 | useEffect |
| 条件渲染 | v-if, v-show | &&, 三元运算符 |
| 列表渲染 | v-for | map() |
| 表单绑定 | v-model | 受控组件 |
| 属性传递 | :prop | prop={value} |
| 事件传递 | @event | onEvent={handler} |