1、什么是React?
React是用于构建用户界面的JavaScript库,提供UI层面的解决方案。它遵循组件设计模式、声明式编程范式和函数式编程概念,使用虚拟DOM高效操作DOM,采用从高阶组件到低阶组件的单向数据流。React将界面分解为独立的小块,每个块就是组件,组件之间可以组合、嵌套构成完整页面。
2、React的特点
- 使用虚拟DOM而非真实DOM
- 支持服务器端渲染
- 遵循单向数据流或数据绑定
- JSX语法
- 声明式编程
- 组件化开发
3、React的主要优点
- 组件式开发,提高代码复用率
- 可同时在客户端和服务器端使用
- JSX提升代码可读性
- 易于与Meteor、Angular等其他框架集成
- 编写UI测试用例简单
- 单向数据流比双向绑定更安全、更快
4、React的限制
- 只是一个库,不是完整的框架
- 库体积庞大,需要时间理解
- 新手程序员学习曲线较陡
- 内联模板和JSX使编码复杂
5、什么是JSX?
JSX是JavaScript XML的简写,是React使用的一种文件格式。它结合JavaScript的表现力和类似HTML的模板语法,使HTML文件易于理解,提升应用可靠性和性能。
6、浏览器无法读取JSX的原因
浏览器只能处理JavaScript对象,无法读取常规JavaScript对象中的JSX。需要通过Babel等JSX转换器将JSX文件转换为JavaScript对象,再传给浏览器处理。
7、React中的组件
组件是React应用的构建块,是独立且可重用的代码片段,用于定义UI的一部分。组件可以是类组件或函数组件,能够维护自己的状态和生命周期。
8、render()方法的作用
每个React组件必须包含render()方法,它返回一个React元素,是原生DOM组件的表示。如需渲染多个HTML元素,必须用<form>、<group>、<div>等封闭标记组合。此函数必须保持纯净,每次调用返回相同结果。
9、Props的概念
Props是React中属性的简写,是只读组件,必须保持纯且不可变。在整个应用中从父组件传递到子组件,子组件不能将prop送回父组件,这有助于维护单向数据流,通常用于呈现动态生成的数据。
10、React中的状态
状态是React组件的核心数据来源,应尽可能简单。状态是确定组件呈现和行为的对象,与props不同,状态是可变的,用于创建动态和交互式组件,可通过this.state()访问。
11、React中的箭头函数
箭头函数(=>)是编写函数表达式的简短语法,允许正确绑定组件上下文,因为在ES6中默认不能使用自动绑定。使用高阶函数时,箭头函数非常有用。
使用优势:
- 语法简洁:比传统函数声明或表达式更简洁
- 不绑定this:箭头函数内部this与封闭词法环境的this值相同
- 不绑定arguments:从封闭词法环境中获取arguments
- 不能用作构造函数:不能使用new关键字
- 没有prototype属性
- 不支持yield关键字:不能用作生成器函数
12、高阶组件(HOC)
高阶组件是重用组件逻辑的高级方法,源于React的组件模式。HOC是自定义组件,包含另一个组件,可以接受子组件提供的任何动态数据,但不会修改或复制输入组件中的行为。HOC可视为"纯"组件。
13、HOC的应用场景
- 代码重用,逻辑和引导抽象
- 渲染劫持
- 状态抽象和控制
- Props控制
14、纯组件
纯组件是最简单、最快的组件类型,可以替换任何只有render()的组件。纯组件增强代码简单性和应用性能。
15、key的重要性
key用于识别唯一的Virtual DOM元素及其驱动UI的相应数据,通过回收DOM中当前所有元素帮助React优化渲染。key必须是唯一的数字或字符串,列表元素需要唯一key帮助React识别哪些项已改变、添加或删除,提高列表渲染性能。
16、React路由
React路由是构建在React之上的强大路由库,帮助向应用程序添加新屏幕和流,使URL与网页显示数据保持同步。它负责维护标准化结构和行为,用于开发单页Web应用,具有简单API。
17、useState返回数组的原因
由于解构赋值的原因:
- 返回数组可对数组中的变量命名,代码更干净
- 返回对象必须与返回值同名,不能重复使用
18、React懒加载实现
React 16.6之后提供React.lazy方法支持组件懒加载,配合webpack的code-splitting特性实现按需加载。
import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
19、React与Vue的区别
设计理念:
- React:倾向于函数式编程思想,推崇组件不可变性和单向数据流
- Vue:结合响应式编程和模板系统,致力于简化开发过程
组件化方式:
- React组件包含状态和行为,所有组件共享状态树
- Vue每个组件有自己的状态和行为,易于数据和行为绑定
数据驱动方式:
- React主要采用单向数据流,组件状态通过setState更新
- Vue支持双向数据绑定(v-model指令),适合表单输入场景
模板语法:
- React使用JSX,标记语言与JavaScript逻辑混写
- Vue使用基于HTML的模板语法,支持指令
生命周期:
- React:初始化、更新、卸载
- Vue:创建、挂载、更新、销毁
状态管理:
- React通过Context API或Redux、MobX等库实现
- Vue提供Vuex作为官方状态管理方案
性能优化:
- React:React.memo、shouldComponentUpdate
- Vue:keep-alive、v-if
响应式系统:
- React:通过setState和useState等API显式触发UI更新
- Vue:通过响应式系统自动追踪依赖并在数据变化时更新视图
类型支持:
- React:原生支持JavaScript,良好结合TypeScript
- Vue:Vue 3提供更好的TypeScript支持
20、React组件通信方式
父组件 => 子组件:
子组件 => 父组件:
- 回调函数
- 事件冒泡机制
- useImperativeHandle和forwardRef
兄弟组件之间:
不相关组件之间:
- Context
- 全局变量
- 观察者模式
- Redux/mobx/dva等
21、useReducer详解
useReducer是React Hooks中的函数,用于管理和更新组件状态,可视为useState的替代方案,适用于处理复杂状态逻辑。相比useState,useReducer在处理复杂状态时更有优势,允许将状态更新逻辑封装在reducer函数中,根据不同动作类型执行相应逻辑,提高代码可读性、可维护性,便于状态追踪和调试。
22、类式组件与函数式组件区别
语法和设计思想:
- 函数式组件采用函数式编程思想
- 类组件采用面向对象编程思想
生命周期和状态变量:
- 类式组件:使用state对象定义状态变量,有componmentDidMount、shouldComponentUpdate等生命周期钩子
- 函数式组件:使用useState创建状态变量,useEffect实现生命周期功能
复用性:
- 类式组件:使用HOC、render props实现逻辑复用
- 函数式组件:使用自定义hooks实现逻辑复用
注意事项:
- 避免在循环/条件判断/嵌套函数中调用hooks
- 不能在useEffect中使用useState
- 类组件不会被替换,两种方式可并存
23、setState同步与异步
React 18之前:
- 在Promise状态更新、js原生事件、setTimeout、setInterval中同步
- 在react合成事件中异步
setState的"异步"并非内部有异步代码实现,而是合成事件和钩子函数调用顺序在更新之前,导致无法立即拿到更新后的值。
React 18之后: setState都表现为异步(批处理)。
24、服务端渲染(SSR)原理
- node server接收客户端请求,根据url路径在路由表查找对应组件
- 获取需要请求的数据,将数据作为props、context或store形式传入组件
- 使用renderToString()将组件渲染为html字符串
- 输出html前将数据注入浏览器端
- 浏览器渲染和节点对比,执行组件内事件绑定和交互
- 浏览器重用服务端输出的html节点
25、常用React Hooks
- useState:定义组件State
- useReducer:管理复杂状态逻辑
- useEffect:componentDidMount、componentDidUpdate和componentWillUnmount的结合
- useLayoutEffect:在浏览器完成绘制之前同步执行
- useContext:获取和使用共享上下文
- useCallback:缓存回调函数,性能优化
- useMemo:缓存传入的props,性能优化
- useRef:获取组件真实节点,保存可变值
- useImperativeHandle:自定义暴露给父组件的实例值或方法
- useDebugValue:开发者工具显示自定义钩子标签
26、useEffect与useLayoutEffect对比
useEffect在React渲染过程中异步调用,用于绝大多数场景;useLayoutEffect在所有DOM变更之后同步调用,主要用于处理DOM操作、调整样式、避免页面闪烁。
useEffect先渲染后改变DOM,可能产生闪烁;useLayoutEffect先改变DOM后渲染,不会产生闪烁。useLayoutEffect总是比useEffect先执行。
27、state和props的区别
state:
props:
相同点:
- 都是JavaScript对象
- 都用于保存信息
- 都能触发渲染更新
区别:
- props从外部传递,state在组件内部管理
- props在组件内部不可修改,state可修改
- state多变可修改
28、虚拟DOM概念
虚拟DOM是内存中的DOM表示。React使用虚拟DOM优化DOM更新过程,通过比较新旧虚拟DOM差异,仅更新实际DOM中改变的部分,提高应用性能。
29、React生命周期方法
- componentWillMount:渲染前执行,用于根组件App级配置
- componentDidMount:第一次渲染后执行,可做AJAX请求、DOM操作、状态更新、设置事件监听器
- componentWillReceiveProps:组件接收到新状态时触发,用于子组件重新渲染
- shouldComponentUpdate:确定是否更新组件,返回false可阻止重新渲染
- componentWillUpdate:确定要更新组件前执行
- componentDidUpdate:更新DOM以响应props或state更改
- componentWillUnmount:取消网络请求,删除事件监听器
30、使用React Hooks的原因
Hooks支持提取和重用跨多个组件通用的有状态逻辑,无需承担高阶组件或渲染props的负担。可轻松操作函数组件状态,无需转换为类组件,避免使用生命周期方法。
31、父子组件useEffect执行顺序
先执行子组件再执行父组件。React保证每次运行useEffect时DOM都已更新完毕。如需父组件先执行,可考虑使用useLayoutEffect。
32、Virtual DOM详解
- 以JavaScript对象形式存在的DOM描述
- 创建目的:更好将虚拟节点渲染到页面视图
- 更新更快
- 无法直接更新HTML
- 元素更新时更新JSX
- DOM操作简单
- 内存消耗少
33、Virtual DOM工作过程
- 底层数据改变时,整个UI在Virtual DOM中重新渲染
- 计算之前DOM表示与新表示的差异
- 完成计算后,仅用实际更改内容更新real DOM
34、受控组件和非受控组件
受控组件由React控制,input等表单输入框值不存在于DOM中,而是以组件状态存在。更新值时调用setState。
非受控组件表单数据由DOM处理,使用Refs获取当前值。
35、React 18更新内容
- 并发模式
- 更新render API
- 自动批处理
- Suspense支持SSR
- startTransition
- useTransition
- useDeferredValue
- useId
- 第三方库Hook
36、React事件机制与原生DOM事件流区别
- React事件绑定到document
- 原生事件绑定到dom
- dom事件优先于document事件执行
37、Redux工作原理
Redux是React第三方状态管理库,基于状态容器概念。组件从存储中作为props接收数据,更新存储的唯一方法是发送操作到存储区,操作传递到reducer。receiver接收操作和当前状态,返回新状态,触发订阅组件重新渲染。
38、React-Router工作原理
组件类型:
依赖库history:
- BrowserHistory:支持HTML5历史记录API
- HashHistory:用于旧版Web浏览器
- MemoryHistory:用于非DOM环境
API:
- BrowserHistory:pushState、replaceState
- HashHistory:location.hash、location.replace
39、React实现keep-alive缓存效果
React Keep Alive提供组件,必须放在Provider里面,每个组件必须拥有唯一key。
40、React创建组件方法
- React.createClass
- ES6 class
- 无状态函数
41、React diff原理
- 树形结构按层级分解,只比较同级元素
- 列表结构每个单元添加唯一key属性
- 只匹配相同class的component
- 合并操作,标记dirty component重新绘制
- 选择性子树渲染
42、虚拟DOM提高性能的原因
虚拟DOM在js和真实dom间加缓存,利用dom diff算法避免不必要的dom操作,提高性能。
43、useCallback和useMemo使用场景
useCallback缓存函数体,useMemo缓存函数执行结果。
useCallback使用场景:
- 函数作为其他hook的依赖项
- 函数作为React.memo()中的组件props
useMemo使用场景:
- 每次渲染需执行复杂运算
- 变量依赖另一个变量的运算结果
44、class变为className的原因
JavaScript中class是关键字,用于定义类。JSX中用className代替,避免与JavaScript关键字冲突。
45、React Diff与Vue Diff对比
React Diff:
- 基于不同类型元素产生不同树的假设
- 开发人员通过key属性告知React稳定元素
- 深度优先遍历,分层比较节点
- 节点类型不同时替换整个子树
Vue Diff:
- 优先判断节点类型是否相同
- 处理列表时依赖key属性
- 对静态节点优化,减少重复比较
46、React合成事件
React合成事件屏蔽浏览器自带事件,解决浏览器不兼容问题。将事件绑定在document上,按照冒泡或捕获路径收集真正的事件处理函数,先处理原生事件,再冒泡到document对象。
47、ajax放置的生命周期
React类组件: 放在componentDidMount生命周期方法中
React Hooks: 放在useEffect hooks中
48、React Hooks开发注意事项
注意事项:
- 只能在React函数中使用
- 只能在函数最外层调用hook
- useState存储引用类型数据时,修改state要返回新引用
原因:
- Hooks专为函数组件逻辑复用设计
- 确保hooks调用顺序
- 引用类型不更新引用地址时,React认为未更新数据
49、React路由守卫实现
利用Route的render函数,通过判断拦截条件实现不同组件跳转,完成路由拦截。
50、React Portals应用场景
Portal提供将子节点渲染到父组件以外DOM节点的方案。
使用场景:
- 父组件有overflow:hidden或z-index样式时
- 子组件需在视觉上"跳出"容器
- 对话框、悬浮卡、提示框
标签: React,JavaScript,前端面试,React_Hooks,虚拟DOM,组件化,JSX,Redux