作为一名Vue开发者,最近开始系统学习React。为了找到感觉,我决定把以前用Vue写的一个经典小组件------B站风格评论列表,用React再实现一次。
案例示范!

源码奉上
js
import { useState } from 'react'
import './commentList.css'
function CommentList() {
// 模拟评论数据
const [comments, setComments] = useState([
{ id: 1, name: '张三', content: '今天天气真好!', date: '2024-06-01', likes: 5 },
{ id: 2, name: '李四', content: '学习React很有趣!', date: '2024-06-02', likes: 3 },
{ id: 3, name: '王五', content: '我喜欢编程!', date: '2024-06-03', likes: 8 }
])
// 新增评论的状态
const [newComment, setNewComment] = useState('');
// 输入框内容变化的处理函数
const commentInput = (e: any) => {
setNewComment(e.target.value);
}
// 发布评论的处理函数
const submitComment = () => {
if (newComment.trim() === '') {
alert('评论内容不能为空');
return;
}
setComments([...comments, {
id: comments.length + 1,
name: '新用户',
content: newComment,
// 获取当前日期,格式为YYYY-MM-DD
date: new Date().toISOString().split('T')[0],
likes: 0
}]);
setNewComment('');
}
// 删除评论的处理函数
const deleteComment = (id: number) => {
setComments(comments.filter(comment => comment.id !== id));
}
// 点赞评论的处理函数
const lickClick = (comment: any) => {
const updatedComments = comments.map(c => {
if (c.id === comment.id) {
return { ...c, likes: c.likes + 1 };
}
return c;
});
setComments(updatedComments);
}
return (
<>
<div className="container">
<h1>B站评论案例</h1>
<div className="header">
<div className="h_left">
<span className="h_title">评论</span>
<span className="comment_num">{comments.length}</span>
</div>
<div className="h_right">
<span className='tab'>最新</span>
<span>|</span>
<span className='tab'>最热</span>
</div>
</div>
<div className="main">
<div className="m_head">
<input type="text" placeholder="发一条善意的评论" value={newComment} onInput={(e) => commentInput(e)}/>
<button onClick={() => submitComment()}>发布</button>
</div>
{
comments.map(comment => (
<div key={comment.id} className="comment">
<div className="info">
<span className="name">{comment.name}</span>
<span className="date">{comment.date}</span>
</div>
<div className="content">{comment.content}</div>
<div className="actions">
<span className="like" onClick={() => lickClick(comment)}>👍 {comment.likes}</span>
<span className="delete" onClick={()=> deleteComment(comment.id)}>删除</span>
</div>
</div>
))
}
</div>
</div>
</>
)
}
export default CommentList
注意
1. 状态管理:响应式系统 vs 不可变状态
这是最大的思维转换点。
-
在Vue中 ,我定义一个
data()函数或使用ref、reactive。Vue通过其响应式系统 ,自动追踪我的依赖。当我在方法里直接修改this.comments或comments.value时,视图会自动更新。javascript
ini// Vue 3 Composition API 写法 const comments = ref([...]); const deleteComment = (id) => { comments.value = comments.value.filter(c => c.id !== id); }; -
在React中 ,我深刻体会到了 "不可变性" 的严格。我必须使用
setComments这个"设置函数",并且永远不能直接修改旧的comments状态 。每次更新都必须返回一个全新的状态。 javascriptini// React 写法 const deleteComment = (id) => { // 错误!直接修改原状态 // comments.splice(index, 1); // 正确!创建并设置一个过滤后的新数组 setComments(comments.filter(comment => comment.id !== id)); };React通过比较状态引用的地址来决定是否重渲染。这种模式强制我思考数据的完整变更路径,虽然开始时有点繁琐,但让数据流变得非常清晰、可预测。
2. 事件绑定:模板指令 vs 原生属性
在模板中绑定事件,两者方式截然不同。
-
Vue 使用自定义的指令,如
@click,语法上是声明式的增强。vue<button @click="submitComment">发布</button> -
React 则更接近原生JS,使用驼峰命名的事件属性 ,如
onClick。jsx<button onClick={submitComment}>发布</button> <span onClick={() => deleteComment(comment.id)}>删除</span>注意第二个例子,为了传递参数,我需要创建一个箭头函数。这在Vue中是不需要的。
3. 视图与逻辑:单文件组件 vs 函数即组件
这是组织代码方式的根本不同。
- Vue 推崇 "关注点分离" ,一个
.vue单文件组件清晰地将<template>、<script>、<style>放在不同的标签块里。 - React 推崇 "关注点聚合" ,组件就是一个JavaScript函数。状态、副作用、事件处理函数、以及返回的JSX,都写在这个函数体内部。相关的UI和逻辑在代码位置上紧邻。
css文件
css
h1 {
color: brown;
}
.container {
width: 600px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ddd;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.container .header {
font-size: 20px;
color: #ccc;
display: flex;
align-items: center;
margin-bottom: 20px;
}
.h_left {
width: 150px;
.h_title {
font-size: 24px;
color: #333;
font-weight: bold;
}
.comment_num {
font-size: 20px;
padding-left: 10px;
}
}
.h_right {
flex: 1;
text-align: center;
display: flex;
gap: 10px;
}
.h_right .tab {
cursor: pointer;
}
.main {
width: 100%;
.m_head {
display: flex;
align-items: center;
margin-bottom: 15px;
.m_avatar {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
}
input {
flex: 1;
height: 30px;
border: 1px solid #ddd;
border-radius: 5px;
padding: 0 10px;
}
button {
margin-left: 10px;
padding: 5px 15px;
background-color: #28a745;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
}
}
.comment {
border-bottom: 1px solid #eee;
padding: 10px 0;
}
.info .name {
font-weight: bold;
margin-right: 10px;
}
.info .date {
color: #999;
font-size: 12px;
}
.actions .like {
color: #007bff;
cursor: pointer;
}
.actions .delete {
margin-left: 20px;
color: #dc3545;
cursor: pointer;
}
.content {
margin-top: 5px;
line-height: 1.5;
}
}
我的CSS文件为什么能"嵌套"?
我立刻意识到:这不是原生CSS。原生CSS不支持这种嵌套语法。
经过排查,我发现这要归功于现代前端构建工具。我用Vite创建的项目,默认集成了 PostCSS 。PostCSS 配合 postcss-nested 插件,会在构建阶段自动将我的嵌套CSS"展平"成浏览器能理解的标准语法: 我写的:
css
.container {
.header { font-size: 20px; }
}
构建后输出的:
css
.container .header { font-size: 20px; }
这给了我几点启发:
- 现代前端开发环境是"增强版"的:我们写的代码(JSX、嵌套CSS)往往不是最终在浏览器中运行的代码,构建工具帮我们做了很多转换和优化。
- 开发体验优先:嵌套CSS让样式结构更清晰,更接近组件的DOM结构,大大提升了编写和维护体验。
- 注意嵌套深度:虽然方便,但要避免嵌套过深(建议不超过3层),否则会产生权重过高的复杂选择器,让后续样式覆盖变得困难。
心得体会
我曾长期觉得 React 上手门槛比 Vue 高,学习曲线不那么友好,加上日常工作中没有使用场景,总觉得学了也容易忘,缺乏持续投入的动力。但留意到越来越多的主流公司和项目都在采用 React,市场对 React 技术栈的需求也更为旺盛,薪资待遇往往略高一筹,让我意识到掌握它或许会带来更广阔的机会。
另一方面,Vue 3.0 推出后,其设计理念和 API 也变得更加灵活和强大------某种程度上说,它也变得"不那么好理解"了。这反而让我觉得,与其在两个框架的复杂度之间纠结,不如直接挑战那个公认更"工程化"、更强调开发者自主控制权的 React。
这次动手重写评论组件的实践给了我一个重要的启示:很多所谓的"难",其实只是"不熟悉" 。当真正动手去写、去调试、去对比两者实现同一功能时的思维差异,抽象的概念会逐渐变得具体。React 严格的"不可变数据"要求、函数组件的清晰逻辑,虽然开始需要适应,但也强迫我养成了更严谨的数据流思考习惯。
我不再把学习看作必须立刻用于生产的任务,而是当成拓宽自己技术视野和解决问题能力的旅程。只要愿意动手去写、去试错、去总结,每一步都会让陌生的东西变得熟悉,让复杂的东西变得清晰。
相信只要保持动手实践,一切都会朝着更好的方向发展。这条路或许并不轻松,但每一步都算数。