React进阶笔记

806 阅读12分钟

React总结

JSX和虚拟DOM

JSX 的本质是什么,它和 JS 之间到底是什么关系?

  1. javascript的语法扩展,它和模板语言很接近,但是它充分具备javascript的能力
  2. Babel是一个工具链,主要是将ecmascript2015+版本的代码转换成向后兼容的javascript语法,以便能够运行在当前和旧版本的浏览器或者环境中

为什么要用 JSX?不用会有什么后果?

JSX语法糖允许前端开发者使用我们最熟悉的类HTML标签语法来创建虚拟dom 在降低学习成本的同时 也提高了研发效率和研发体验

JSX 背后的功能模块是什么,这个功能模块都做了哪些事情?

  1. createElement有3个参数,type config 和children
  2. create Element就像开发者和React Element调用之间的一个转换器 一个数据处理层 它可以从开发者处接受相对简单的参数,然后将这些参数按照reactElement的预期做一层格式化,最终通过调用reactElement来实现元素的创建
  3. reactElement把传入的参数按照一定的规范 组装进了element对象中,并给他返回了react.createElement 最终React.CreatElement又把它交回开发者手中
  4. ReactDOM.render最终将虚拟dom转换成真实dom

虚拟dom在整个react工作流中的作用

  1. 组件在初始化时,会通过调用生命周期中的render方法,生成虚拟dom,然后再通过调用reactdom.render方法,实现虚拟dom到真实dom的转换
  2. 当组件更新时,回再次通过调用render方法生成虚拟dom,然后借助diff定位出两次虚拟dom的差异,从而针对发生变化的真实dom做定向更新

React生命周期

  1. componentWillReceiveProps并不是由props的变化触发的,而是由父组件的更新触发的
  2. getDerivedStateFromProps: 使用props来派生/更新state,在这个生命周期内部是访问不到this的,该生命周期需要一个对象格式的返回值,不可或缺,因为react需要总这个返回值来更新组件的state 该生命周期是用来代替componentWillReceiveProps
  3. getSnapshotBeforeUpdate: 执行时机是在render方法之后,真实dom更新之前。在这个阶段我们可以同时获取到更新前的真实dom和更新前后的state和props信息。该生命周期的设计初衷是,为了与componentDidUpdate一起,涵盖过时的componentWillUpdate的所有用例。

React16为什么要废弃will相关的三个生命周期

  1. 因为它们都处于render阶段 都可能重复被执行,在重复执行的过程中存在不小的风险
  2. React16改造生命周期的主要动机是为了配合fiber架构带来的异步渲染机制,针对生命周期中长期被滥用的部分推行了具有强制性的最佳实践,确保了Fiber机制下数据和视图的安全性,同时也确保了生命周期方法的行为更加纯粹,可控,可预测。

React数据在组件间流动

  1. 基于props的单向数据流:当前组件的 state 以 props 的形式流动时,只能流向组件树中比自己层级更低的组件。
  2. 父-子组件通信:父组件可以直接将 this.props 传入子组件,实现父-子间的通信
  3. 子-父组件通信:父组件传递给子组件的是一个绑定了自身上下文的函数,那么子组件在调用该函数时,就可以将想要交给父组件的数据以函数入参的形式给出去,以此来间接地实现数据从子组件到父组件的流动。
  4. 兄弟组件通信:兄弟组件之间共享了同一个父组件
  5. 发布-订阅模式驱动数据流:监听事件的位置和触发事件的位置是不受限的
    • 订阅(事件监听)--on(): 负责注册事件的监听器,指定事件触发时的回调函数
    • 发布(事件触发)--emit(): 负责触发事件,可以通过传参使其在触发的时候携带数据
    • off(): 负责监听器的清除
  6. Context API: React 官方提供的一种组件树全局通信的方式
    • React.createContext: 创建一个 context 对象
    • Provider: 数据的 Provider(提供者)
    • Consumer: 数据的消费者, 不仅能够读取到 Provider 下发的数据,还能读取到这些数据后续的更新

