React16の新機能は何ですか?

青灯夜游
青灯夜游オリジナル
2020-12-16 11:55:062727ブラウズ

react16 の新機能: 1. render は配列と文字列を返すことをサポートします; 2. エラー境界; 3. createPortal; 4. カスタム DOM 属性をサポートします; 5. Fiber; 6. Fragment; 7. createContext; 8 、フックなど

React16の新機能は何ですか?

【関連チュートリアルの推奨事項: React ビデオ チュートリアル

React v16.0

#render は配列と文字列を返すことをサポートしています

// 不需要再将元素作为子元素装载到根元素下面
render() {
  return [
    <li/>1</li>,
    <li/>2</li>,
    <li/>3</li>,
  ];
}

エラー境界

React15 レンダリング プロセス中にランタイム エラーが発生すると、React コンポーネント全体がクラッシュします、エラー メッセージは不明瞭で読みにくいです。 React16 は、より洗練されたエラー処理戦略をサポートしています。コンポーネントのレンダリングまたはライフサイクル メソッドでエラーがスローされた場合、他のコンポーネントのレンダリングに影響を与えることなく、コンポーネント構造全体がルート ノードからアンロードされます。エラー境界を使用できます。エラーの最適化。

class ErrorBoundary extends React.Component {
  state = { hasError: false };

  componentDidCatch(error, info) {
    this.setState({ hasError: true });

    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      return <h1>数据错误</h1>;
    }
    
    return this.props.children;
  }
}
createPortal

createPortal の登場により、ドキュメント フローから分離されたポップアップ ウィンドウ、ダイアログ ボックス、その他のコンポーネントの開発が便利になります。これは、以前の不安定な API である不安定な API である不安定な API を置き換えます。

const isReact16 = ReactDOM.createPortal !== undefined;

const getCreatePortal = () =>
  isReact16
    ? ReactDOM.createPortal
    : ReactDOM.unstable_renderSubtreeIntoContainer;
createPortal を使用すると、componentDidMount、componentDidUpdate、その他のライフサイクル関数を使用せずに、Dialog コンポーネントをすばやく作成できます。

そして、createPortal によってレンダリングされた DOM を介して、ポータルの入り口からイベントが湧き出ることができ、入り口に onDialogClick などのイベントがある場合は、createPortal の DOM を呼び出すこともできます。

import React from 'react';
import { createPortal } from 'react-dom';

class Dialog extends React.Component {
  constructor() {
    super(props);

    this.node = document.createElement('div');
    document.body.appendChild(this.node);
  }

  render() {
    return createPortal(
      <div>
        {this.props.children}
      </div>,
      this.node
    );
  }
}
サポートカスタム DOM 属性

以前の React バージョンの DOM は、HTML と SVG でサポートされている属性以外の属性を認識しませんでした。React16 バージョンでは、すべての属性が DOM 要素に渡されます。この新機能により、利用可能な React DOM プロパティのホワイトリストを削除できます。著者は以前、非 DOM 属性をフィルタリングするためのメソッド filter-react-dom-props を作成しましたが、16 年後にはそのようなメソッドは必要なくなります。

ファイル サイズの削減

React16 は、ロールアップを使用して、さまざまなターゲット形式のコードをパッケージ化します。パッケージ化ツールの変更により、ライブラリ ファイルのサイズが削減されました。

    React ライブラリのサイズが 20.7kb (圧縮後 6.9kb) から 5.3kb (圧縮後 2.2kb) に縮小されました
  • ReactDOM ライブラリのサイズが 141kb (圧縮後 42.9kb) に縮小されました) から 103.7kb (圧縮後 32.6kb)
  • React ReactDOM ライブラリのサイズが 161.7kb (圧縮後 49.8kb) から 109kb (圧縮後 43.8kb) に減少しました
Fiber

Fiber は React コア アルゴリズムを再実装したもので、元の同期更新プロセスを断片化して、メイン スレッドの長期的なブロックを回避し、アプリケーションのレンダリングをよりスムーズにします。

React16 より前では、コンポーネントを更新するときに、各コンポーネントのライフサイクル関数が呼び出され、仮想 DOM が計算および比較され、DOM ツリーが更新されます。このプロセス全体は同期的に実行され、途中で中断することはできません。コンポーネントが比較的大きく、更新操作に時間がかかる場合、ブラウザの唯一のメイン スレッドがコンポーネントの更新操作を実行し、ユーザー入力やアニメーションのレンダリングに応答できなくなり、ユーザー エクスペリエンスに大きな影響を与えます。

Fiber はシャーディングのアイデアを使用して、長いタスクを多くの小さな部分に分割します。各小さな部分の実行時間は非常に短く、各小さな部分が実行された後、制御は React に戻りますタスク調整用のモジュールです。緊急のタスクがある場合は最初に処理され、そうでない場合は更新され続けます。これにより、他のタスクに実行の機会が与えられ、唯一のスレッドが常に排他的になるわけではありません。

