创建项目
npx create-react-app my-app
启动项目
npm start
目录结构
目录/文件名 | 描述 |
---|---|
README.md |
项目的自述文件 |
node_modules/ |
项目依赖包存放目录 |
package.json |
包管理配置文件,记录项目信息和依赖 |
package-lock.json |
锁定依赖版本,确保跨环境一致性 |
public/ |
公共资源目录 |
public/index.html |
主 HTML 文件,React 应用将被引入到这里 |
public/favicon.ico |
网站图标 |
public/manifest.json |
PWA 配置文件 |
src/ |
源代码目录 |
src/components |
通用功能组件 |
src/pages |
页面组件 |
src/router |
路由文件 |
src/store |
Redux 状态管理相关的代码 |
src/store/modules |
Redux 各个子模块 |
src/store/index.js |
Redux 组装各个子模块的文件 |
src/App.css |
App 组件的样式文件 |
src/App.js |
应用的主要组件 |
src/App.test.js |
App 组件的测试文件 |
src/index.css |
入口样式文件 |
src/index.js |
应用的入口文件 |
src/logo.svg |
示例 logo 文件 |
src/reportWebVitals.js |
Web Vitals 性能监测报告工具 |
src/setupTests.js |
测试环境设置文件 |
.gitignore |
Git 版本控制系统忽略文件规则 |
.eslintrc.json |
ESLint 配置文件,用于代码风格检查 |
.gitattributes |
Git 属性配置文件 |
yarn.lock |
(如果使用 Yarn)锁定依赖版本的文件 |
Virtual DOM 是什么?
虚拟 DOM 是以 JS 对象的形式模拟真实 DOM。虚拟 DOM 并不直接与用户交互,而是作为实际 DOM 的抽象和中间层存在。通过 Diff 算法对比新旧虚拟 DOM 树的差异,精确地找出哪些部分发生了变化,从而对真实 DOM 中变动的部分进行最小化更新,而非重新渲染整个页面。
什么是 Diff 算法?它是如何进行比较的?
一种用于确定并计算两个对象(通常是虚拟 DOM 树)差异的算法。其核心目的是为了高效地更新用户界面(UI)。React 的 Diff 算法专注于找出新旧虚拟 DOM 树之间的最小变化集,以便尽可能快速且高效地将这些变化应用到实际的 DOM 上,以此达到 UI 的快速更新,同时减少不必要的重渲染,提升性能。
- 分层比较(Tree Diff):首先对树进行分层遍历,而不是一次性比较整个树的所有节点。这意味着 React 只会对位于同一层级的节点进行比较,而不会跨层级直接比较。这样可以显著减少比较的复杂性。
- 同层节点遍历:在每一层,React 从左到右遍历子节点。对于每个节点,它检查新旧节点的类型是否相同。如果类型不同,React 认为这是一个重大变更,会直接替换整个子树;如果类型相同,则继续比较属性和子节点。
- 元素类型与属性比较:当节点类型相同时,React 会检查属性是否有变化。如果有属性变更,React 会尽可能地就地更新这些属性,而不是替换整个元素。
- 列表比较与 Key 的作用:对于子节点是列表的情况,React 依赖于列表中每个元素的唯一 key 属性来优化比较。通过 key,React 能快速确定哪些元素是新增的、删除的或是位置发生了变化,从而减少不必要的创建和销毁操作。没有 key 或错误使用 key 会导致 React 采取较为低效的比较策略。
- Component Diff:对于组件,React 会比较组件的类型。如果类型相同,它可能会复用已存在的组件实例并仅更新 props。如果类型不同,React 将卸载旧组件并创建新组件。
React 中 keys 的作用是什么?
key 是 React 中用于追踪哪些列表中元素被修改、删除或者被添加的辅助标识。在 Diff 算法中,key 用来判断该元素节点是被移动过来的还是新创建的元素,减少不必要的元素重复渲染。
State 和 Props 的区别是什么?
相同点:
Props 和 State 是普通的 JS 对象,它们都是用来保存信息的。
不同点:
State(状态):是组件自己管理数据、控制自己的状态,可以修改。
Props(属性): 是外部传入的数据参数,不可修改。
注意点:
没有 State 的叫做无状态组件,有 State 的叫做有状态组件。
多用 Props,少用 State,也就是多写无状态组件。
setState 是同步的还是异步的?
setState 通常是异步的,React 会将多个 setState 函数合并成一个队列,然后批量更新组件。
如果改变状态的代码处于某个 HTML 元素的事件中,则其是异步的,都则是同步。
什么是受控组件和非受控组件?
非受控组件:表单元素的值不由 React 直接管理,而是直接从 DOM 中读取,常使用 Ref 属性来访问 DOM 元素。
受控组件:通过 setState 将表单元素的值维护到了 state 中,需要时再从 state 中取出,这里的数据就受到了 state 的控制,称为受控组件。
什么是展示组件和容器组件?
展示组件(Presentational Components):通常是无状态的,接收来自容器组件的 Props。主要负责 UI 的呈现,即组件的外观和布局。它们专注于如何将数据渲染成用户可见的界面。
容器组件(Container Components):通常是有状态(state)的。负责管理数据和业务逻辑,决定组件如何运作。它们与数据源交互,如 API 调用、Redux store 等,并将数据传递给展示组件。
什么是高阶组件?
高阶组件(Higher-Order Components 简称 HOC)是一种函数,接受一个组件作为参数并返回一个新的组件。高阶组件内部可以对传入的组件进行包装,添加额外的属性(props)、状态管理、生命周期方法或者其他逻辑。用于实现组件的复用、逻辑的抽象和代码的组合。
React Hooks 是什么?
React Hooks 是 React 16.8 版本引入的一个特性,它允许你在不编写 class 组件的情况下使用 state 和其他 React 特性。Hooks 让函数组件也能拥有状态管理和生命周期方法等功能,从而简化组件结构,提高代码的可读性和可维护性。
useRef()和 React.createRef()的区别?
useRef():在每一次组件更新,再次执行 useRef 方法的时候,不会创建新的 ref 对象,获取到的还是第一次创建的 ref 对象(函数组件推荐使用)。
createRef():在每一次组件更新的时候,会创建一个新的 ref 对象,比较浪费性能(类组件推荐使用)。
常见或常用的 Hooks 有哪些?
useState():在函数组件中使用状态,修改状态值,更新视图。
useRef():在函数组件中获取 DOM 元素。
useEffect():在函数组件中执行副作用操作,如数据获取、订阅、定时器等。简单来说就是模拟类组件的生命周期。在第一个参数(回调函数)中,再 return 一个函数,可以清除副作用(比如:清除定时器)。
useMemo():在函数组件重新渲染时缓存计算结果,避免重复计算。类似于 Vue 的 computed。
useReducer():用来操作复杂的组件状态逻辑。参数分别是 reducer 函数和初始状态,根据 action.type 执行对行类型操作。
useCallback():在函数组件中缓存函数,避免函数每次渲染都创建新的函数。简单来说当父组件重新渲染的时候因为传递了函数给子组件,子组件也会跟着渲染。当使用 useCallback 包裹函数后,可以避免每次重新渲染都创建新的函数。
useContext():用于多层组件实现数据共享,无需显式地传递 props。
有待补充
React.memo()是什么?
React.memo() 是一个高阶组件,用于优化组件的渲染。当组件的 props 没有变化时,React.memo() 会阻止组件的渲染。
注意:调用 React.memo() 会返回一个新的组件。
import {
useState, memo } from "react";
// React默认渲染机制,只要父组件更新,子组件也会重新渲染
// 父组件点击按钮改变名字时,子组件也会跟着修改,但其实子组件并没有改变
// 这个时候子组件没必要重新渲染
const MemoSon = memo(function Son(props) {
console.log("Son", props);
return (
<div>
<h3>Son</h3>
</div>
);
});
function App() {
const [count, setCount] = useState(0);
const [name, setName] = useState("zhangsan");
return (
<div>
<h1>App</h1>
<MemoSon count={
count} />
<button onClick={
() => setCount