Redux: Redux 是 JavaScript 状态容器,它提供可预测的状态管理

  1. store: 一个单一数据源,而且是只读的
  2. action: 对变化的描述
  3. reducer: 一个纯函数,它负责对变化进行分发和处理, 最终将新的数据返回给 store
  4. 在 Redux 的整个工作过程中,数据流是严格单向的。
  5. 如果你想对数据进行修改,只有一种途径:派发 action。action 会被 reducer 读取,进而根据 action 内容的不同对数据进行修改、生成新的 state(状态),这个新的 state 会更新到 store 对象里,进而驱动视图层面做出对应的改变。
  6. Redux 通过提供一个统一的状态容器,使得数据能够自由而有序地在任意组件之间穿

类组件和函数组件的不同

  1. 类组件需要继承 class,函数组件不需要
  2. 类组件可以访问生命周期方法,函数组件不能
  3. 类组件中可以获取到实例化后的 this,并基于这个 this 做各种各样的事情,而函数组件不可以
  4. 类组件中可以定义并维护 state(状态),而函数组件不可以

hook:Hooks的本质是链表

useState:为函数组件引入状态

useEffect: 三个生命周期componentDidMount componentDidUpdate componentWillUnmount

useEffect(callBack,[])

  1. 每一次渲染后都执行的副作用: 传入回调函数, 不传依赖数组
  2. 仅在挂载阶段执行一次的副作用: 传入回调函数 且这个回调函数的返回值不是一个函数, 同时传入一个空数组
  3. 仅在挂载和卸载阶段执行的副作用: 传入回调函数 且这个函数的返回值是一个函数 同时传入一个空数组
  4. useEffect回调中返回的函数称为“清除函数”
  5. 每一次渲染都触发,且卸载阶段也触发的副作用: 传入回调函数, 且这个函数的返回值是一个函数,同时不传第二个参数
  6. 根据一定的依赖条件触发的副作用:传入回调函数 同时传入一个非空数组

为什么需要react-hooks?

  1. 告别难以理解的class
    • this和生命周期这两个痛点
  2. 解决业务逻辑难以拆分的问题
  3. 使状态逻辑复用变得简单可行
    • 之前靠的是HOC(高阶组件)和Render Props这些组件设计模式
    • 现在通过自定义Hook,达到既不破坏组织结构,又能实现逻辑复用的效果
  4. 函数组件从设计思想上来看更加契合React的理念

Hook并非万能

  1. Hooks暂时还不能完全地为函数组件补齐类组件的能力
  2. Hooks在使用层面有着严格的规则约束

Hooks的使用规则

  1. 只在react函数中调用Hook
  2. 不要在循环,条件或嵌套函数中调用Hook
    • 确保Hooks在每次渲染时都保持同样的执行顺序

虚拟DOM:本质上是JS和DOM之间的一个映射, 在形态上是一个能够描述DOM结构和其属性信息的JS对象

diff: 指的是对比两棵虚拟DOM树之间差异的过程

调和:又名“协调”,通过如ReactDom等类库使之与真实的DOM同步, 这一过程叫调和

  1. 调和不等于diff
  2. 调和是“使一致”的过程
  3. diff是“找不同”的过程

setState

  1. 批量更新:每来一个setState,就把它塞进一个队列里面攒起来,等时机成熟, 再把攒起来的state结果做合并,最后只针对最新的state值走一次更新流程
  2. 在React钩子函数及合成事件中,setState表现为异步
  3. 在setTimeout setInterval等函数中,包括在DOM原生事件中,setState表现为同步

Fiber架构:从架构角度是对React核心算法的重写 从编码角度是React内部所定义的一种数据结构 从工作流角度其保存了组件需要更新的状态和副作用

  1. Javascript是单线程的 浏览器是多线程的
  2. javascript线程和渲染线程必须是互斥的,当其中一个线程执行时,另一个线程只能挂起等待
  3. 当事件被触发时 对应的任务不会立即被执行 而是由事件线程把它添加到任务队列的末尾 等待Javascript的同步代码执行完毕后在空闲的时间里执行出列
  4. Fiber的核心: 可中断 可恢复 优先级
  5. 每个更新任务都会被赋予一个优先级

