React哲学与特点
所有图片来自 字节前端训练营 - React 基础与实践.pdf - 飞书云文档 (feishu.cn)
此文档是对对应课程 React 基础与实践 - 掘金 (juejin.cn) 的笔记
react是使用 JavaScript 构建 快速响应 的 Web应用 的首选方式之一。
React实现快速响应的原因:
对影响浏览器性能的2个主要因素做出了解决方案:
- 等待下载资源
- 一次性请求太多资源:React.lazy
- 下载速度慢:React.Suspense
- 请求错误:ErrorBoundary
- 大部分事件下浏览器是单线程执行的
- JS执行阻塞了页面渲染:时间切片、异步更新
- 计算布局大小:React Fiber
React更新流程
React基础
- 架构的角度来看
通常使用JSX作为React描述Web标签(或组件)的方式以简化React.createElement(type, config, childrens)的写法。
但浏览器不能直接识别JSX,因此需要使用babel工具将JSX转化为JS。
此外,实现了对加载速度慢(或下载内容过多)、访问不存在资源的优化。
- 组成的角度
1.界面的展示
单纯从界面的组成:将页面拆分为若干部分用组件分别实现,再抽象出可复用的UI形成可复用组件,对于可复用的逻辑拆分为高阶组件或hook函数。
对于单页面应用SPA:React Router实现前端路由;
2.状态管理
- React Redux
- context
组件的角度:
- 状态管理
- 直接使用DOM
- 组件通信
- 性能优化
react组件的3大属性:state/ref/props
状态管理: 其中 state 存储可变状态;ref存储持久化数据(即在组件重新渲染后仍能保存的数据)(可变)(同时,修改ref的值也不会引起组件的重新渲染)
DOm的使用:ref也可以得到DOM对象,实现对DOM的直接调用
组件通信:通过 props(父子间) context redux(跨组件、跨页面)
性能优化:此处从减少不变元素的重复渲染角度出发
const MemoMyComponent = React.memo(MyComponent)
// 此时使用MemoMyComponent组件,当传入其中的props不变时,不会重新渲染
const newValue = useMemo(() => value, [dependences])
// 若dependences不变,则返回原有的值
const newFn = useCallback(() => fn, [dependences])
// 若dependences不变,则返回原有的函数
// 等价
const newFn = useMemo(() => {() => fn}, [dependences])
一个样例
import React, { useState, useCallback, memo } from 'react'
// 子组件Button,点击增加父组件的计数值,但本身 不需要 改变
// 因此,需要其传入的参数 handClick 不变
const MyButton = memo((handleClick) => {
return (
<Button onClick={handleClick}></Button>
)
})
const Counter = () => {
// 记录点击次数
const [count, onSetCount] = useState(0);
// 使用useCallback使得每次返回相同的函数(相同的对象引用)
const handleClick = useCallback(() => {
increaseCount(count => count + 1);
},[]);
return (
<div>
<span>{count}</span>
<MyButton handleClick={ handleClick }>Click</MyButton>
</div>
)
}
点击按钮,增加count的计数。
由于每次增加计数改变了state的值,导致父组件Counter的重新渲染
因此子组件MyButton也重新渲染.
为了优化不需要重新渲染的子组件MyButton,对其使用了React.memo,同时需要配合使用useMemo或useCallback使传入子组件props中的变量或函数保持不变以避免重新渲染。
即useMemo和useCallback只是用于值不变时返回原值(不变的对象引用/指针)。需要配合React.memo返回不变的依赖,实现减少布标要的渲染。
但同时也增加了创建函数或变量的次数,需要具体判断是否有效优化性能。
参考:
重新认识 React.useCallback - 掘金 (juejin.cn)
React—攻克Hooks之useMemo - 知乎 (zhihu.com)
类组件函数组件的对比
类组件:
- 继承
React.Component实现render方法 - this
- 生命周期函数
函数组件:
- 无生命周期
- 借助useHooks实现
state和在指定生命周期执行函数 - 直接返回JSX
函数组件更灵活,代码量少;可以将逻辑抽取出hook实现复用。
另:组件是对UI实现抽取复用;hook是对逻辑实现抽取复用。hook可以是组件的一部分,即粒度更小。
关于hook
原则:
- 只能在函数组件中调用hook
- 自定义hook必须以
use开头 - 必须在顶层调用hook
实际上,与react识别到hook的方式有关。
在顶层调用的原因:
react以数组的方式保存各个state,即每次重新渲染时调用useState的顺序不能改变。
关于hook的过期闭包问题:
JS的闭包是一个函数使用了其上层环境的变量
而当上层环境的变量更新,闭包没有同步更新时,就出现了过期闭包问题。
修改状态是直接对setState进行的,因此在使用useEffect时,可能出现使用旧的state值,此时将其加入依赖即可。
useEffect(() => {}, [dependences])