したがって、コンポーネントを更新する場合、更新タスクが完了する前に、優先度の高い別の更新プロセスによって中断される可能性があります。優先度の高い更新タスクが最初に処理され、更新は優先度の低いタスクが最初に処理され、更新タスクによって実行された作業は完全にキャンセルされるため、再度やり直す機会を待つ必要があります。そのため、React Fiber は更新プロセスを 2 つのフェーズに分割します:

    最初のフェーズは調整フェーズです。Fiber は更新する必要がある DOM を見つけます。このフェーズは中断される可能性があります。
  • 2 番目のフェーズであるコミット フェーズは、DOM の更新と表示を完了するために中断することはできません。
Fiber を使用した後は、最初のフェーズに関連するライフ サイクル機能を確認する必要があります。ロジックの複数回または繰り返しの呼び出し:

    componentWillMount
  • componentWillReceiveProps
  • ShouldComponentUpdate
  • componentWillUpdate
(第 2 ステージあり)関連するライフサイクル関数:

    componentDidMount
  • componentDidUpdate
  • componentWillUnmount

React v16 .1

Call Return(react-call-return npm)

react-call-return は現在、主に親コンポーネント用の独立した npm パッケージです。サブコンポーネントのコールバック情報は次のとおりです。サブコンポーネント シーンによって提供されるソリューションをレンダリングするために使用されます。

React16 より前は、上記のシナリオに対して一般的に 2 つのソリューションがありました:

  • 首先让子组件初始化渲染,通过回调函数把信息传给父组件,父组件完成处理后更新子组件 props,触发子组件的第二次渲染才可以解决,子组件需要经过两次渲染周期,可能会造成渲染的抖动或闪烁等问题;
  • 首先在父组件通过 children 获得子组件并读取其信息,利用 React.cloneElement 克隆产生新元素,并将新的属性传递进去,父组件 render 返回的是克隆产生的子元素。虽然这种方法只需要使用一个生命周期,但是父组件的代码编写会比较麻烦;

React16 支持的 react-call-return,提供了两个函数 unstable_createCall 和 unstable_createReturn,其中 unstable_createCall 是 父组件使用,unstable_createReturn 是 子组件使用,父组件发出 Call,子组件响应这个 Call,即 Return。

  • 在父组件 render 函数中返回对 unstable_createCall 的调用,第一个参数是 props.children,第二个参数是一个回调函数,用于接受子组件响应 Call 所返回的信息,第三个参数是 props;
  • 在子组件 render 函数返回对 unstable_createReturn 的调用,参数是一个对象,这个对象会在unstable_createCall 第二个回调函数参数中访问到;
  • 当父组件下的所有子组件都完成渲染周期后,由于子组件返回的是对 unstable_createReturn 的调用所以并没有渲染元素,unstable_createCall 的第二个回调函数参数会被调用,这个回调函数返回的是真正渲染子组件的元素;

针对普通场景来说,react-call-return 有点过度设计的感觉,但是如果针对一些特定场景的话,它的作用还是非常明显,比如,在渲染瀑布流布局时,利用 react-call-return 可以先缓存子组件的 ReactElement,等必要的信息足够之后父组件再触发 render,完成渲染。

import React from 'react';
import { unstable_createReturn, unstable_createCall } from 'react-call-return';

const Child = (props) => {
  return unstable_createReturn({
    size: props.children.length,
    renderItem: (partSize, totalSize) => {
      return <div>{ props.children } { partSize } / { totalSize }</div>;
    }
  });
};

const Parent = (props) => {
  return (
    <div>
      {
        unstable_createCall(
          props.children,
          (props, returnValues) => {
            const totalSize = returnValues.map(v => v.size).reduce((a, b) => a + b, 0);
            return returnValues.map(({ size, renderItem }) => {
              return renderItem(size, totalSize);
            });
          },
          props
        )
      }
    </div>
  );
};

React v16.2

Fragment

Fragment 组件其作用是可以将一些子元素添加到 DOM tree 上且不需要为这些元素提供额外的父节点,相当于 render 返回数组元素。

render() {
  return (
    <Fragment>
      Some text.
      <h2>A heading</h2>
      More text.
      <h2>Another heading</h2>
      Even more text.
    </Fragment>
  );
}

React v16.3

createContext

全新的 Context API 可以很容易穿透组件而无副作用,其包含三部分:React.createContext,Provider,Consumer。

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

class ThemeProvider extends React.Component {
  state = {theme: 'light'};

  render() {
    return (
      <ThemeContext.Provider value={this.state.theme}>
        {this.props.children}
      </ThemeContext.Provider>
    );
  }
}

