React小demo,评论列表

作为一名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() 函数或使用 refreactive。Vue通过其响应式系统 ,自动追踪我的依赖。当我在方法里直接修改 this.commentscomments.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 状态 。每次更新都必须返回一个全新的状态。 javascript

    ini 复制代码
    // 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; }

这给了我几点启发:

  1. 现代前端开发环境是"增强版"的:我们写的代码(JSX、嵌套CSS)往往不是最终在浏览器中运行的代码,构建工具帮我们做了很多转换和优化。
  2. 开发体验优先:嵌套CSS让样式结构更清晰,更接近组件的DOM结构,大大提升了编写和维护体验。
  3. 注意嵌套深度:虽然方便,但要避免嵌套过深(建议不超过3层),否则会产生权重过高的复杂选择器,让后续样式覆盖变得困难。

心得体会

我曾长期觉得 React 上手门槛比 Vue 高,学习曲线不那么友好,加上日常工作中没有使用场景,总觉得学了也容易忘,缺乏持续投入的动力。但留意到越来越多的主流公司和项目都在采用 React,市场对 React 技术栈的需求也更为旺盛,薪资待遇往往略高一筹,让我意识到掌握它或许会带来更广阔的机会。

另一方面,Vue 3.0 推出后,其设计理念和 API 也变得更加灵活和强大------某种程度上说,它也变得"不那么好理解"了。这反而让我觉得,与其在两个框架的复杂度之间纠结,不如直接挑战那个公认更"工程化"、更强调开发者自主控制权的 React。

这次动手重写评论组件的实践给了我一个重要的启示:很多所谓的"难",其实只是"不熟悉" 。当真正动手去写、去调试、去对比两者实现同一功能时的思维差异,抽象的概念会逐渐变得具体。React 严格的"不可变数据"要求、函数组件的清晰逻辑,虽然开始需要适应,但也强迫我养成了更严谨的数据流思考习惯。

我不再把学习看作必须立刻用于生产的任务,而是当成拓宽自己技术视野和解决问题能力的旅程。只要愿意动手去写、去试错、去总结,每一步都会让陌生的东西变得熟悉,让复杂的东西变得清晰。

相信只要保持动手实践,一切都会朝着更好的方向发展。这条路或许并不轻松,但每一步都算数。

相关推荐
青瓜达利园2 小时前
zustand 入门
前端
triumph_passion2 小时前
Tailwind CSS v4 深度指南:目录架构与主题系统
前端·css
UIUV2 小时前
React表单处理:受控组件与非受控组件全面解析
前端·javascript·react.js
henry2 小时前
React Native 横向滚动指示器组件库(淘宝|京东...&旧版|新版)
前端
一只爱吃糖的小羊2 小时前
JSBridge 传参陷阱:h5明明传了参数,安卓却收到为空
前端·javascript
实习生小黄2 小时前
window.print 实现简单打印
前端·javascript
Wect2 小时前
LeetCode 26.删除有序数组中的重复项:快慢指针的顺势应用
前端·typescript
同学807962 小时前
H5实现网络信号检测全解析(附源码)
前端·javascript
不想秃头的程序员2 小时前
Vue 与 React 数据体系深度对比
前端·vue.js