react16版本详细解析

React v16.0

  • render 支持返回数组和字符串
  • Error Boundaries
  • createPortal
  • 支持自定义 DOM 属性
  • 减少文件体积
  • Fiber

React v16.1

  • react-call-return

React v16.2

  • Fragment

React v16.3

  • createContext
  • createRef
  • forwardRef
  • 生命周期函数的更新
  • Strict Mode

React v16.4

  • Pointer Events
  • update getDerivedStateFromProps

React v16.5

  • Profiler

React v16.6

  • memo
  • lazy
  • Suspense
  • static contextType
  • static getDerivedStateFromError()

React v16.7(~Q1 2019)

  • Hooks

React v16.8(~Q2 2019)

  • Concurrent Rendering

React v16.9(~mid 2019)

  • Suspense for Data Fetching

 

 

以下细致讲解:

React v16.0: 

  • render支持返回数组和字符串;
  • error baoundaries: 15版本渲染过程出现错误,会导致组件奔溃,错误信息不明确,16版本,如果一个错误在组件渲染或者生命周期中被抛出,整个组件结构会从根节点中卸载,不影响其他组件的渲染;

  • createPortal: 
  • 减少文件体积:使用rollup针对不同的目标格式进行代码打包,文件大小得到缩减;
  • Fiber:

v16之前,更新组件时会调用各个组件的生命周期函数,计算和对比虚拟dom,更新DOM树,这个过程是同步进行的,中途无法中断,如果组件比较庞大,更新操作耗时较长时,会导致浏览器唯一的主线程都是执行组件更新曹邹,而无法响应用户的输入或动画的渲染,很影响用户体验。

Fiber利用分片的思想,把一个耗时长的任务分成很多小片,每一个小片的运行时间很短,在每个小片执行完之后,就把控制权交还给React负责任务协调的模块,如果有紧急任务就去优先处理,如果没有就继续更新,这样唯一的线程就不会一直被独占;

因此Fiber把一个更新过程分为两个部分:

  • Render Phase : Fiber会找出需要更新的dom,这个阶段可以被打断;
  • Commit Phase:无法被打断,完成dom的更新并展示;

在使用 Fiber 后,需要要检查与第一阶段相关的生命周期函数,避免逻辑的多次或重复调用:

  • componentWillMount
  • componentWillReceiveProps
  • shouldComponentUpdate
  • componentWillUpdate

与第二阶段相关的生命周期函数:

  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount

React v16.1:

  • Call Return(react-call-return npm):提供了两个函数unstable_crateCall(父组件使用)和unstable_createReturn(子组件使用),父组件发出Call,子组件响应这个Call,即Return;
  •  父组件render函数中返回对unstable_crateCall的调用,第一个参数(props.children),第二个参数是回调,接受子组件响应Call返回的信息,第三个参数是props;
  • 在子组件render函数返回对unstable_createReturn的调用,参数是一个对象;

React v16.2:

Fragment组件:将一些子元素添加到dom tree上,切不需要提供额外的父节点,相当于render返回数组元素;

React v16.3:

  • createContext: 

   Context API 分为三部分:

  • React.createContext 是一个函数,它接收初始值并返回带有 Provider 和 Consumer 组件的对象;
  • Provider 组件是数据的发布方,一般在组件树的上层并接收一个数据的初始值;
  • Consumer 组件是数据的订阅方,它的 props.children 是一个函数,接收被发布的数据,并且返回 React Element;

  • React.createRef() : 获取ref对象
  • React.forwardRef():父组件访问子组件的ref,接受一个函数,参数为props,ref;

  • 生命周期引入getDerivedStateFromProps 、 getSnapshotBeforeUpdate 及 componentDidCatch,标记为不安全的方法:componentWillMount、componentWillReceiveProps 和 componentWillUpdate;

componentDidCatch(error, infor):让开发者自主处理错误信息;

React v16.4

Pointer Events

指针事件是为指针设备触发的 DOM 事件。它们旨在创建单个 DOM 事件模型来处理指向输入设备,例如鼠标,笔 / 触控笔或触摸(例如一个或多个手指)。指针是一个与硬件无关的设备,可以定位一组特定的屏幕坐标。拥有指针的单个事件模型可以简化创建 Web 站点和应用程序,并提供良好的用户体验,无论用户的硬件如何。但是,对于需要特定于设备的处理的场景,指针事件定义了一个 pointerType 属性,用于检查产生事件的设备类型。

React 新增 onPointerDown / onPointerMove / onPointerUp / onPointerCancel / onGotPointerCapture / onLostPointerCapture / onPointerEnter / onPointerLeave / onPointerOver / onPointerOut 等指针事件。

这些事件只能在支持 指针事件 规范的浏览器中工作。如果应用程序依赖于指针事件,建议使用第三方指针事件 polyfill。

