函数组件和 class
组件的区别
- 纯函数,输入
props
,输出JSX
- 没有实例,没有生命周期,没有
state
- 不能扩展其他方法
什么是受控组件?
- 表单的值受
state
控制(状态驱动视图) - 需要自行监听
onChange
, 更新state
何时使用异步组件
- 加载大组件
- 路由懒加载
多个组件有公共逻辑,如何抽离
- 高阶组件(HOC)
Render Props
- mixin 已经被 React 废弃
redux 如何进行异步请求
- 使用异步
action
- 如
redux-thunk
react-router 如何配置懒加载
lazy(()=> import('component'))
1 2 3 4 5 6 7 8 9 10 11 12 13 14
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; import React, { Suspense, lazy } from 'react'; const Home = lazy(() => import('./routes/Home')); const About = lazy(() = import('-/routes/About')); const App = () => ( <Router> <React.Suspense fallback={<div>loading</div>}> <Switch> <Router exact path="/" component={Home}/> <Route path="/about" component={About}/> </Switch> </React.Suspense> </Router> );
React.PureComponent
作用
- 实现浅比较的
shouldComponentUpdate
- 性能优化
- 要结合不可变值使用
React
事件和 DOM
事件的区别
- 所有事件挂载到了
document
上 event
不是原生的,是SyntheticEvent
合成事件对象dispatchEvent
(dispatchEvent
方法接受一个事件对象作为参数,并在DOM元素上触发该事件)
React
性能优化
- 渲染列表是加
key
- 自定义事件、DOM 事件及时销毁
- 合理使用异步组件
- 减少函数 bind this 的参数
- 合理使用
SCU
PureComponent
和memo
- 合理使用
Immutable.js
- 通用的性能优化,如图片懒加载
React 和 Vue 的区别
不同点:
React
使用JSX
拥抱JS
,Vue
使用模版拥抱html
React
函数式编程,Vue
生明式编程React
更多需要自力更生,Vue
把想要的都给你
相同点:
- 都支持组件化
- 都是数据驱动视图
- 都使用
vdom
操作DOM
React Hooks 性能优化
useMemo
缓存数据useCallback
缓存函数useMemo
useCallback
相当于 class 组件中的SCU
和PureComponent
React Hooks 遇到哪些坑
useState
初始化值只能初始化一次render
:初始化state
re-render
:只恢复初始化的state
值,不会再重新设置新的值,想要修改 只能使用setState
修改
useEffect
内部不能修改state
(解决方案推荐使用useRef()
)useEffect
依赖引用类型,会出现死循环(原理是:依赖项的比较是通过Object.is({}, {})
进行比较,当前两个对象比较返回的是false
, 如果是值类型会对比值的是否相同)
React Hooks 做组件逻辑复用的点
- 完全符合
Hooks
原有规则,没有其他要求,易理解记忆 - 变量作用域很明确
- 不会产生组件嵌套的问题
JSX 本质
React.createElement
是h
函数,返回vnode
,然后通过patch
进行处理渲染- 第一个参数可能是组件,也可能是
html tag
标签。(区分组件与html写法是根据首字母的大小写,组件规定要首字母大小,html标签都是小写) - 组件名称首字母必须大写(React 规定)
1 2 3 4 5 6 7 8 9 10 11 12
// JSX 语法 const listElem = <div> <Input submitTitle={onSubmitTitle}/> <List list={list}/> </div> // 转化为`React.createElement` // const listElem = React.createElement("div", null, React.createElement(Input, { // submitTitle: onSubmitTitle // }), React.createElement(List, { // list: list // }));
子组件暴漏方法给父组件调用
React.forwardRef
提供了一个强大的方法在组件中传递ref
,forwardRef
将父组件创建的ref
引用关联到子组件中任意元素useImperativeHandle
定义要在组件中暴漏的数据和自定义方法,此方法接受两个参数:ref
和一个回调函数,回调函数返回一个对象(由父组件定义的ref
直接调用当前子组件件暴漏的方法)
使用代码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 子组件 ChildComponent.js
import React, { forwardRef, useImperativeHandle } from 'react';
const ChildComponent = forwardRef((props, ref) => {
const internalMethod = () => {
console.log("Internal method called in child component");
// 这里可以是子组件内部的逻辑
};
// 将自定义方法暴露给父组件
useImperativeHandle(ref, () => ({
callInternalMethod: internalMethod
}));
return (
<div>
{/* 子组件的其他内容 */}
</div>
);
});
export default ChildComponent;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 父组件 ParentComponent.js
import React, { useRef } from 'react';
import ChildComponent from './ChildComponent';
const ParentComponent = () => {
// 创建一个 ref
const childRef = useRef();
// 在父组件中调用子组件的自定义方法
const handleButtonClick = () => {
if (childRef.current) {
childRef.current.callInternalMethod();
}
};
return (
<div>
<ChildComponent ref={childRef} />
<button onClick={handleButtonClick}>Call Child Method</button>
</div>
);
};
export default ParentComponent;
class component
使用ref
是React.createRef()
子类组件被调用自定义方法时,默认自定义方法就可以被父组件直接调用
fiber
fiber
出现的背景,性能解决:
js
是单线程,且和DOM
渲染共用一个线程- 当组件足够复杂,组件更新时计算和渲染压力大
- DOM 操作需求(动画,鼠标拖拽)出现页面卡顿
- 将
reconciliation
阶段进行任务拆分(commit
无法拆分) DOM
需要渲染时暂停,空闲时恢复 (DOM
什么时候需要渲染是通过window.requestIdleCallback
)
fiber
是 react
核心算法的一次重新实现,将原本同步的更新过程碎片化,避免主线程长时间的阻塞。
利用分片的思想,将耗时任务分成很多小片,每个小片执行完之后,把控制权交给 react
负责协调的模块,如果有紧急任务就优先处理,没有就继续更新。
一个更新过程,两个阶段:
第一阶段:找出需要更新的 DOM
,这个阶段是可以被打断的 第二阶段:完成 DOM
的更新展示,这个阶段是不可以被打断的
useEffect 闭包陷阱
- React Hooks
useEffect
闭包陷阱
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function useEffectDemo() {
const [value, setValue] = useState(0)
useEffect(() => {
setInterval(() => {console.log(value)}, 1000)
}, [])
function clickHandler() {
setValue(value+1)
}
return (
<div>
value: {value} <button onClick={clickHandler}>increase</button>
</div>
)
}
// 输出结果为 0
useEffect
依赖项为空数组时它只会执行一次console.log(value)
获取的永远是一次渲染时初始化的state
值,value
发生变更时重新执行渲染
react 18 新特性
React 18的新特性包括:
并发渲染:React 18引入了并发渲染,这是一种新的后台机制,使React能够同时准备多个版本的UI¹²。并发渲染允许React在执行重渲染任务的过程中快速响应用户交互¹。
自动批处理:React 18引入了自动批处理,这是一种性能改进,可以使用户界面开发过程更加流畅和响应¹²。
新的API:React 18引入了一些新的API,如
createRoot
,hydrateRoot
,renderToPipeableStream
,renderToReadableStream
,以及一些新的Hooks,如useId
,useTransition
,useDeferredValue
,useSyncExternalStore
,useInsertionEffect
¹。过渡:React 18引入了
startTransition
API,这是一种新的API,可以帮助你保持应用的响应性¹²。服务端的Suspense:React 18引入了服务端的Suspense,这是一种新的架构改进,可以提高渲染性能¹²。
严格模式:在React 18中,
ReactDOM.render
和renderToString
被弃用¹。
使用createRoot
代替render
。在你的index.js
中,将ReactDOM.render
更新为ReactDOM.createRoot
来创建一个root,并使用root渲染你的应用¹。
fibar 与 react18 并发渲染 区别
React 18的并发渲染
和React Fiber
是两个不同的概念,但它们都是React
的核心部分,用于优化UI的渲染。
React Fiber是 React 的核心算法的重新实现。Fiber
引入了一种新的调度机制,将渲染工作分解成更小的单元(称为“Fiber”),并根据其重要性为每个 Fiber
分配优先级³。高优先级的 Fiber
(如用户输入和动画)会被优先处理,而低优先级的Fiber(如离屏更新)会被推迟处理,直到浏览器有空闲时间³。
React 18的并发渲染则是建立在Fiber基础之上的一种新的渲染模式。并发渲染允许 React
在执行重渲染任务的过程中快速响应用户交互。这是通过让渲染变得可中断来实现的:React
可以中断、暂停、恢复或放弃渲染。这种模式使得 React
可以同时准备多个版本的UI。
总的来说,React Fiber
是React的底层架构,用于优化渲染的调度,而React 18的并发渲染
则是在此基础上,通过允许渲染的中断和恢复,来进一步提升应用的响应性和性能
useReducer 和 redux 的区别
useReducer
是useState
的代替方案,用于state
复杂变化useReducer
是单个组件状态管理,组件通讯还需要props
redux
是全局的状态管理,多组件共享数据
为什么要使用 Hooks
- 完善函数组件的能力,函数更适合 React 组件
- 组件逻辑复用,Hooks 表现更好
- 更好的状态管理,Hooks 可以更好的管理组件状态