class ThemedButton extends React.Component {
  render() {
    return (
      <ThemeContext.Consumer>
        {theme => <Button theme={theme} />}
      </ThemeContext.Consumer>
    );
  }
}

createRef / forwardRef

React16 规范了 Ref 的获取方式,通过 React.createRef 取得 Ref 对象。

// before React 16
···

  componentDidMount() {
    const el = this.refs.myRef
  }

  render() {
    return <div ref="myRef" />
  }

···

// React 16+
  constructor(props) {
    super(props)
    
    this.myRef = React.createRef()
  }

  render() {
    return <div ref={this.myRef} />
  }
···

React.forwardRef 是 Ref 的转发, 它能够让父组件访问到子组件的 Ref,从而操作子组件的 DOM。 React.forwardRef 接收一个函数,函数参数有 props 和 ref。

const TextInput = React.forwardRef((props, ref) => (
  <input type="text" placeholder="Hello forwardRef" ref={ref} />
))

const inputRef = React.createRef()

class App extends Component {
  constructor(props) {
    super(props)
    
    this.myRef = React.createRef()
  }

  handleSubmit = event => {
    event.preventDefault()
    
    alert('input value is:' + inputRef.current.value)
  }
  
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <TextInput ref={inputRef} />
        <button type="submit">Submit</button>
      </form>
    )
  }
}

生命周期函数的更新

React16 采用了新的内核架构 Fiber,Fiber 将组件更新分为两个阶段:Render Parse 和 Commit Parse,因此 React 也引入了 getDerivedStateFromProps 、 getSnapshotBeforeUpdate 及 componentDidCatch 等三个全新的生命周期函数。同时也将 componentWillMount、componentWillReceiveProps 和 componentWillUpdate 标记为不安全的方法。

static getDerivedStateFromProps(nextProps, prevState)

getDerivedStateFromProps(nextProps, prevState) 其作用是根据传递的 props 来更新 state。它的一大特点是无副作用,由于处在 Render Phase 阶段,所以在每次的更新都会触发该函数, 在 API 设计上采用了静态方法,使其无法访问实例、无法通过 ref 访问到 DOM 对象等,保证了该函数的纯粹高效。

为了配合未来的 React 异步渲染机制,React v16.4 对 getDerivedStateFromProps 做了一些改变, 使其不仅在 props 更新时会被调用,setState 时也会被触发。

  • 如果改变 props 的同时,有副作用的产生,这时应该使用 componentDidUpdate;
  • 如果想要根据 props 计算属性,应该考虑将结果 memoization 化;
  • 如果想要根据 props 变化来重置某些状态,应该考虑使用受控组件;
static getDerivedStateFromProps(props, state) {
  if (props.value !== state.controlledValue) {
    return {
      controlledValue: props.value,
    };
  }
  
  return null;
}

getSnapshotBeforeUpdate(prevProps, prevState)

getSnapshotBeforeUpdate(prevProps, prevState) 会在组件更新之前获取一个 snapshot,并可以将计算得的值或从 DOM 得到的信息传递到 componentDidUpdate(prevProps, prevState, snapshot) 函数的第三个参数,常常用于 scroll 位置定位等场景。

componentDidCatch(error, info)

componentDidCatch 函数让开发者可以自主处理错误信息,诸如错误展示,上报错误等,用户可以创建自己的 Error Boundary 来捕获错误。

componentWillMount(nextProps, nextState)

componentWillMount 被标记为不安全,因为在 componentWillMount 中获取异步数据或进行事件订阅等操作会产生一些问题,比如无法保证在 componentWillUnmount 中取消掉相应的事件订阅,或者导致多次重复获取异步数据等问题。

componentWillReceiveProps(nextProps) / componentWillUpdate(nextProps, nextState)

componentWillReceiveProps / componentWillUpdate 被标记为不安全,主要是因为操作 props 引起的 re-render 问题,并且对 DOM 的更新操作也可能导致重新渲染。

Strict Mode

StrictMode 可以在开发阶段开启严格模式,发现应用存在的潜在问题,提升应用的健壮性,其主要能检测下列问题:

  • 识别被标志位不安全的生命周期函数
  • 对弃用的 API 进行警告
  • 探测某些产生副作用的方法
  • 检测是否使用 findDOMNode
  • 检测是否采用了老的 Context API
class App extends React.Component {
  render() {
    return (
      <div>
        <React.StrictMode>
          <ComponentA />
        </React.StrictMode>
      </div>
    )
  }
}

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

Hooks

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

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

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

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

Concurrent Rendering

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

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

React v16.9

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>
  );
}

更多编程相关知识,请访问:编程课程!!

以上がReact16の新機能は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。