资讯动态

React入门实战:从项目创建到组件与状态管理

发布时间:2026/10/21 6:34:52 来源:尧图企业网站定制
1. 项目概述1.1 初学React前需要想清楚的事我接触React的时间不算早是在用原生JavaScript写完几个不大不小的项目之后才回头补的React。那时候的感觉是代码重复太多操作DOM的逻辑和业务逻辑搅在一起数据一变就要手动更新页面上好几个地方。React给我的第一印象不是网上说的“难”而是“别扭”因为这玩意儿的写法和传统思维完全不一样。学React“组件化”“声明式”“不可变数据”这几个词是绕不开的。一个页面不是从头到尾生成HTML而是拆成一个个独立的组件每个组件只管自己的一亩三分地自己的数据自己负责数据变了页面自动跟着变。这种开发方式像搭积木每个积木独立成型拼在一起就是整个页面。这篇文章适合三种人刚刚学完HTML/CSS/JavaScript、正准备进军框架的新手在Vue里泡了比较久、想横向对比一下的开发者还有那种看教程看了好几遍、但始终没动手敲过代码的拖延症患者。文章里所有代码我都在本地跑过踩过的坑会单独标出来争取让你照着敲就能跑起来。1.2 这次体验的主角创建与组件标题里写了两个关键词创建和组件。创建解决的是“环境从哪来、项目怎么初始化”的问题组件解决的是“React程序怎么组织、怎么复用”的问题。这两块是React的入门门槛跨过去之后后面什么Hooks、状态管理、路由都会顺畅很多。我这边的学习路径是先用官方脚手架建一个项目然后逐个拆解里面的文件搞清楚React运行时到底做了什么再动手把默认页面清空从零写自己的第一个组件。整个过程我不打算开视频跟课也不打算用在线编辑器糊弄自己直接在本地把环境搞起来用最朴素的方式感受React的实际工作方式。2. 为什么选React作为第一个框架2.1 框架选型背后的大实话很多初学者面临的第一个问题是Vue、React、Angular到底该学哪个我当时的判断思路是这样先看招聘市场需求再看生态规模最后看学习曲线是否友好。React的核心优势是“足够纯粹”。它本身只解决视图层的问题官方提供的核心API非常少class组件时代就是那十几个生命周期后来函数组件加Hooks之后核心概念也就是useState、useEffect这么几个。学习成本不高的原因在于它的心智模型非常统一状态决定视图状态变了视图就变。你不需要理解服务端渲染、路由、构建优化这些东西也能先把页面跑起来。Vue的上手速度快是因为模板语法接近HTML但对新手来说这不一定完全是好事。写久了模板对JavaScript本身的依赖会减少数据响应式这一层被框架包装得太好很多时候你根本不知道背后发生了什么。React反而不包装这些没有双向绑定没有指令就是函数调用就是JavaScript本身。这让我觉得学React的过程中顺便能巩固JavaScript这是最初没想到的额外收获。2.2 React解决的核心痛点用原生JavaScript写页面的时候最烦的事情莫过于手动操作DOM。一个输入框的值要响应到页面上你得先拿到元素再监听事件再更新另一个元素的内容。如果页面上有十个地方依赖同一个数据那就有十处更新逻辑要维护。我写过最痛苦的一段代码就是一个筛选功能同时同步三个下拉框、一个列表和一段统计文字改一处漏两处写到最后自己都不敢改。React把这个过程打包成了简单两步声明状态然后在JSX里描述“状态长什么样”。状态变了组件重新执行函数React内部自动算出前后两次的差异只更新需要动的那部分DOM。你不用再关心“哪里要改、改成什么”你只需要回答“数据是什么”。这个转变让我写代码的心态变化很大整个人的关注点从“操作步骤”变成了“描述结果”用声明式的思路替代原来的命令式操作。2.3 React背后的虚拟DOM原理速览新手第一次听说虚拟DOM容易理解成“不直接操作真实DOM了”其实这个说法不完全对。React确实不直接操作真实DOM但它维护的是一棵用JavaScript对象表示的虚拟节点树。每次组件状态更新React会重新生成一棵新的虚拟树然后用diff算法对比新旧两棵树的差异最后把差异部分批量提交给真实DOM。用大白话解释真实DOM像是写在黑板上的内容每次擦掉重写成本很高虚拟DOM是脑子里先构思好要改成什么样对比出区别后再上黑板只改不同的地方。实际开发中这个性能提升主要体现在高频率更新场景比如输入框、列表排序、实时刷新这类操作。初学者不必深挖diff算法细节但要知道为什么React能高效更新界面这对手写组件时的性能思路有帮助。提示虚拟DOM并不是所有场景都比直接操作DOM快React的收益更多来自开发效率和跨平台能力。真正想学性能优化那是另外一个深水区了。3. 本地环境准备与项目创建全流程3.1 Node.js和包管理工具的正确选择React项目跑在Node.js环境上这一步绕不过去。安装Node.js的时候有一个关键选择装LTS版本还是Current版本。我建议装LTS长期支持版稳定性优先。某些React相关工具链对新版本Node支持不及时装Current版本容易踩到兼容性坑我身边就有同学因为Node版本太高导致Vite启动报错最后老老实实换回LTS才解决。包管理工具我在npm、yarn、pnpm之间犹豫过一阵。npm是Node自带的老牌工具优势是零额外安装、兼容性最好yarn当年靠并行下载速度快出名但现在npm也追得差不多pnpm是后起之秀硬链接机制省磁盘空间安装速度在项目多了以后优势明显。新手阶段我建议直接用npm少一个变量就少一个坑。等以后你同时维护的项目多到磁盘吃紧再切pnpm也不迟。安装完之后在终端里运行node -v和npm -v如果能正常打印版本号环境就基本就绪了。这一步最常见的坑是终端提示“node不是内部或外部命令”绝大多数情况是安装时没勾选“Add to PATH”要么重装要么手动把Node的安装路径加到系统环境变量里。3.2 用Vite初始化React项目创建React项目的方式最传统的是官方脚手架Create React App缩写是CRA。但CRA现在官方都推荐新项目改用Vite了。Vite的最大优势是启动速度和热更新速度非常快因为它利用浏览器原生ES模块开发时无需打包省去了Webpack那种一启动就等几十秒的煎熬。我用的就是Vite。在终端里执行一行命令项目就创建出来npm create vitelatest my-react-app -- --template react命令里的my-react-app是项目名称模板指定为react。运行过程中它可能会问你是否安装create-vite这个包输入y回车就行。创建过程很快十几秒就能完成。之所以在create前面用npm create而不是npm install是因为npm create是npm 7以上版本提供的快捷方式会自动下载对应工具并执行初始化。创建完成后按顺序执行三条命令cd my-react-app npm install npm run devnpm install会按照package.json里的依赖清单把React、ReactDOM这些包全都装到本地node_modules目录正常情况下几分钟内完成。npm run dev启动开发服务器后终端会打印一个本地访问地址默认是http://localhost:5173用浏览器打开能看到React官方默认页面说明环境搭建成功。3.3 初始化后的目录结构逐个拆建好项目不要急着写代码先把目录结构搞清楚不然找不到文件在哪体验会很糟。Vite创建的React项目结构不算复杂最核心的几个文件我都过一遍index.html整个应用的HTML入口。Vite把index.html放在根目录而不是public目录这和传统后端的模板结构不太一样。浏览器打开页面时加载的就是这个文件里面会有一段 React最终要把内容挂载到这个div里。src/main.jsxJavaScript入口文件。它做的事情主要是把React应用挂载到页面核心代码是ReactDOM.createRoot(document.getElementById(root)).render( )。src/App.jsx根组件文件。一个React应用本质上就是一棵组件树App.jsx是这棵树的根节点。src/App.css和src/index.css样式文件分别对应组件样式和全局样式。vite.config.jsVite配置文件新手阶段基本不需要动。这里面main.jsx值得多看两眼import React from react import ReactDOM from react-dom/client import App from ./App.jsx ReactDOM.createRoot(document.getElementById(root)).render( React.StrictMode App / /React.StrictMode )createRoot是React 18版本引入的新API作用是创建React根节点然后把组件树渲染进真实DOM。React.StrictMode是严格模式它不会渲染任何可见内容只会在开发环境下做额外的检查比如帮你会发现组件里潜在的副作用问题。我刚接触时总以为StrictMode是多余的后来才知道它对养成良好编码习惯有帮助生产环境构建时它不会产生任何额外代码。注意开发环境热更新失效、页面刷新报错八成是node_modules依赖损坏先删掉node_modules和package-lock.json再重新npm install概率能解决。3.4 清空默认页面开始第一次极简渲染Vite的默认页面是官方Demo里面引用了React的Logo还有一些示例组件放到项目里当模板没问题但我要学习就需要保留一个干净起点。我把App.jsx里面的内容整个清空替换成最简单的代码function App() { return ( div h1Hello React/h1 p这是我的第一个React组件/p /div ) } export default App保存之后浏览器页面会自动更新。现在不管你怎么刷新看到的都是这两行内容。这个简单的页面背后已经发生了很多事React把这段看起来像HTML的代码解析成JavaScript对象再把对象渲染成真实DOM。这就是组件的最基本形态一个返回JSX的函数。3.5 创建过程中踩过的坑与排查方法整个创建流程我遇到过三个典型问题。第一个终端执行npm create时长时间没反应最后报网络错误。排查后发现是网络镜像源的问题npm默认源下载速度不稳定换成国内镜像源后速度提升明显。设置镜像源的命令是npm config set registry https://registry.npmmirror.com第二个npm install过程特别慢卡在某个包不动。这个其实也是网络问题和上面同一个解决方案。第三个浏览器打开后页面白屏控制台报错提示找不到某个依赖。这个一般是依赖没装全把node_modules删除后重装基本能解决。经验之谈新项目创建后第一时间看package.json里的依赖版本如果发现包版本过老或者过新直接改版本号再npm install。React 18之前的API和18有差异网上搜到的旧教程代码可能直接跑不起来注意区分。3.6 项目文件的组织方式项目创建好之后文件组织方式会影响后续的开发体验。我习惯的规划是所有组件都放进src/components目录每个组件一个文件夹里面放组件文件、样式文件和测试文件。比如建一个Button组件就创建一个src/components/Button目录里面放Button.jsx和Button.css。页面级的组件放在src/pages目录公共组件放在src/components目录工具函数放在src/utils目录接口请求放在src/api目录。这个分层在项目规模小的时候看不出优势等组件多了、页面多了没有良好组织的话src目录会变成一个文件堆积场找一个组件要翻半天目录树。4. 深入理解组件函数组件与类组件4.1 组件到底是个什么东西组件这个词听起来抽象其实它就是一段可以复用、可以组合的代码单元。拿看得见摸得着的东西比喻组件就像乐高积木。每一块积木有固定形状和接口不同积木之间通过凸起和凹槽拼插组合。React组件也一样有自己的结构、自己的功能还可以从外部接收参数用来控制显示效果。一个React应用的页面结构从组件角度看就是一棵倒挂的树。根组件是整棵树的起点它的子组件形成树枝子组件的子组件形成更细的枝条。数据从根部往下传递一层一层传递到叶子组件。这种做法把复杂页面拆成了许多小模块每个模块只知道自己职责范围内的信息不再需要统筹全局代码可维护性就有了基础。4.2 函数组件React的入门第一步函数组件就是普通的JavaScript函数按约定函数名首字母大写返回值是JSX结构。React 18时代函数组件已经成为主力写法官方文档也推荐用函数组件加Hooks代替类组件。为什么函数组件受欢迎因为它足够简单——不需要记忆生命周期函数的执行时机不需要处理this指向问题就是”函数进来、JSX出去“。一个最常见的函数组件长这样function UserCard(props) { return ( div classNameuser-card h2{props.name}/h2 p职业{props.job}/p /div ) }这个组件接收一个props对象作为参数name和job是外部传入的数据组件内部只负责把数据显示出来。它的关注点非常单纯没有任何多余逻辑。props里的内容你可以理解成外部递给组件的一张纸条组件拿到纸条按上面的信息来渲染内容。这种数据流是单向的从父组件流向子组件子组件不能反向修改父组件的数据这在调试时非常舒服——你可以确定数据的来源顺着组件树往上就能找到。第一次看完这个函数组件我最大的感慨是这不就是个函数吗原来框架没我想的那么玄乎。React的哲学之一就是“UI是函数的结果”给相同的输入一定能得到相同的输出这让代码的行为变得可预测。4.3 类组件虽然旧但能看懂老代码函数组件简单但React生态里还有大量老项目是用类组件写的。我刚学的时候不太理解为什么要有类组件这种写法直到读开源项目代码时才明白理解类组件是为了能读懂存量代码。类组件的基本形态import React, { Component } from react class UserCard extends Component { render() { return ( div classNameuser-card h2{this.props.name}/h2 p职业{this.props.job}/p /div ) } }和函数组件一比差异很明显。类组件需要继承React的Component类必须实现render方法访问props的时候用的是this.props。类组件里可以使用this.setState来更新内部状态可以声明生命周期方法结构更复杂但能力更多。实际生产中React 18都出来了新项目再用类组件属实没必要。函数组件加Hooks能完成类组件的所有功能代码还更简洁。但面试会问类组件和函数组件的区别接手老项目也难免碰到了解它的原理不是为了在新项目里用它是为了应急。4.4 组件通信props与回调函数单组件只是入门页面是多个组件协作的结果。组件之间的通信最常见的方式是父组件通过props向子组件传数据反过来子组件要向父组件传数据传的是回调函数。举一个实际场景父组件有一个用户列表子组件是列表里的每一项点击子组件的“删除”按钮要告诉父组件“这一项要被删除”。实现方式是这样// 父组件 function UserList() { const users [ { id: 1, name: 张三 }, { id: 2, name: 李四 } ] const handleDelete (id) { console.log(删除用户, id) } return ( div {users.map(user ( UserItem key{user.id} user{user} onDelete{handleDelete} / ))} /div ) } // 子组件 function UserItem({ user, onDelete }) { return ( div span{user.name}/span button onClick{() onDelete(user.id)}删除/button /div ) }子组件的onDelete属性是父组件传下来的函数子组件不调用它也永远不会执行只有点击按钮时才触发。这个设计很巧妙——子组件并不知道删除的具体逻辑是什么它只负责把消息传递出去父组件怎么处理完全是父组件的事。这就是单向数据流带来的清晰职责划分。4.5 组件的渲染流程与更新机制一个React组件从诞生到消失会经历挂载、更新、卸载三个阶段。挂载阶段执行函数体返回JSX并渲染到页面上更新阶段发生在props或state变化时组件函数重新执行React对比新旧虚拟DOM后更新真实界面卸载阶段发生在组件从组件树中被移除时。函数组件没有类组件那些生命周期方法React 18用Hooks来替代比如useEffect可以在组件挂载后、更新后、卸载前各执行一段逻辑。写函数组件时心里要装着render这个动作代表什么每次render函数体从头到尾执行一遍新产生的返回值会用于对比更新。所以不要在函数体里做昂贵的计算或者发起网络请求——那会导致每次更新都重复执行。4.6 受控组件与非受控组件表单处理的基本功表单是Web应用里躲不开的场景。React对表单的处理跟原生HTML有很大不同原生表单有自己的内部状态用户输入的内容存在DOM的value属性里React则推荐用状态来控制输入框的值这种组件叫受控组件。function LoginForm() { const [username, setUsername] useState() const handleChange (event) { setUsername(event.target.value) } return ( input typetext value{username} onChange{handleChange} / ) }输入的value由username状态控制用户每一次键入触发onChange通过setUsername更新状态React重新渲染输入框显示新值。状态成为唯一数据源任何时候你要拿到输入框的值直接读username就行不用再通过查找DOM元素的方式拿。非受控组件则是不控制值属性用ref直接读取DOM的value适用于不需要实时干预输入内容的场景如表单提交时才取值的简单表单。受控组件的优势在动态校验、联动查询、条件禁用时能体现得很明显。5. 状态管理React心智模型的核心5.1 useState函数组件的第一口甜头组件只有静态展示是不够的真实页面需要交互交互就需要状态。React的useState是函数组件中最基础的Hooks用来让组件“记住”一些数据。以计数器为例子import { useState } from react function Counter() { const [count, setCount] useState(0) const increment () { setCount(count 1) } return ( div p当前计数{count}/p button onClick{increment}加一/button /div ) }useState返回两个值当前状态值和更新状态的函数。每次调用setCount组件重新渲染count变量更新为最新的值。用数组解构的写法说明React官方希望你在需要多个状态时多次调用useState把它们分开管理而不是在一个大对象里集中存放。一个容易忽略的细节setCount传入的是新值React会把这个新值作为下一次渲染的count。如果你需要基于旧值计算新值直接用count在闭包里取值在某些情况下可能会拿到过期数据。以防这种情况React允许传入一个函数setCount(prevCount prevCount 1)函数参数里的prevCount始终是当前最新值适合连续更新、异步更新这类场景。我在写计数器连带双击事件时踩过这个坑第一次双击结果只加了1原因是两次点击用的都是同一个闭包里的旧值。5.2 状态不可变更新为什么不能直接修改变量React有一条铁律更新状态时不要直接修改它而是创建新数据替代。比如有一个数组状态const [list, setList] useState([1, 2, 3]) // 错误写法 list.push(4) setList(list) // 正确写法 setList([...list, 4])错误写法的问题在于list被push修改后和之前的list内存引用相同React比较旧状态和新状态时会认为它们没有变化不会触发组件更新界面就不会刷新。正确写法会创建一个新数组React才能检测到变化。这条规则背后的原因跟React的更新机制有关。React判断状态是否变化用的是Object.is比较创建新引用才能让比较结果为false组件才能重新渲染。这个细节对性能也有影响一旦遵守不可变更新React可以做更精细的更新追踪。新手时期我也觉得这是多此一举直到在列表页面遇到过数据状态改了但界面怎么都不刷新的问题才意识到不可变更新不是风格偏好而是React的底层要求。5.3 状态提升多个组件共享同一个数据页面里如果有两个组件需要显示同一份数据怎么办React给出的方案是状态提升。把数据放在两个组件的共同父组件里通过props传给两个子组件。输入框组件负责修改状态展示组件负责读取状态状态在父组件中保存两个子组件都通过props获取。function Parent() { const [keyword, setKeyword] useState() return ( div SearchInput value{keyword} onChange{setKeyword} / SearchResult keyword{keyword} / /div ) }状态提升的好处是非常直白地展示数据流状态在哪谁改它谁读它一眼便知。等以后你发现props层层传递很烦自然会去找Context或状态管理库但那时候你已经理解了它们的本质——无非是把状态提升到更高层级或者提升到组件树之外让更多组件可以访问。5.4 使用状态时常见的三个错误第一在useState里放入对象/数组作为初始值时每次渲染都会重新创建初始值期望只在初次创建一次实际上不会useState的初始化值只在组件首次渲染时生效后续渲染时这段初始化代码会被忽略不会反复执行这个不用过度担心。第二组件卸载后仍然调用setState浏览器控制台会报“Cant perform a React state update on an unmounted component”。实际场景比如组件里发起了一个网络请求返回之前用户切走了页面。React 18之后这个警告有所调整但逻辑上仍然要尽量避免在卸载后更新状态常见做法是利用useEffect的清理函数取消请求。第三把多次相关状态拆成多个useState调用导致setA、setB、setC连写三行。如果它们本来就属于同一份业务数据用useReducer或useState保存单个对象可能更合适。状态拆分的粒度没有标准答案但原则是“相关一起变的数据放一起管理”。5.5 Hooks规则里不能破坏的两条底线React Hooks有两个规则违反任意一条都不会报语法错误但运行时会有严重问题。第一条只在函数组件顶层调用Hooks不要在循环、条件语句或嵌套函数里调用。原因很简单——React依赖Hooks的调用顺序来保持状态与组件实例的关联每次渲染调用顺序必须保持一致否则状态会错乱。第二条只能在React函数组件或自定义Hooks中调用Hooks不要在普通JavaScript函数里调用。实际开发中条件调用useState的典型错误场景是这样组件里需要根据某个布尔值决定是否初始化一个状态。直觉写法是if (visible) { useState(...) }。这段代码在visible为true时调用Hooks、false时不调用下一次渲染顺序就乱了。解决办法是把条件判断移到组件内部始终无条件调用useState再根据条件决定如何使用它。6. 关键差异意识JSX不是HTML6.1 JSX与HTML的核心区别用React写页面最直观的差异就是JSX。JSX是JavaScript的语法扩展看起来像HTML但不完全是HTML。第一次写React时我把class直接写进JSX里页面样式完全没有生效控制台还报了错误。原因很简单JSX里不能用class要用className。为什么不能用class因为class是JavaScript的保留字。JSX最终会被转译成React.createElement调用className在其中作为props传给React最终映射为DOM的class属性。同理htmlFor替代label标签里的for。风格上还有一个习惯JSX标签属性命名使用驼峰式。onclick变成onClicktabindex变成tabIndexonchange变成onChange。导致这些命名的根本原因是JSX对象的属性风格更接近JavaScript而不是HTML。6.2 JSX里怎么渲染数据花括号的使用JSX的表达式插值用单层花括号里面放的是JavaScript表达式可以是一个变量、一次三元判断、一个函数调用。比如const userName 小明 const isVip true return ( div p用户名{userName}/p p会员状态{isVip ? VIP用户 : 普通用户}/p /div )注意花括号里不能写完整的JavaScript语句比如if、for这类。如果需要条件渲染有三元表达式或逻辑与。需要循环渲染用数组的map方法。JSX表达式的原则是任何可以在等号右边出现的内容都可以放进去。列表渲染是新手必然会碰到的场景function List() { const items [苹果, 香蕉, 橙子] return ( ul {items.map((item, index) ( li key{index}{item}/li ))} /ul ) }每个列表项都必须有一个key属性React用key来识别列表中的每一项在增删排序时追踪每个元素。我刚开始注册key最省事的方式就是用index后来才知道列表项顺序变化时index为key会引发状态错乱。最稳妥的key是数据里天然存在的唯一id比如用户id、订单号。只有在列表是纯静态展示、不增不改不排序时才适合用index。6.3 条件渲染的几种常见写法React的条件渲染本质上还是JavaScript的条件判断。只是用不同的语法风格组合在JSX里// 三元表达式 {isLoggedIn ? UserPanel / : LoginButton /} // 逻辑与适合只显示、不显示两种状态 {isLoading div加载中.../div} // switch风格通过函数返回 function getContent(status) { switch (status) { case loading: return div加载中/div case error: return div出错啦/div default: return div正常显示/div } }eng注意逻辑与写法中isLoading为false时React不会渲染任何内容但如果isLoading是0或者空字符串React会渲染数字0或者空字符串。所以在条件渲染时保证条件变量是布尔值如果是数量判断要写成{count 0 ...}不要写{count ...}。6.4 key属性的理解和应用前面提到列表渲染必须用keyReact官方建议key应该是在数据里稳定存在的唯一标识。为什么key必须稳定假设有一个渲染用户列表的组件每个列表项内部还有一个输入框。如果使用index作为key当用户列表前插入一条新数据时React会认为原来的第一个节点变成了第二条数据输入框会被复用但用户输入的内容就串到了错误的位置。用唯一id作为keyReact就能准确识别哪条数据去哪了输入框跟着正确的数据走。6.5 样式方案的几种选择React的样式方案比原生HTML灵活选择也多。最基础的用法是className配全局CSS用className指定样式类名页面效果跟原生CSS一致。复杂项目常用CSS ModulesVite默认支持这种方案局部作用域样式类名编译后自动加哈希后缀避免样式冲突。更进阶的有styled-components这种CSS-in-JS方案把样式直接写在JavaScript文件里动态样式特别灵活。新手阶段我推荐CSS Modules一次配置后续开发体验都比较顺。它解决的“样式冲突”问题是全局CSS最让人头疼的两个不同组件里都定义了.title类后加载的类会覆盖先加载的类排查起来相当费劲。CSS Modules把这个痛点解决了学习成本几乎为零就是import样式后当对象用它的属性名。import styles from ./Card.module.css function Card() { return div className{styles.card}卡片内容/div }7. 从零搭建第一个入门组件完整实操演示7.1 制作一个用户名片卡组件掌握了前面的知识点现在完整做一个小组件练手。需求很简单做一个用户名片卡显示头像、昵称、签名点赞功能根据点赞数量显示不同文案。拆解这个需求这是单一组件有状态点赞数有props用户数据有交互点击按钮。先做静态结构。新建src/components/UserCard.jsxfunction UserCard({ user }) { return ( div classNameuser-card div classNameavatar{user.avatar}/div div classNameinfo h3{user.name}/h3 p{user.bio}/p /div /div ) } export default UserCarduser是一个包含头像、昵称、签名的数据对象。组件把这个对象拆分各个字段渲染到对应位置。这里的props解构写法在函数组件中很常见我第一次写总是写props.user.name解构之后代码简洁很多。然后在App.jsx里引入这个组件并传入数据import UserCard from ./components/UserCard function App() { const user { name: 小明, bio: 前端开发者喜欢捣鼓新玩意, avatar: } return UserCard user{user} / } export default App数据从App传递到UserCardUserCard不直接拥有数据只负责渲染。数据来源集中在App里修改数据时只需要改一处。页面刷新后卡片就显示出来了。7.2 加入交互逻辑点赞功能和文案联动静态视图完成接下来加状态和交互。修改UserCard加入useStateimport { useState } from react function UserCard({ user }) { const [liked, setLiked] useState(false) const [likeCount, setLikeCount] useState(0) const handleLike () { setLiked(!liked) setLikeCount(prev liked ? prev - 1 : prev 1) } return ( div classNameuser-card div classNameavatar{user.avatar}/div div classNameinfo h3{user.name}/h3 p{user.bio}/p button onClick{handleLike} {liked ? 已点赞 : 点赞} /button span{likeCount 0 ? ${likeCount} 人赞过 : 还没人点赞抢个沙发}/span /div /div ) }这里的关键逻辑在handleLike里。liked表示是否已点赞likeCount是点赞总数。点击按钮时点赞状态取反同时根据取反后的liked值决定likeCount是加一还是减一。注意我用了函数式更新setLikeCount(prev ...)就是为了确保在状态连续变化时始终拿的是最新值。这套实现逻辑覆盖了React入门的核心内容函数组件、props传参、JSX渲染、条件渲染、状态管理、事件处理。代码量不大但每一个语法点都有实际意义建议你亲自动手敲一遍遇到错误就查控制台过程比结果更有价值。7.3 抽离子组件卡片里的每个模块用户名片卡已经能用了但组件内部还可以拆。把头像、信息区、按钮分别做成小组件结构更清晰。比如function Avatar({ emoji }) { return div classNameavatar{emoji}/div } function UserInfo({ name, bio }) { return ( div classNameinfo h3{name}/h3 p{bio}/p /div ) } function LikeButton({ liked, onClick }) { return ( button onClick{onClick} {liked ? 已点赞 : 点赞} /button ) }然后UserCard组合这三个子组件。每个子组件只做一件事测试起来非常容易想改样式也不会误伤其他部分。子组件之间互不干扰这正是组件化的核心价值——把一个大问题拆成若干小问题每个小问题的解决方案都是独立的。7.4 在这个实操里学到的东西完成这个小组件后我对React的几个核心概念都有了直观认识。props让我理解了怎么传数据、数据流向哪useState让我掌握了怎么让组件“活”起来拆分组件让我体会到了复用自己的代码。这套顺序学习下来前面零散的JSX语法、虚拟DOM原理、组件概念都串联起来了形成了完整的心智模型。8. 新手高频坑点React初学避雷指南8.1 渲染不更新状态改了页面没反应的查找顺序React新手遇到最多的问题就是“改了状态页面没变”。我的排查思路可以总结成一条链先确认setState有没有被调用再看调用时传的是不是新引用再看组件有没有被卸载最后看是不是有多个组件实例导致改的不是同一个。最常见的原因有两个。第一直接修改了对象或数组类型的状态。前面说过React基于引用比较直接修改相同引用不会触发渲染。解法是创建新对象再set。第二异步场景下拿旧状态闭包设置了新状态。比如在setTimeout里用旧值加一连续触发两次结果只加了一次。解法是用函数式更新。8.2 JSX语法细节为什么我的标签对不上JSX要求所有标签必须正确闭合和嵌套一个标签没闭合整个组件都会报错。常见的错误有自闭合标签写成了导致React解析到末尾还在等闭合标签注释方式不对JSX注释用{/* ... */}而不是HTML的 属性值漏了花括号导致变量名被当成字符串。还有一个细节JSX相邻的顶层元素必须包在一个标签里。如果你return两个相邻divReact会报“Adjacent JSX elements must be wrapped in an enclosing tag”。解法是外面包一层div或者用Fragment空标签/。8.3 组件的重新渲染为什么子组件也跟着跑父组件每次状态更新都会导致子组件重新渲染这个现象新手会困惑。比如父组件输入框每次键入都重新渲染子组件接收一个静态props但也在跟着执行。React的默认行为是父组件render所有子组件都跟着render不管props有没有实际变化。这不一定带来性能问题但如果子组件渲染开销大就会卡顿。解决的方案是React.memo它包裹组件后会在props没有变化时跳过组件重新渲染。注意memo只做浅层比较嵌套对象属性变化它感知不到实际要结合使用场景判断是否有效。新手阶段不用纠结这个性能问题先理解渲染机制才是重点。8.4 useEffect里的依赖数组写错就出大事useEffect在函数组件里用来处理副作用比如订阅事件、发起请求、操作外部API。它的依赖数组很容易写错。最简单的方式是每次渲染执行不传依赖数组传空数组只在挂载和卸载时执行传具体变量变量变化时执行。空数组依赖和React严格的规则放一起会产生一个经典场景useEffect里要读取一个外部状态但依赖数组是空的React会在每次渲染都看到旧的state。新手以为useEffect只在初始化时执行一次就好了结果发现里面读到的是第一次渲染的值。解法是把依赖加进数组或者用ref读取最新值。这个坑我花了整整一个下午才明白所以特别拿出来提醒一下。9. 初学阶段的实用技巧与扩展建议9.1 利用浏览器插件提升调试效率开发React项目Chrome浏览器配上React Developer Tools插件能大幅提升调试体验。装上以后浏览器开发者工具的Components面板能直观看到组件树点击任意组件能看到它的props和state。还有Profiler面板用来分析组件渲染性能找到性能瓶颈很有用。Vite项目开发模式下默认有热更新修改代码保存即刷新页面对快速迭代很有价值是我最常用的工具之一。9.2 遇到问题的排查路径自己学习React遇到的问题多数不是突破性的大部分都能通过搜索解决。碰到报错先读报错信息React的控制台报错提示已经足够具体比如告诉我们附近缺少key、标签需要包裹等等。看不懂报错时再去搜索搜索关键词用英文报错原文而不是中文翻译搜索结果质量往往高很多。排错时还可以用一个小技巧如果某个组件渲染结果和你预期的不一致在render的return前加console.log打印props和state观察每次渲染的数据变化。数据对了但页面不对问题在JSXJSX对但结果错问题在逻辑两个都对但页面还不对多数是样式或事件处理的问题。9.3 学完组件之后的学习路线组件这块基础打好之后后面的主线就是把React生态串联起来。建议按这个顺序推进先系统过一遍useEffect、useContext、useReducer这几个Hooks然后学React Router做页面路由接着学fetch或axios调接口再往后接触状态管理库比如Zustand或Redux Toolkit最后再看Next.js这种全栈框架。React官方文档一直是我推荐的学习材料里面的互动教程特别适合新手。不要只看不写每学一个概念就找一个交互场景把它用起来哪怕只是一个计数器、一个待办清单。我自己的经验是光看文档产生“我懂了”的错觉太常见了动手写一次才知道自己哪里没理解透。9.4 和Vue的横向对比给React初学者的建议很多从Vue过来的开发者会觉得React别扭——没有模板指令、没有双向绑定、JSX需要适应。我的经历是从React切入再了解Vue的但身边不少朋友正好相反。就开发思维来说Vue更偏“搭积木式的拼装”模板上手快React更偏“JavaScript的函数式思维”需要你有一定的逻辑抽象能力。如果你感觉React学起来不顺不代表你不行只是还没适应它的思维方式。9.5 一个值得养成的编程习惯小步提交及时验证我学React期间受益最大的一个习惯就是每写十几行代码就保存一次并在浏览器里看效果。不要一口气把整个组件写完再验证出了问题很难定位。小步修改、及时验证问题范围自然就小了。这个方法在React这种声明式框架里尤其好用因为你改的每一点都能立刻看到视图反馈。最后给自己的学习过程做个总结初学React不需要第一天就理解虚拟DOM的全部细节也不需要死记所有Hooks API。先把“创建一个项目、写一个组件、让界面动起来”这三步做好整个React的世界会自然而然地在你面前展开。遇到问题不逃避动手试错就是最好的老师。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