React核心语法:组件化与声明式编程
React 的核心语法围绕“组件化”“声明式编程”展开从最初的类组件到如今的函数组件Hooks开发体验不断优化。以下是开发和面试中最常用的核心语法附实战代码、考点解析和避坑指南确保拿来就用、记了就会。2.1 核心基础JSX 语法React 独有面试必问JSXJavaScript XML是React的核心语法允许在JavaScript中编写HTML-like代码本质是React.createElement()方法的语法糖浏览器无法直接解析JSX需通过Babel编译为JavaScript代码后执行。2.1.1 JSX 基础语法规则// 1. 基本使用HTML标签直接嵌入JSX const App () { return ( div classNameapp h1React 基础语法/h1 pJSX 是 React 独有的语法糖/p /div ); }; // 2. 嵌入JS表达式用 {} 包裹 const name React; const age 10; const App () { return ( div p框架{name}/p p诞生至今{age 年}/p p是否主流{age 5 ? 是 : 否}/p /div ); }; // 3. 注意事项面试避坑 // ① class 需改为 className避免与JS关键字冲突 // ② style 需传入对象key为驼峰命名如fontSize而非font-size // ③ 标签必须闭合单标签需加 /如 img src / // ④ 只能有一个根节点或用 Fragment / 无包裹节点 const App () { return ( div classNamebox style{{ fontSize: 16px, color: #333 }} 正确写法 /div img srcreact-logo.png altReact图标 / / ); };2.1.2 面试考点必背问题JSX 是什么它和 HTML、JavaScript 的关系是什么标准答案1. JSX 是 React 独有的语法糖允许在 JavaScript 中编写 HTML-like 代码简化 React 组件的编写2. JSX 本质是 React.createElement() 方法的语法糖Babel 会将 JSX 编译为 React.createElement(type, props, children) 调用最终生成虚拟 DOM 对象3. JSX 不是 HTML它支持嵌入 JS 表达式、自定义属性如 className且有严格的语法规则如标签闭合、驼峰命名4. JSX 也不是纯 JavaScript需通过编译才能被浏览器解析执行。2.2 组件定义函数组件 vs 类组件面试高频对比React 组件分为两种函数组件React 16.8 推荐和类组件传统方式逐步被函数组件替代面试中常考两者的区别、适用场景以及为什么推荐函数组件。2.2.1 函数组件推荐结合 Hooks 使用函数组件是简单的JavaScript函数接收Props参数返回JSXReact 16.8引入Hooks后函数组件可以拥有状态State和生命周期成为开发首选。// 基础函数组件无状态 const Greeting (props) { // 接收Props参数 const { name } props; return h1Hello, {name}!/h1; }; // 带状态的函数组件结合Hooks import { useState } from react; const Counter () { // 用useState定义状态count为状态值setCount为更新状态的方法 const [count, setCount] useState(0); // 事件处理 const increment () { setCount(count 1); // 不可直接修改count需通过setCount更新 }; return ( div p计数{count}/p button onClick{increment}增加/button /div ); };2.2.2 类组件传统方式了解即可类组件继承自React.Component通过state定义状态通过生命周期方法管理组件生命周期语法繁琐目前仅在旧项目中可见。import React from react; class Counter extends React.Component { // 定义状态 state { count: 0 }; // 事件处理 increment () { // 不可直接修改this.state需通过this.setState()更新 this.setState({ count: this.state.count 1 }); }; // 渲染方法必须有返回JSX render() { return ( div p计数{this.state.count}/p button onClick{this.increment}增加/button /div ); } }2.2.3 面试考点函数组件 vs 类组件必背对比维度函数组件类组件面试重点语法复杂度简单JavaScript函数代码简洁繁琐需继承React.Component重写render方法为什么推荐函数组件1. 语法简洁开发效率高2. 结合Hooks可灵活管理状态和生命周期避免类组件的this指向问题3. 性能更优无类实例创建的开销4. 更易适配TypeScript类型推导更简单5. 符合React未来的发展趋势React官方重点优化函数组件。状态管理通过HooksuseState、useReducer管理通过this.state和this.setState()管理生命周期通过HooksuseEffect、useLayoutEffect模拟通过生命周期方法componentDidMount等管理this指向无this避免this指向混乱有this需注意绑定箭头函数、bind易出错适用场景所有场景推荐尤其是复杂交互、状态管理场景旧项目维护简单无状态组件不推荐新开发使用2.3 核心APIProps 与 State组件通信与状态管理基础Props 和 State 是 React 组件的核心概念两者都用于存储数据但用途不同面试中常考两者的区别、使用场景及注意事项。2.3.1 Props父传子只读不可改PropsProperties是父组件传递给子组件的数据子组件接收后只读不可修改单向数据流用于组件间通信父传子可设置默认值、类型校验。// 父组件传递Props import { useState } from react; import Child from ./Child; const Parent () { const [name] useState(React); return ( div Child name{name} age{10} isPopular{true} / /div ); }; // 子组件接收Props设置默认值和类型校验 import PropTypes from prop-types; const Child (props) { // 解构Props const { name, age, isPopular, gender 未知 } props; // gender设置默认值 return ( div p框架{name}/p p年龄{age}/p p是否主流{isPopular ? 是 : 否}/p p性别{gender}/p /div ); }; // 类型校验面试考点提升代码健壮性 Child.propTypes { name: PropTypes.string.isRequired, // 字符串类型必填 age: PropTypes.number.isRequired, // 数字类型必填 isPopular: PropTypes.bool, // 布尔类型可选 gender: PropTypes.string, // 字符串类型可选 }; export default Child;2.3.2 State组件内部状态可修改State 是组件内部的状态数据用于管理组件自身的动态变化如输入框内容、计数器值可通过setState类组件或useState函数组件修改修改后组件会重新渲染。核心注意事项面试避坑State 不可直接修改如 count count 1 错误需用 setCount(count 1)State 更新是异步的类组件this.setState、函数组件setState都是异步如需依赖上一次的State需传入函数如 setCount(prev prev 1)State 是局部的仅作用于当前组件子组件无法直接访问需通过Props传递。import { useState } from react; const InputDemo () { // 定义输入框状态 const [inputValue, setInputValue] useState(); // 处理输入变化依赖上一次状态用函数形式 const handleInputChange (e) { setInputValue(e.target.value); // 普通更新 }; // 重置输入框依赖上一次状态用函数形式 const resetInput () { setInputValue(prev ); // 传入函数确保拿到最新的prev值 }; return ( div input typetext value{inputValue} onChange{handleInputChange} placeholder请输入内容 / button onClick{resetInput}重置/button p输入内容{inputValue}/p /div ); };2.3.3 面试考点Props vs State必背问题Props 和 State 的区别是什么标准答案1. 来源不同Props 是父组件传递的State 是组件内部定义的2. 可修改性不同Props 只读不可修改State 可通过setState/useState修改3. 作用范围不同Props 用于组件间通信父传子State 用于管理组件内部动态状态4. 更新影响不同Props 变化会触发子组件重新渲染State 变化会触发当前组件及子组件重新渲染5. 默认值Props 可设置默认值State 可设置初始值。2.4 核心APIHooksReact 16.8 重点面试必考Hooks 是 React 16.8 引入的新特性核心作用是“让函数组件拥有状态和生命周期”解决类组件语法繁琐、this指向混乱、逻辑复用困难的问题。常用 Hooks 及实战的是面试高频考点必须熟练掌握。2.4.1 常用 Hooks 实战开发必备useState管理组件内部状态最基础的Hooks用于定义组件内部的状态返回“状态值 更新状态的方法”前面已实战核心注意事项异步更新、不可直接修改状态。useEffect模拟生命周期处理副作用副作用组件渲染后执行的操作如请求数据、操作DOM、设置定时器useEffect 可模拟类组件的 componentDidMount、componentDidUpdate、componentWillUnmount 三个生命周期。import{useState, useEffect}fromreact;const EffectDemo(){const[count, setCount]useState(0);//1. 模拟 componentDidMount只执行一次依赖项为空数组 useEffect((){console.log(组件挂载完成请求数据);// 模拟请求数据 fetch(/api/data).then(resres.json()).then(dataconsole.log(请求到的数据, data));// 模拟设置定时器 const timersetInterval((){setCount(prevprev 1);},1000);// 清理函数模拟 componentWillUnmount组件卸载时执行return(){console.log(组件卸载清理定时器和请求);clearInterval(timer);// 取消请求需结合AbortController};},[]);// 依赖项为空数组只执行一次 //2. 模拟 componentDidUpdatecount变化时执行 useEffect((){console.log(count变化了, count);},[count]);// 依赖项为countcount变化时执行returnpcount{count}/p;};useContext跨层级组件通信用于解决“props drilling”props层层传递问题实现跨层级组件通信无需手动传递props。import{createContext, useContext, useState}fromreact;//1. 创建Context const ThemeContextcreateContext();//2. 父组件提供Context值 const Parent(){const[theme, setTheme]useState(light);const toggleTheme(){setTheme(prevprevlight?dark:light);};return(ThemeContext.Providervalue{{theme, toggleTheme}}Child //ThemeContext.Provider);};//3. 子组件接收Context值无需通过props传递 const Child(){const{theme, toggleTheme}useContext(ThemeContext);return(divstyle{{background: themelight?#fff:#333, color: themelight?#333:#fff}}p当前主题{theme}/pbuttononClick{toggleTheme}切换主题/button/div);};useReducer复杂状态管理用于管理复杂状态如对象、数组类似Redux的思想将状态更新逻辑抽离使代码更易维护。import{useReducer}fromreact;//1. 定义reducer函数接收state和action返回新state const reducer(state, action){switch(action.type){caseINCREMENT:return{...state, count: state.count 1};caseDECREMENT:return{...state, count: state.count -1};caseRESET:return{...state, count:0};default:returnstate;}};const ReducerDemo(){//2. 使用useReducer接收reducer和初始state const[state, dispatch]useReducer(reducer,{count:0});return(divp计数{state.count}/pbuttononClick{()dispatch({type:INCREMENT})}增加/buttonbuttononClick{()dispatch({type:DECREMENT})}减少/buttonbuttononClick{()dispatch({type:RESET})}重置/button/div);};useRef获取DOM元素/保存持久化值有两个核心用途1. 获取DOM元素如输入框焦点2. 保存持久化值组件重新渲染时值不会重置且修改不会触发组件重新渲染。import{useRef, useEffect}fromreact;const RefDemo(){//1. 获取DOM元素 const inputRefuseRef(null);//2. 保存持久化值组件重新渲染时值不会重置 const countRefuseRef(0);useEffect((){// 组件挂载后让输入框获取焦点 inputRef.current.focus();// 每渲染一次countRef的值加1不会触发组件重新渲染 countRef.current1;console.log(组件渲染次数, countRef.current);},[]);return(divinputref{inputRef}typetextplaceholder自动获取焦点//div);};6. **useMemouseCallback性能优化**用于优化组件渲染性能避免不必要的重新渲染import{useState, useMemo, useCallback}fromreact;importChild from./Child;const MemoDemo(){const[count, setCount]useState(0);const[name, setName]useState(React);// useMemo缓存计算结果只有count变化时才重新计算 const doubleCountuseMemo((){console.log(重新计算doubleCount);returncount *2;},[count]);// useCallback缓存函数只有name变化时才重新创建函数 const handleClickuseCallback((){console.log(点击事件name, name);},[name]);return(divpcount{count}doubleCount{doubleCount}/pbuttononClick{()setCount(prevprev 1)}增加count/buttonbuttononClick{()setName(prevprev !)}修改name/buttonChildonClick{handleClick}//div);};- useMemo缓存计算结果依赖项不变时不会重新计算 - useCallback缓存函数依赖项不变时不会重新创建函数避免子组件因函数引用变化而重新渲染。2.4.2 Hooks 使用规则面试避坑必背只能在函数组件的顶层调用Hooks不能在if、for、循环、条件判断、嵌套函数中调用只能在函数组件或自定义Hooks中调用Hooks不能在类组件、普通JavaScript函数中调用Hooks调用顺序必须固定每次组件渲染时Hooks的调用顺序不能变自定义Hooks必须以“use”开头如useRequest、useTheme便于React识别和检查规则useEffect的清理函数必须清理副作用如定时器、请求、事件监听避免内存泄漏。2.5 组件通信开发高频面试必问React 组件通信是开发中最常用的场景面试中常考“不同组件关系父子、跨层级、兄弟的通信方式”以下是按使用频率排序的5种通信方式附实战代码和适用场景。**Props/回调函数父子组件通信最常用**父传子用Props子传父用“回调函数”父组件传递一个函数给子组件子组件调用该函数传递数据。// 父组件传递Props和回调函数import{useState}fromreact;importChild from./Child;const Parent(){const[parentMsg, setParentMsg]useState(父组件消息);const[childMsg, setChildMsg]useState();// 父组件定义回调函数接收子组件传递的数据 const handleChildMsg(msg){setChildMsg(msg);};return(divp子组件传来{childMsg}/pChildmsg{parentMsg}onSendMsg{handleChildMsg}//div);};// 子组件接收Props调用回调函数传递数据 const Child(props){const{msg, onSendMsg}props;const sendMsg(){// 调用父组件传递的回调函数传递子组件数据 onSendMsg(子组件消息);};return(divp父组件传来{msg}/pbuttononClick{sendMsg}向父组件发送消息/button/div);};**useContext跨层级组件通信常用**用于解决“props drilling”问题跨层级如祖父→孙子组件通信无需手动传递props前面已实战适用场景中小型项目、跨层级组件通信不复杂的场景。**状态管理工具Redux/RTK/Zustand中大型项目常用**用于全局状态管理适用于多组件共享状态如用户信息、主题配置后续生态章节详细讲解面试中常考Redux的核心原理与使用。**事件总线EventBus兄弟组件通信偶尔用**通过自定义事件机制实现非父子、非跨层级组件通信如兄弟组件需手动解绑事件避免内存泄漏。//1. 定义事件总线utils/eventBus.js class EventBus{constructor(){this.events{};}// 订阅事件 on(eventName, callback){if(!this.events[eventName]){this.events[eventName][];}this.events[eventName].push(callback);}// 发布事件 emit(eventName, data){if(this.events[eventName]){this.events[eventName].forEach(callbackcallback(data));}}// 解绑事件 off(eventName, callback){if(this.events[eventName]){this.events[eventName]this.events[eventName].filter(cbcb!callback);}}}exportdefault new EventBus();//2. 组件A发布事件importeventBus from../utils/eventBus;const ComponentA(){const sendMsg(){eventBus.emit(brotherMsg,组件A的消息);};returnbuttononClick{sendMsg}向组件B发送消息/button;};//3. 组件B订阅事件import{useEffect}fromreact;importeventBus from../utils/eventBus;const ComponentB(){useEffect((){// 订阅事件 const callback(data){console.log(收到组件A的消息, data);};eventBus.on(brotherMsg, callback);// 解绑事件避免内存泄漏return(){eventBus.off(brotherMsg, callback);};},[]);returnp组件B等待接收消息/p;};**useRef forwardRef父子组件通信获取子组件DOM/方法**父组件通过useRef获取子组件的DOM元素或方法需子组件用forwardRef包裹适用于父组件需要操作子组件DOM的场景如输入框焦点、滚动操作。import{useRef, forwardRef, useImperativeHandle}fromreact;// 子组件用forwardRef包裹暴露方法给父组件 const ChildforwardRef((props,ref){ const inputRefuseRef(null);//用useImperativeHandle暴露方法给父组件可选避免暴露整个子组件实例 useImperativeHandle(ref,()({ focusInput:(){ inputRef.current.focus();},clearInput:(){ inputRef.current.value;} }));returninputref{inputRef}typetext/;});// 父组件用useRef获取子组件暴露的方法 const Parent(){const childRefuseRef(null);const handleFocus(){// 调用子组件暴露的方法 childRef.current.focusInput();};const handleClear(){childRef.current.clearInput();};return(divChildref{childRef}/buttononClick{handleFocus}让子组件输入框获取焦点/buttonbuttononClick{handleClear}清空子组件输入框/button/div);};2.6 其他常用语法开发必备条件渲染通过if-else、三元表达式、逻辑与实现适用于根据状态展示不同UI。列表渲染用map方法渲染列表必须添加key唯一标识避免渲染错误面试必问key的作用。事件处理React事件是合成事件不是原生DOM事件语法为onClick驼峰命名需注意绑定this函数组件无需绑定。组件懒加载用React.lazy和Suspense实现减少首屏加载体积提升首屏加载速度。// 1. 条件渲染 const ConditionDemo () { const [isLogin, setIsLogin] useState(false); return ( div {isLogin ? p欢迎登录/p : button onClick{() setIsLogin(true)}登录/button} {isLogin p登录后可查看更多内容/p} /div ); }; // 2. 列表渲染key必加 const ListDemo () { const list [ { id: 1, name: React }, { id: 2, name: Vue }, { id: 3, name: Angular } ]; return ( ul {list.map(item ( li key{item.id}{item.name}/li // key为唯一标识推荐用后端返回的id ))} /ul ); }; // 3. 组件懒加载 import { lazy, Suspense } from react; // 懒加载组件按需加载只有当组件被渲染时才加载 const LazyComponent lazy(() import(./LazyComponent)); const LazyDemo () { return ( Suspense fallback{p加载中.../p} LazyComponent / /Suspense ); };面试考点key 的作用是什么标准答案1. key 是React用于识别列表中唯一的DOM元素帮助React区分不同的列表项2. 作用减少不必要的DOM操作提升列表渲染性能避免列表项渲染混乱如状态错位3. 注意key 必须是唯一的、稳定的不能用index作为key当列表项增删改时index会变化导致React误判影响性能和状态。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2443519.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!