Skip to main content

React

React 是一个用于构建用户界面的库,核心概念是组件化设计声明式编程

React 的特点:当数据发生变化时,UI 能够自动把变化反映出来。

虚拟 DOM

虚拟 DOM(Virtual DOM),是对真实 DOM 的一个轻量级表示,保存在内存中。在每次组件更新时,React 会先生成新的虚拟 DOM 树,并与之前的虚拟 DOM 树进行 diff,只对比出变化的部分,再应用到真实 DOM 上,从而避免了大量的 DOM 操作。

以前是基于浏览器 DOM 的 API 去控制 DOM 节点的创建、修改和删除。

虚拟 DOM 的优点:

  • 跨平台
  • 处理兼容性

Diff 算法

最小化更新

找出差异后,react 会计算出需要更新的最小操作集,只更新实际 DOM 中变化的部分,而不是重绘整个页面。

批量更新

react 会批量处理多次状态更新,而不是每次状态变化就立即更新 DOM,避免频繁的更新渲染。

优先级调度

react 使用优先级调度机制,确保高优先级的更新优先执行。

比如「用户输入」比「网络请求后的渲染」优先级更高,会优先执行。

Fiber 架构

错误边界

错误边界(Error Boundary)是一个 React 组件,该组件可以捕获其子组件的错误,并渲染出备用 UI。

默认情况下,如果 React 渲染期间发生错误,整个组件树都会被卸载。例如有一个父组件,有若干个子组件,当其中一个子组件报错时,整个组件树都不会被渲染。理想情况是,其他组件正常渲染,而报错的组件被隐藏或者替换成备用 UI。

可以使用 react-error-boundary 解决这个问题。

pnpm add react-error-boundary

示例,因为子组件 Foo 报错,导致父组件的内容也无法渲染,页面空白。

Error.jsx
import { useState } from 'react';

function Foo() {
// 这里会报错,没有bar方法
bar();
return <div>foo</div>;
}

function App() {
const [count, setCount] = useState(0);

const handleClick = () => {
setCount(count + 1);
};
return (
<>
<div>{count}</div>
<button onClick={handleClick}>click</button>
<Foo />
</>
);
}

export default App;

使用react-error-boundary后,控制台依然会报错,但是页面正常渲染,错误组件会渲染出备用 UI。

ErrorBoundary.jsx
import { useState } from 'react';
import { ErrorBoundary } from 'react-error-boundary';

function Foo() {
bar();
return <div>foo</div>;
}

function App() {
const [count, setCount] = useState(0);

const handleClick = () => {
setCount(count + 1);
};
return (
<>
<div>{count}</div>
<button onClick={handleClick}>click</button>
<ErrorBoundary fallback={<div>Something went wrong</div>}>
<Foo />
</ErrorBoundary>
</>
);
}

export default App;

fallback 就是备用 UI,当子组件报错时,会渲染出这个备用 UI。

结合 TS 使用

React.FC

不推荐写 React.FC

参考

type Props = {
name?: string,
age?: number
};

// 不推荐
const App: React.FC<Props> = props => {};

// 推荐
const App = (props: Props) => {};

通过 vite 创建 react 项目

通过 vite 创建的 react 项目里,ReactDOM.createRoot(document.getElementById('root')!),这里最后的 ! 的作用是什么?

! 的作用是为了告诉 TS 编译器:document.getElementById('root') 不会是 null,不要对此产生类型错误警告。

在 TypeScript 中,! 后缀运算符被称为非空断言操作符。当应用于表达式时,它告诉编译器:「我确信这个表达式的值在这个上下文中不会是 nullundefined」。

ReactDOM.createRoot(document.getElementById('root')!) 这行代码中,document.getElementById('root') 返回的是 HTMLElement | null 类型,因为 DOM API 可能找不到与给定 ID 匹配的元素,此时会返回 null。然而在实际应用中,通常有一个 id 为 root 的 HTML 元素用于挂载 React 应用,所以「断言」这里不可能是 null

加上 ! 后,ts 编译器会忽略对这一表达式可能为 nullundefined 的检查,并假设它始终是一个非空的 HTMLElement 类型实例,这样就可以安全地调用 .createRoot() 方法。