React基础与实践 | 青训营笔记

39 阅读7分钟

React:用于构建用户界面的JavaScript库

React哲学(Thinking In React)

React是用JavaScript构建快速响应的大型Web应用程序的首选方式之一。

影响Web性能的两大主要原因:(快速响应的瓶颈

解决方法:

  1. 等待资源加载
    1. React.Lazy(动态加载)
    2. React.Suspense
    3. ErrorBoundary(资源加载失败提示)
  2. 浏览器线程执行
    1. 异步更新
    2. 时间切片
    3. React Fiber
import React, { useState, Suspense } from 'react';   // 引入React相关组件
import { Tabs } from 'antd';   // 引入Ant Design中的Tabs组件

// 定义组件
const MyTabs = ({ routes }) => {
  const [activeKey, setActiveKey] = useState('1');   // 用useState定义当前激活的Tab key

  // 组件渲染方法
  const renderTabs = () => {
    return routes.map((item) => {
      const Component = React.lazy(() => import(`./${item.key}`));   // 使用React.lazy动态加载Tab内容

      // 定义Tab内容渲染方法
      const RenderComponent = (
        <Suspense fallback={"资源加载中..."}>
          <Component />
        </Suspense>
      );
      
      // 返回一个TabPane组件
      return (
        <Tabs.TabPane key={item.key} title={item.name}>
          {RenderComponent}
        </Tabs.TabPane>
      );
    });
  };

  // 返回最终的Tabs组件
  return (
    <Tabs activeTab={activeKey} onChange={setActiveKey}>
      {renderTabs()}
    </Tabs>
  );
};

// 导出组件
export default MyTabs;

更新流程

  • Scheduler(调度器)

    • 维护时间切片(类似requestldleCallback)
    • 与浏览器任务调度
    • 优先级调度
  • Reconciler(协调器)

    • 将JSX(React中的特殊语法)转化为Fiber
    • Fiber树对比(双缓存)
    • 确定本次更新的Fiber
  • Renderer(渲染器)

    • 渲染器用于管理一棵React树,使其根据底层平台进行不同的调用。

优缺点:

优点:

  • 快速响应:Fiber
  • 组件化:复用性强
  • 声明式编程
  • 跨平台:只需修改渲染器

缺点:

  • 大型引用需要配套学习 状态管理、路由设备
  • 不适合小型应用,需要用Babel处理

React基础

Web 应用

  • 打包配置:JSX -> babel -> JS加载优化和错误降级。
  • React Router向应用中快速地添加视图和数据流,保持页面与URL间的同步。
  • 可复用UI -> 组件 -> 页面。可复用逻辑抽离成hook。
  • 多页面多组件共享信息redux & context

组件

  • 通过定义state操作视图Mount时获取数据更新stateRef保存与视图无直接关系的值unMount前清空Ref
  • props父子组件通信context & redux组件信息共享
  • 数据决定视图通过Ref获取到DOM
  • 函数使用useCallback值或者计算使用useMemo组件包裹memo

函数式组件的优点:

  • 代码量骤减,组件干净清爽
  • 没有复杂的生命周期
  • 支持自定义hook,逻辑复用方便

Hook规则 & 原理

组件和Hook的关系

  • 我们将UI拆成多个独立单元,这些单元组合可以构成多种视图展示,这些独立单元就是组件。组件相当于原子。
  • hook贴近组件内部运行的各种概念逻辑,effect、state、context等。hooks更贴切于电子。

只能在 React 函数中调用Hook

  • 在React函数组件中 或自定义Hook中调用

  • 自定义Hook必须以use开头

  • Hook中的state是完全隔离的

Hook过期闭包问题

过期闭包指的是当一个变量在创建闭包时被捕获,并且该变量在后续的执行过程中发生了改变,而闭包中仍然保存着旧值的情况。

因为因为 Hook 可以在函数组件中捕获和更新组件状态,并且每次渲染时都会重新执行,确保状态的正确性。所以使用Hook解决过期闭包问题。

如程序:

import React, { useState } from 'react';

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

  function handleClick() {
    setCount(count + 1);
  }

  return <button onClick={handleClick}>点击 {count} 次</button>;
}

由于 useState Hook 在每次渲染时都会重新执行,因此 count 变量和 setCount 函数的闭包是实时更新的,避免了过期闭包问题的出现。

React常用API及其作用

类组件基类 React.Component

  • React.Component 是 React 类组件的基类。
  • 类组件继承 React.Component 后,可以通过实现 render 方法来返回要渲染的 React 元素。

浅层对比 React.PureComponent

  • React.PureComponent 继承自 React.Component,并重写了 shouldComponentUpdate() 方法。
  • React.PureComponent 内置了浅层对比(shallow compare) props 和 state 进行性能优化。
  • 适用于组件仅依赖于 props 和 state 的情况。

