「这是我参与11月更文挑战的第24天,活动详情查看:2021最后一次更文挑战」
Hope is a good thing, maybe the best of things. And no good thing ever dies.
核心概念
-
JSX 语法 是一个
JavaScript的语法扩展,React语法糖。Babel会把JSX转译成一个名为React.createElement()函数调用。 -
组件
- class组件
- React.Component或者React. PureComponent 创建
- 可以使用生命周期函数
- handle事件(自定义的方法)需要绑定this,否则this就是undefined(class 类不管是原型方法还是静态方法定义,“this”值在被调用的函数内部将为 undefined)
- React.Component或者React. PureComponent 创建
- constructor
- 定义state
- 调用super(props),组件使用props
- 可以使用生命周期函数
- this绑定
- handle事件(自定义的方法)需要绑定this,否则this就是undefined
- class 类不管是原型方法还是静态方法定义,“this”值在被调用的函数内部将为 undefined
- 通常在在constructor中进行绑定this
- 可以使用this.state和this.props
- constructor
- 函数式组件
- 无生命周期函数
- 使用useState()钩子定义state
- 无this
- 经过Babel翻译的结果
- Babel是严格模式‘use strict’下进行的,不允许函数里的this指向window,所以这里指向undefined
- class组件
-
state
- 组件本身的状态
- state变化会触发组件重新渲染
-
props
- 组件的“只读”属性
- 组件的props发生变化触发组件重新渲染
-
生命周期(Class组件)
- 废弃组件生命周期图谱
- componentWillUpdate()
- 可以使用 UNSAFE_componentWillUpdate()
- componentWillReceiveProps()
- 可以使用 UNSAFE_componentWillReceiveProps()
- componentWillMount()
- 可以使用 UNSAFE_componentWillMount()
- componentWillUpdate()
- 挂载
- constructor()
- constructor(props) {
super(props); // 不要在这里调用 this.setState() this.state = { counter: 0 }; this.handleClick = this.handleClick.bind(this); } - 在 React 组件挂载之前,会调用它的构造函数
- 通过给 this.state 赋值对象来初始化内部 state
- 为事件处理函数绑定实例
- constructor(props) {
- static getDerivedStateFromProps()
- static getDerivedStateFromProps(props, state)
- 此方法使用较少,无法获取this示例,在render()前调用
- 在组件初始化和后续的更新过程中都会被调用
- render()
- 纯函数,执行渲染
- 如果 shouldComponentUpdate() 返回 false,则不会调用 render()
- componentDidMount()
- 组件挂载后(插入 DOM 树中)立即调用
- 获取网络数据
- 添加订阅
- constructor()
- 更新
- static getDerivedStateFromProps()
- static getDerivedStateFromProps(props, state)
- 此方法使用较少,无法获取this示例,在render()前调用
- 在组件初始化和后续的更新过程中都会被调用
- shouldComponentUpdate()
- shouldComponentUpdate(nextProps, nextState),返回Boolean值
- 当 props 或 state 发生变化时,在执行渲染前调用。默认返回true
- 作为性能优化的方式,但是尽量少用,去使用PureComponent组件替代
- render()
- 纯函数,执行渲染
- 如果 shouldComponentUpdate() 返回 false,则不会调用 render()
- getSnapshotBeforeUpdate()
- getSnapshotBeforeUpdate(prevProps, prevState)
- 此方法使用很少,在组件最近一次渲染前调用
- 应返回 snapshot 的值(或 null),作为componentDidUpdate(prevProps, prevState, snapshot)的入参
- 组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)
- 回值将作为参数传递给 componentDidUpdate()
- componentDidUpdate()
componentDidUpdate(prevProps) { // 典型用法(不要忘记比较 props): if (this.props.userID !== prevProps.userID) { this.fetchData(this.props.userID); } }- 更新后会被立即调用。首次渲染不会执行此方法
- 组件更新后,可以进行DOM操作
- 调用setState(),需要用条件语句包裹,否则死循环
- static getDerivedStateFromProps()
- 卸载
- componentWillUnmount()
- 组件卸载及销毁之前直接调用
- 清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅
- 不应调用 setState(),因为该组件将永远不会重新渲染
- componentWillUnmount()
- 废弃组件生命周期图谱
高级指南
- Context
- Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法
- 通常将UI主题、用户信息等一些组件公用数据放到context中,作为组件的共享数据
- 关键API
- React.createContext
- Context.Provider
- Context.Consumer
- Refs
- Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素
- 提供了非props的父组件和子组件交互方式,可以直接在父组件中修改子组件实例
- 通过 .current去访问ref属性
- class组件
- React.createRef()
- 函数式组件
- 函数组件没有实例,不能直接使用Ref,可以通过forwardRef()
- 内部使用 useRef()
- ref转发的过程
- 调用 React.createRef 创建了一个 React ref 并将其赋值给 ref 变量
- 指定 ref 为 JSX 属性,将其向下传递给
<FancyButton ref={ref}> - React 传递 ref 给 forwardRef 内函数
(props, ref) => ...,作为其第二个参数 - 向下转发该 ref 参数到
<button ref={ref}>,将其指定为 JSX 属性 - 当 ref 挂载完成,ref.current 将指向
<button>DOM 节点
- 高阶组件(HOC)
- 高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧
- 高阶组件是参数为组件,返回值为新组件的函数
- 因为Ref不是组件的props,透传 Ref 需要通过 React.forwardRef()
- diff算法
- Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计
- tree diff
- React 通过 updateDepth 对 Virtual DOM 树进行层级控制
- 只比较同一层级的节点,即同一层的所有子节点
- tree diff
- 拥有相同类的两个组件将会生成相似的树形结构,拥有不同类的两个组件将会生成不同的树形结构
- component diff
- 对于同一层级的一组子节点,它们可以通过唯一 id 进行区分
- element diff
- Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计
- 虚拟DOM
- Fiber
- render/reconciliation,可以中断
- fiber tree
- workInProgress tree
- 调度器(Scheduler)React Fiber 与浏览器的核心交互流程
- 优先级
- synchronous,与之前的Stack Reconciler操作一样,同步执行
- 首屏(首次渲染)
- task,在next tick之前执行
- animation,下一帧之前执行
- requestAnimationFrame
- high,在不久的将来立即执行
- requestIdleCallback
- low,稍微延迟执行也没关系
- requestIdleCallback
- offscreen,下一次render时或scroll时才执行
- requestIdleCallback
- synchronous,与之前的Stack Reconciler操作一样,同步执行
- 优先级
- 生命周期
- componentWillMount(已废弃)
- componentWillReceiveProps(已废弃)
- getDerivedStateFromProps
- shouldComponentUpdate
- componentWillUpdate (已废弃)
- fiber 如何中断/断点恢复
- 中断:检查当前正在处理的工作单元,保存当前成果(firstEffect, lastEffect),修改 tag 标记一下,迅速收尾并再开一个requestIdleCallback,下次有机会再做
- 断点恢复:下次再处理到该工作单元时,看 tag 是被打断的任务,接着做未完成的部分或者重做
- commit 批量更新差异点 (effect list),不能被打断
- 生命周期
- componentDidMount
- componentDidUpdate
- componentWillUnmount
- 生命周期
- render/reconciliation,可以中断
- 合成事件
- 事件注册React 事件原理
- document注册事件
- 在组件挂载阶段,react 根据组件本身声明的事件类型(onclick、onchange、等等),通过 addEventListener 给 document 添加事件监听,然后指定统一的回调函数 dispatchEvent,同一事件不论注册几次,最终只会保留一个有效实例,从而减少内存开销。
- 存储事件回调
- React 会把组件内所有的事件统一存放到一个对象中(listenerBank),通过事件类型进行分类,回调函数的存储采用键值对(key/value)的方式,key 是组件的唯一标识 id,value 对应的就是事件的回调函数。
- 事件分发
- React 的事件触发只会发生在 DOM 事件流的冒泡阶段,因为在 document 上注册时就默认是在冒泡阶段被触发执行
- 事件注册React 事件原理
Hook
- 解决的问题
- 组件之间的复用状态逻辑
- 组件的复杂度高,难以拆分为小的颗粒
- 常用的hook
- useState常用的React Hook
- useContext
- 接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值
- useEffect
- 执行一个带有副作用的函数
- 在浏览器完成布局与绘制之后,在一个延迟事件中被调用,但是它总会在任何新的渲染前执行
- 组件卸载的时候需要清除一些副作用,useEffect 函数只需返回一个清除函数即可
- useLayoutEffect
- 在所有的 DOM 布局完成之后同步调用 effect触发重新渲染。通常用来获取元素的位置,比如scroll的值
- useCallback
- useMemo
- useRef
- useReducer
- 类似redux的reducer
- useImperativeHandle
- useDebugValue
结语
如果这篇文章帮到了你,欢迎点赞👍和关注⭐️。
文章如有错误之处,希望在评论区指正🙏🙏
欢迎关注我的微信公众号,一起交流技术,微信搜索 🔍 :「 五十年以后 」