React的特点:
- JSX
- 它使用虚拟DOM ,减少 DOM 操作,提升性能。便于和其他平台集成。
- 它可以进行服务器端渲染。
- 单向数据流。
- 组件化
双向数据绑定和单向数据流区别?
单向绑定的优点在于清晰可控,缺点则在于模板代码过多。(React)
双向绑定可以简化开发,但是数据变化不透明。(vue同时支持单向绑定和双向绑定)
什么是虚拟DOM?
虚拟DOM是真实DOM在内存中的表示,UI的表示形式保存在内存中,并且与实际的DOM同步,这是一个发生在渲染函数被调用和元素在屏幕上显示的步骤,整个过程被称为调和
React 组件生命周期有哪些不同阶段?
react常用的生命周期方法有哪些?
render、constructor、componentDidMount、componentDidUpdate、componentDidUnmount
constructor:
如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。
通常,在 React 中,构造函数仅用于以下两种情况:
- 通过给
this.state
赋值对象来初始化内部 state。- 为事件处理函数绑定实例
在
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 时,会在渲染之前调用。
为什么不能直接更新state?
如果直接更新state,不会重新渲染组件。需要使用setState()方法更新state,它对state对象进行更新,当state改变时,组件通过重新渲染来响应。
setState()
和 forceUpdate()
setState()
:该方法将对组件 state 的更改排入队列,并通知 React 需要使用更新后的 state 重新渲染此组件及其子组件。这是用于更新用户界面以响应事件处理器和处理服务器数据的主要方式
forceUpdate()
:强制让组件重新渲染。调用 forceUpdate()
将致使组件调用 render()
方法,此操作会跳过该组件的 shouldComponentUpdate()
。但其子组件会触发正常的生命周期方法,包括 shouldComponentUpdate()
方法。(应该避免使用 forceUpdate()
,尽量在 render()
中使用 this.props
和 this.state
。)
state和props的区别?
相同点:都是普通的js对象,他们包含着影响渲染输出的信息
不同点:state是组件自己管理数据,控制自己的状态,可变;props是外部传入的数据参数,可读不可变
可以把props 的值复制给 state吗?
constructor(props) {
super(props);
// 不要这样做
this.state = { color: props.color };
}
如此做毫无必要,同时还产生了 bug(更新 prop 中的 color
时,并不会影响 state),你可以直接使用 this.props.color
。
只有在你刻意忽略 prop 更新的情况下使用。此时,应将 prop 重命名为 initialColor
或 defaultColor
。必要时,你可以修改它的 key,以强制“重置”其内部 state。
类组件和函数组件之间的区别是什么?
类组件:功能完善,可以使用其他特性,如状态和生命周期钩子,有this。
函数组件:语法更短、更简单,只能接收props渲染到页面,且props不会改变,无state,没有this,不能使用生命周期钩子。(可用hooks解决部分问题)
Hooks是什么?
Hooks是 React 16.8 中的新添加内容。它们允许在不编写类的情况下使用state和其他 React 特性。使用 Hooks,可以从组件中提取有状态逻辑,这样就可以独立地测试和重用它。Hooks 允许咱们在不改变组件层次结构的情况下重用有状态逻辑,这样在许多组件之间或与社区共享 Hooks 变得很容易
hooks使得函数组件中也可以使用state和生命周期。
useEffect
Hook 可以看做是 componentDidMount
,componentDidUpdate
和 componentWillUnmount
这三个函数的组合。
常用的hooks:
useState:定义state的数据,参数是初始化的数据,返回值两个值1. 初始化值,2. 修改的方法
useEffect:是一个副作用函数,组件更新完成后触发的函数。在useEffect 返回一个函数,组件被销毁的时候触发。
useMemo:用来计算数据,返回一个结果,监听数据的变化,第二个参数就是监听的数据,具有缓存性。
用useMemo进行性能优化?
当父组件向子组件组件通信的时候,父组件中数据发生改变,更新父组件导致子组件的更新渲染,但是如果修改的数据跟子组件无关的话,更新子组件会导致子组件不必要的DOM渲染,是比较消耗性能的,这个时候我们可以使用useMemo或者memo做组件的缓存,减少子组件不必要的DOM渲染
useCallback:当父组件向子组件传递函数的时候,父组件的改变会导致函数的重新调用产生新的作用域,所以还是会导致子组件的更新渲染,这个时候我们可以使用useCallback来缓存组件
useRef:相当于createRef的使用,创建组件的属性信息
useContext:相当在函数组件中获取context状态数的内容信息
useReducer:用来弥补useState的补不足, 可以把数据进行集中式的管理,单独处理数据的逻辑信息
使用Hooks的注意事项?
- 不要在循环,条件或嵌套函数中调用 Hook,确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。
- 不要在普通的 JavaScript 函数中调用 Hook,只在 React 函数中或自定义 Hook 中调用 Hook。
react中refs是什么?
Refs提供了一种方式,允许我们访问DOM节点或在render方法中创建的React元素。
在少数情况中,有些需求要求我们对页面的真实DOM进行直接操作,这就要求我们有直接访问真实DOM的能力,而Refs就为我们提供了这种能力。例如:
1、管理焦点,文本选择或媒体播放。2、触发强制动画。3、集成第三方DOM库
我们可以在组件添加一个ref属性来使用,该属性是一个回调函数,接收作为其第一个参数的底层DOM元素或组件挂载实例
什么是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