React的特点:
- JSX
- 它使用虚拟DOM ,减少 DOM 操作,提升性能。
- 它可以进行服务器端渲染。
- 单向数据流。
- 组件化
双向数据绑定和单向数据流区别?
单向数据流:明确的数据流向,父组件流向子组件,子组件通过事件向父组件传递信息。
双向数据流:简化开发,但是数据变化不透明,但可能导致数据流难以追踪。
Vue 3 并不像 React 那样严格遵循单向数据流的概念,而是采用了更灵活的数据流模型。虽然它支持单向数据流,但也支持双向数据绑定以及其他通信模式。
React的Suspense和Vue3的Suspense有什么区别?
目的作用相同,但写法不同。都是主要用于处理异步数据加载和懒加载组件时,UI的展示,提升用户体验。
什么是虚拟DOM?
虚拟 DOM 是一个轻量级的内存中的表示形式,用于描述真实 DOM 的结构。React 通过比较虚拟 DOM 的前后两次渲染结果来确定最小的更新集,然后批量更新真实 DOM,从而减少不必要的重渲染,提高性能。
Diff算法、key
不管是React还是Vue,都是通过diff算法对比虚拟dom树,通过比较差异,减少dom的操作。我们面试中经常会被问到react或vue中key的作用,diff算法在对比两个虚拟节点时需要判断是否为相同节点,而key就是为了方便diff明确的判断两个节点是否为同一个虚拟节点,是的话判断子节点是否有变更(有变更更新真实Dom),不是的话继续比。所以合理利用key可以有效减少真实Dom的变动,从而减少页面重绘和回流的频率,进而提高页面更新的效率。
类组件和函数组件之间的区别是什么?
类组件:功能完善,可以使用其他特性,如状态和生命周期钩子,有this。
函数组件:语法更短、更简单,只能接收props渲染到页面,且props不会改变,无state,没有this,不能使用生命周期钩子。(可用hooks解决部分问题)
Hooks是什么?
可以在不编写类的情况下使用state和其他特性,使得函数组件更灵活且强大。
常用的Hook:
useState:定义state的数据,参数是初始化的数据,返回值有两个值:1. 初始化值,2. 修改的方法
useEffect:是一个副作用函数,组件更新完成后触发,可进行的操作如:数据获取、订阅或手动更改 DOM等等。它可以实现类组件中的 componentDidMount
、componentDidUpdate
和 componentWillUnmount
生命周期方法。useEffect 会返回一个函数,组件被销毁的时候触发。
useMemo:用来计算数据,返回一个结果,监听数据的变化,第二个参数就是监听的数据,具有缓存性。
用useMemo进行性能优化?
当父组件向子组件组件通信的时候,父组件中数据发生改变,更新父组件导致子组件的更新渲染,但是如果修改的数据跟子组件无关的话,更新子组件会导致子组件不必要的DOM渲染,是比较消耗性能的,这个时候我们可以使用useMemo或者memo做组件的缓存,减少子组件不必要的DOM渲染
useCallback:用于避免在每次渲染时都创建新的函数,从而提高性能。当父组件向子组件传递函数的时候,父组件的改变会导致函数的重新调用产生新的作用域,从而导致子组件的更新渲染,这个时候我们可以使用useCallback来缓存组件。
useRef:用于创建一个可变的引用对象,通常用于访问 DOM 元素或保存值。
useContext:用于在组件树中传递数据,而无需手动传递 props。
useReducer:用来弥补useState的不足,它接受一个 reducer 函数和初始状态,返回当前状态和一个 dispatch 函数,用于触发状态更新。可以把数据进行集中式的管理,单独处理数据的逻辑信息,功能类似于Redux。
使用Hook的注意事项?
- 不要在循环,条件或嵌套函数中调用 Hook,确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。
- 不要在普通的 JavaScript 函数中调用 Hook,只在 React 函数中或自定义 Hook 中调用 Hook。
React 18 的新特性
-
Concurrent Mode (并发模式):
- 可中断的渲染:React 可以在浏览器空闲时进行渲染,这意味着渲染任务可以被打断,并在稍后继续。
- 渐进渲染:React 可以逐步更新用户界面,而不是一次性更新整个页面。
- 交互性:即使在渲染过程中,用户也可以与应用程序交互,React 会优先处理用户的交互。
- 错误恢复:React 可以在遇到错误时恢复到已知的稳定状态,而不是让整个应用程序崩溃。
- Suspense:
Suspense
是与并发模式紧密相关的一个概念,它允许你优雅地处理异步数据加载和组件加载过程中的等待状态。
-
Suspense:
- Suspense 是 React 18 中的一个重要特性,用于优雅地处理异步数据加载和组件加载过程中的等待状态。
- Suspense 可以让你定义加载中的 UI,并在数据加载完成之前展示这个 UI。
-
Start Transition:
startTransition
是 React 18 中的一个新 API,用于将一系列状态更新打包成一个批处理,从而减少不必要的重渲染。 -
Strict Mode:
Strict Mode 在 React 18 中得到进一步增强,用于帮助开发者发现潜在的问题,并确保应用程序的健壮性。
-
Server Components:
Server Components 允许在服务器端渲染组件,从而改善首屏加载时间和 SEO。
-
Optimistic Updates:
Optimistic Updates 允许开发者在数据实际更新之前就先展示更新后的状态,从而提升用户体验。
state和props的区别?
- props:是 React 组件的属性,由父组件传递数据给子组件。它们是只读的,不能在组件内部被改变。
- state:是组件内部的数据,可以被组件自身改变。当 state 改变时,组件会重新渲染。
为什么不能直接更新state?
如果直接更新state,不会重新渲染组件。需要使用setState()方法更新state,它对state对象进行更新,当state改变时,组件通过重新渲染来响应。
setState()
和 forceUpdate()
setState()
:该方法将对组件 state 的更改排入队列,并通知 React 需要使用更新后的 state 重新渲染此组件及其子组件。这是用于更新用户界面以响应事件处理器和处理服务器数据的主要方式
forceUpdate()
:强制让组件重新渲染。调用 forceUpdate()
将致使组件调用 render()
方法,此操作会跳过该组件的 shouldComponentUpdate()
。但其子组件会触发正常的生命周期方法,包括 shouldComponentUpdate()
方法。(应该避免使用 forceUpdate()
,尽量在 render()
中使用 this.props
和 this.state
。)
可以把props 的值复制给 state吗?
constructor(props) {
super(props);
// 不要这样做
this.state = { color: props.color };
}
如此做毫无必要,同时还产生了 bug(更新 prop 中的 color
时,并不会影响 state),你可以直接使用 this.props.color
。
只有在你刻意忽略 prop 更新的情况下使用。此时,应将 prop 重命名为 initialColor
或 defaultColor
。必要时,你可以修改它的 key,以强制“重置”其内部 state。
如何在 React 中处理表单数据?
可以使用
useState
或者类组件中的this.state
来管理表单数据。通过将表单字段与状态绑定,可以实现双向数据绑定,从而处理用户输入。
如何在 React 中实现状态管理?
- 使用 useState 或 useReducer 来管理局部状态。
- 使用 Context API 来管理跨组件的状态。
- 使用Redux 或 MobX 来管理全局状态。
react中refs是什么?
Refs提供了一种方式,允许我们访问DOM节点或在render方法中创建的React元素。
在少数情况中,有些需求要求我们对页面的真实DOM进行直接操作,这就要求我们有直接访问真实DOM的能力,而Refs就为我们提供了这种能力。例如:
1、管理焦点,文本选择或媒体播放。2、触发强制动画。3、集成第三方DOM库
我们可以在组件添加一个ref属性来使用,该属性是一个回调函数,接收作为其第一个参数的底层DOM元素或组件挂载实例
什么是 React 的并发模式?
React 的并发模式是一个高级特性,允许 React 在浏览器空闲时进行渲染,从而提高用户体验。它通过
useTransition
和Suspense
等 API 来实现。
React 组件生命周期有哪些不同阶段?
react常用的生命周期方法有哪些?
render、constructor、componentDidMount、componentDidUpdate、componentDidUnmount
constructor:
如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。
通常,在 React 中,构造函数仅用于以下两种情况:
在
constructor()
函数中不要调用setState()
方法。如果你的组件需要使用内部 state,请直接在构造函数中为this.state
赋值初始 state:constructor(props) { super(props); // 不要在这里调用 this.setState() this.state = { counter: 0 }; this.handleClick = this.handleClick.bind(this); }
只能在构造函数中直接为
this.state
赋值。如需在其他方法中赋值,你应使用this.setState()
替代。
render:是 class 组件中唯一必须实现的方法,当 render
被调用时,它会检查 this.props
和 this.state
的变化并返回结果。(*如果 shouldComponentUpdate()
返回 false,则不会调用 render()
。)
componentDidMount:会在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,此处是实例化请求的好地方。
componentDidUpdate:会在更新后会被立即调用。首次渲染不会执行此方法。
你也可以在
componentDidUpdate()
中直接调用setState()
,但请注意它必须被包裹在一个条件语句里,正如以下的例子那样进行处理,否则会导致死循环。componentDidUpdate(prevProps) { // 典型用法(不要忘记比较 props): if (this.props.userID !== prevProps.userID) { this.fetchData(this.props.userID); } }
componentWillUnmount:会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount()
中创建的订阅等。
react不常用的生命周期方法:
shouldComponentUpdate:当 props 或 state 发生变化时,shouldComponentUpdate()
会在渲染执行之前被调用。返回值默认为 true。如果确定在 state 或 props 更新后组件不需要在重新渲染,则可以返回false,可用于提高性能。
注意!
- 首次渲染或使用
forceUpdate()
时不会调用该方法。- 不建议在
shouldComponentUpdate()
中进行深层比较或使用JSON.stringify()
。这样非常影响效率,且会损害性能。
getSnapshotBeforeUpdate:
在最近一次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)。此生命周期方法的任何返回值将作为参数传递给 componentDidUpdate()
。(少部分场景会用到,如需要以特殊方式处理滚动位置的聊天线程等。)
Error boundaries: 这其实是 React 组件,它会在其子组件树中的任何位置捕获 JavaScript 错误,并记录这些错误,展示降级 UI 而不是崩溃的组件树。Error boundaries 组件会捕获在渲染期间,在生命周期方法以及其整个树的构造函数中发生的错误。(仅使用 Error boundaries 组件来从意外异常中恢复的情况;不要将它们用于流程控制。)
static getDerivedStateFromError():
此生命周期会在后代组件抛出错误后被调用。 它将抛出的错误作为参数,并返回一个值以更新 state。会在渲染
阶段调用,因此不允许出现副作用。 如遇此类情况,请改用 componentDidCatch()
。
componentDidCatch():
此生命周期在后代组件抛出错误后被调用。会在“提交”阶段被调用,因此允许执行副作用。 它应该用于记录错误之类的情况。
static getDerivedStateFromProps():
会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null
则不更新任何内容。
后续版本过时的生命周期方法都会加上UNSAFE_的前缀,例如:
UNSAFE_componentWillMount该名称将继续使用至 React 17。可以使用 rename-unsafe-lifecycles codemod 自动更新你的组件。
UNSAFE_componentWillMount:在挂载之前被调用。它在 render()
之前调用。
UNSAFE_componentWillReceiveProps:会在已挂载的组件接收新的 props 之前被调用。UNSAFE_componentWillUpdate:
当组件收到新的 props 或 state 时,会在渲染之前调用。
什么是Redux?
JavaScript 状态容器,提供可预测化的状态管理。应用中所有的 state 都以一个对象树的形式储存在一个单一的 store 中。 惟一改变 state 的办法是触发 action,一个描述发生什么的对象。 为了描述 action 如何改变 state 树,你需要编写 reducers。如下:
import { createStore } from 'redux';
/**
* 这是一个 reducer,形式为 (state, action) => state 的纯函数。
* 描述了 action 如何把 state 转变成下一个 state。
*
* state 的形式取决于你,可以是基本类型、数组、对象、
* 甚至是 Immutable.js 生成的数据结构。惟一的要点是
* 当 state 变化时需要返回全新的对象,而不是修改传入的参数。
*
* 下面例子使用 `switch` 语句和字符串来做判断,但你可以写帮助类(helper)
* 根据不同的约定(如方法映射)来判断,只要适用你的项目即可。
*/
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
// 创建 Redux store 来存放应用的状态。
// API 是 { subscribe, dispatch, getState }。
let store = createStore(counter);
// 可以手动订阅更新,也可以事件绑定到视图层。
store.subscribe(() =>
console.log(store.getState())
);
// 改变内部 state 惟一方法是 dispatch 一个 action。
// action 可以被序列化,用日记记录和储存下来,后期还可以以回放的方式执行
store.dispatch({ type: 'INCREMENT' });
// 1
store.dispatch({ type: 'INCREMENT' });
// 2
store.dispatch({ type: 'DECREMENT' });
// 1
如何优化React的性能
- 使用 React.memo 或 shouldComponentUpdate 来避免不必要的渲染。
- 使用 useMemo 和 useCallback 来缓存计算结果和函数。
- 分割大型组件为更小的子组件。
- 使用懒加载和代码分割技术来按需加载组件。
如何在 React 中实现服务器端渲染 (SSR)?
在 React 中实现 SSR 通常涉及使用
ReactDOMServer.renderToString
方法将组件渲染为 HTML 字符串,然后将这个字符串发送到客户端,在客户端再使用ReactDOM.hydrate
方法来激活静态渲染的内容。