React v16.5

Profiler

React 16.5 添加了对新的 profiler DevTools 插件的支持。这个插件使用 React 的 Profiler 实验性 API 去收集所有 component 的渲染时间,目的是为了找出 React App 的性能瓶颈,它将会和 React 即将发布的 时间片 特性完全兼容。

React v16.6

memo

React.memo() 只能作用在简单的函数组件上,本质是一个高阶函数,可以自动帮助组件执行shouldComponentUpdate(),但只是执行浅比较,其意义和价值有限。

const MemoizedComponent = React.memo(props => {
  /* 只在 props 更改的时候才会重新渲染 */
});

lazy / Suspense

React.lazy() 提供了动态 import 组件的能力,实现代码分割。

Suspense 作用是在等待组件时 suspend(暂停)渲染,并显示加载标识。

目前 React v16.6 中 Suspense 只支持一个场景,即使用 React.lazy() 和 实现的动态加载组件。

import React, {lazy, Suspense} from 'react';
const OtherComponent = lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <OtherComponent />
    </Suspense>
  );
}

static contextType

static contextType 为 Context API 提供了更加便捷的使用体验,可以通过 this.context 来访问 Context。

const MyContext = React.createContext();

class MyClass extends React.Component {
  static contextType = MyContext;
  
  componentDidMount() {
    const value = this.context;
  }
  
  componentDidUpdate() {
    const value = this.context;
  }
  
  componentWillUnmount() {
    const value = this.context;
  }
  
  render() {
    const value = this.context;
  }
}

getDerivedStateFromError

static getDerivedStateFromError(error) 允许开发者在 render 完成之前渲染 Fallback UI,该生命周期函数触发的条件是子组件抛出错误,getDerivedStateFromError 接收到这个错误参数后更新 state。

class ErrorBoundary extends React.Component {
  state = { hasError: false };
  
  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return { hasError: true };
  }
  
  componentDidCatch(error, info) {
    // You can also log the error to an error reporting service
    logErrorToMyService(error, info);
  }
  
  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }
    
    return this.props.children; 
  }
}

React v16.7(~Q1 2019)

Hooks

Hooks 要解决的是状态逻辑复用问题,且不会产生 JSX 嵌套地狱,其特性如下:

  • 多个状态不会产生嵌套,依然是平铺写法;
  • Hooks 可以引用其他 Hooks;
  • 更容易将组件的 UI 与状态分离;

Hooks 并不是通过 Proxy 或者 getters 实现,而是通过数组实现,每次 useState 都会改变下标,如果 useState 被包裹在 condition 中,那每次执行的下标就可能对不上,导致 useState 导出的 setter 更新错数据。

更多 Hooks 使用场景可以阅读下列文章:

function App() {
  const [open, setOpen] = useState(false);
  
  return (
    <>
      <Button type="primary" onClick={() => setOpen(true)}>
        Open Modal
      </Button>
      <Modal
        visible={open}
        onOk={() => setOpen(false)}
        onCancel={() => setOpen(false)}
      />
    </>
  );
}

React v16.8(~Q2 2019)

Concurrent Rendering

Concurrent Rendering 并发渲染模式是在不阻塞主线程的情况下渲染组件树,使 React 应用响应性更流畅,它允许 React 中断耗时的渲染,去处理高优先级的事件,如用户输入等,还能在高速连接时跳过不必要的加载状态,用以改善 Suspense 的用户体验。

目前 Concurrent Rendering 尚未正式发布,也没有详细相关文档,需要等待 React 团队的正式发布。

React v16.9(~mid 2019)

Suspense for Data Fetching

Suspense 通过 ComponentDidCatch 实现用同步的方式编写异步数据的请求,并且没有使用 yield / async / await,其流程:调用 render 函数 -> 发现有异步请求 -> 暂停渲染,等待异步请求结果 -> 渲染展示数据。

无论是什么异常,JavaScript 都能捕获,React就是利用了这个语言特性,通过 ComponentDidCatch 捕获了所有生命周期函数、render函数等,以及事件回调中的错误。如果有缓存则读取缓存数据,如果没有缓存,则会抛出一个异常 promise,利用异常做逻辑流控制是一种拥有较深的调用堆栈时的手段,它是在虚拟 DOM 渲染层做的暂停拦截,代码可在服务端复用。

import { fetchMovieDetails } from '../api';
import { createFetch } from '../future';

const movieDetailsFetch = createFetch(fetchMovieDetails);

function MovieDetails(props) {
  const movie = movieDetailsFetch.read(props.id);

  return (
    <div>
      <MoviePoster src={movie.poster} />
      <MovieMetrics {...movie} />
    </div>
  );
}

 


版权声明:本文为Sharon598原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。