高阶组件 React.memo

  • React.memo 是一个高阶组件,用于只比较 props 变更的情况下避免不必要的渲染。
  • 当前组件的 props 不变时,React.memo 可以使用上一次渲染结果缓存,从而提高性能。

创建 React 元素 React.createElement

  • React.createElement 是创建并返回 React 元素的函数,可以用于在不使用 JSX 的情况下构建 React 应用。
  • 函数的第一个参数是标签名或组件类型,第二个参数是 props,第三个参数及之后的参数是子元素。

克隆 React 元素 React.cloneElement

  • React.cloneElement 是用于克隆并返回包含新 props 的 React 元素的函数。
  • 可以通过这个函数为新元素添加额外 props 或者覆盖原有 props

遍历操作 React.Children

  • React.Children.map 用于遍历 React 子元素并返回一个新的子元素数组,可以在遍历过程中为子元素添加或修改 props
  • React.Children.forEach 用于遍历 React 子元素,不能返回新的子元素数组。
  • React.Children.count 用于获取子元素数量。
  • React.Children.only 用于判断是否只有一个子元素。

创建 ref React.createRef

  • React.createRef 用于创建一个 ref,并附加到具体元素上,在 class 组件中可以使用该 ref 获取 dom 结构。

转发 ref React.forwardRef

  • React.forwardRef 可以让组件转发 ref,使父组件可以获取子组件的 dom 元素或执行子组件的方法。
  • 可与 useImperativeHandle 配合使用。

动态加载组件 React.lazy

  • React.lazy 可以实现组件的动态加载和构建,提高应用性能。
  • 只能在函数组件中使用,不支持 SSR 和服务端渲染。

优雅降级 React.Suspense

  • React.Suspense 是一个组件,在资源未准备好时可以优雅地降级。
  • 可以用于等待代码分割、组件懒加载、异步请求等场景。

React常用Hooks及其使用

React Hooks 是 React 16.8 引入的一项功能,可以让函数组件拥有类似于 class 组件的功能。以下是 React Hooks 的常用函数:

useState

  • useState 返回一个数组 [state, setState],其中 state 表示当前状态的值,setState 是一个函数,用于更新 state 的值。

useEffect

  • useEffect 用于在函数组件中执行副作用操作,比如网络请求、订阅事件等。
  • 可以在挂载后、依赖变化时、组件卸载前等场景下执行操作。
  • 接收两个参数:第一个参数是一个函数,表示要执行的操作;第二个参数是一个数组,用于指定影响该操作的变量,若该数组为空,则表示该操作仅在挂载和卸载时执行。

useContext

  • useContext 用于接收最近的上层 context 对象,并返回其值。
  • 一般与 createContext 一起使用,可以让子组件获取到父组件传递下来的数据。

useRef

  • useRef 返回一个可变的 ref 对象,在组件生命周期内持续存在。
  • 可以用于保存任意可变值,也可以和其他 DOM API 一起使用获取 DOM 元素或者保存一些组件内的状态。

useMemo

  • useMemo 接收两个参数:第一个参数是计算函数,第二个参数是依赖数组。
  • 只有在依赖项变化时才调用计算函数,并返回计算值。
  • 可以用于优化计算量较大的操作。

useCallback

  • useCallback(fn, deps) 相当于 useMemo(() => fn, deps),一般返回的是一个新函数。
  • 可以用于避免子组件不必要地重新渲染。

useReducer

  • useReduceruseState 的更丰富替代方案。
  • 返回 [state, dispatch],其中 state 可以是复杂对象,dispatch 可以更新这个复杂对象。

useImperativeHandle

  • useImperativeHandle 可以让组件向父组件暴露方法。
  • 一般和 forwardRef 一起使用。

useLayoutEffect

  • useLayoutEffect 的执行时机是浏览器把内容真正渲染到界面之前,和 componentDidMount 等价。
  • useEffect 的执行时机不同,useEffect 的执行时机是在浏览器完成渲染之后。

具体场景案例

  • 对一个网页划分组件

    • _Layout

    • _Page

    • _Component

  • 组件间通信案例

  • 组件间共享信息

  • 组件性能优化

  • 组件挂载位置

  • 逻辑复用

总结

React是一个用于构建用户界面的JavaScript库,它通过组件化、声明式编程和虚拟DOM等特性,提供了高效的UI渲染和管理方式。React Hooks的出现使得函数组件具备类似于class组件的功能,包含了useState、useEffect、useContext、useRef、useMemo、useCallback等常用API,可以帮助开发者快速高效地实现各种功能,同时也需要注意优化组件的性能。