Redux是Javascript状态容器 它提供可预测的状态管理

  1. store: 它是一个单一的数据源 而且是只读的
  2. action: 它是对变化的描述
  3. reducer: 它负责对变化进行分发和处理, 最终将新的数据返回给store
  4. 工作流:想要修改数据,只能通过派发action,action会被reducer读取,reducer将根据action内容的不同执行不同的计算逻辑,最终生成新的state, 新的state回更新到DOM对象中从而驱动视图层做出对应的改变。
  5. applyMiddleware.js:中间件模块
  6. bindActionCreators.js: 用于将传入的actionCreator与dispatch相结合
  7. combineReducers.js: 将多个reducer合并起来
  8. compose.js:把接收到的函数从右向左组合
  9. createStore.js: 创建store, 它是整个流程的入口, 也是redux中最核心的api
  10. Redux工作流的核心:dispatch动作, 它刚好能把action, reducer和store这3个串联起来
  11. redux-thunk: 异步action解决方案
    • 中间件的执行时机:action被分发之后,reducer触发之前
    • 中间件的执行前提:即applyMiddleware将会对dispatch函数进行改写 使得dispatch在触发reducer之前 会首先对Redux中间件的链式调用

React-Router

  1. BrowserRouter: 路由器--根据Route定义出来的映射关系 为新的路径匹配它对应的逻辑
  2. Route: 路由--负责定义路径与组件之间的映射关系
  3. Link: 导航--负责触发路径的改变

为什么需要前端路由

前端路由可以帮助我们在仅有一个页面的情况下,“记住”用户当前走到了哪一步——为 SPA 中的各个视图匹配一个唯一标识。

原生DOM下的事件流

  1. 一个页面往往会被绑定许许多多的事件,而页面接收事件的顺序,就是事件流
  2. W3C标准约定了一个事件的传播需要经过三个阶段
    • 事件捕获阶段
    • 目标阶段
    • 事件冒泡阶段
  3. 事件委托:把多个子元素的同一类型的监听逻辑合并到父元素上通过一个监听函数来管理的行为

React事件系统

  1. React合成事件:在底层抹平了不同浏览器的差异,在上层面向开发者暴露统一的,稳定的,与DOM原生事件相同的事件接口

React性能优化

  1. 使用shouldComponentUpdate规避冗余的更新逻辑
    • React组件会根据shouldComponentUpdate的返回值,来决定是否执行该方法后的生命周期,进而决定是否进行re-render(重渲染)
  2. PureComponent + Immutable.js
    • PureComponent将会在shouldComponentUpdate中对组件更新前后的props和state进行浅比较,并根据浅比较的结果,决定是否需要继续更新流程
  3. React.memo与useMemo
    • React.memo可以实现类似shouldComponentUpdate或者PureComponent的效果
    • React.memo控制是否需要重渲染一个组件
    • useMemo控制的是是否需要重复执行某一段逻辑

React设计模式

  1. 高阶组件(HOC)
    • 高阶函数:接收函数作为输入,或者输出另一个函数的一类函数
    • 高阶组件:参数为组件,返回值为新组件的函数
  2. Render Props
    • 它强调用组件包裹函数
    • 它的载体是一个React组件
    • 它的子组件需要以函数形式存在
  3. 有状态组件和无状态组件

Hooks其他钩子

  1. useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内持续存在。
  2. useLayoutEffect:它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。
  3. useContext:接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定。
  4. useReducer:它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。
  5. useMemo和useCallback都会在组件第一次渲染的时候执行,之后会在其依赖的变量发生改变时再次执行;并且这两个hooks都返回缓存的值,useMemo返回缓存的变量,useCallback返回缓存的函数。