溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

react16有哪些新特性

發(fā)布時間:2020-12-17 09:53:59 來源:億速云 閱讀:257 作者:小新 欄目:web開發(fā)

這篇文章主要介紹react16有哪些新特性,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

react16新特性:1、render支持返回數(shù)組和字符串;2、錯誤邊界;3、createPortal;4、支持自定義DOM屬性;5、Fiber;6、Fragment;7、createContext;8、Hooks等等。

React v16.0

render 支持返回數(shù)組和字符串
// 不需要再將元素作為子元素裝載到根元素下面
render() {
  return [
    <li/>1</li>,
    <li/>2</li>,
    <li/>3</li>,
  ];
}
錯誤邊界

React15 在渲染過程中遇到運行時的錯誤,會導致整個 React 組件的崩潰,而且錯誤信息不明確可讀性差。React16 支持了更優(yōu)雅的錯誤處理策略,如果一個錯誤是在組件的渲染或者生命周期方法中被拋出,整個組件結(jié)構(gòu)就會從根節(jié)點中卸載,而不影響其他組件的渲染,可以利用 error boundaries 進行錯誤的優(yōu)化處理。

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

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

    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      return <h2>數(shù)據(jù)錯誤</h2>;
    }
    
    return this.props.children;
  }
}
createPortal

createPortal 的出現(xiàn)為 彈窗、對話框 等脫離文檔流的組件開發(fā)提供了便利,替換了之前不穩(wěn)定的 API unstable_renderSubtreeIntoContainer,在代碼使用上可以做兼容,如:

const isReact16 = ReactDOM.createPortal !== undefined;

const getCreatePortal = () =>
  isReact16
    ? ReactDOM.createPortal
    : ReactDOM.unstable_renderSubtreeIntoContainer;

使用 createPortal 可以快速創(chuàng)建 Dialog 組件,且不需要牽扯到 componentDidMount、componentDidUpdate 等生命周期函數(shù)。

并且通過 createPortal 渲染的 DOM,事件可以從 portal 的入口端冒泡上來,如果入口端存在 onDialogClick 等事件,createPortal 中的 DOM 也能夠被調(diào)用到。

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 使用 Rollup 針對不同的目標格式進行代碼打包,由于打包工具的改變使得庫文件大小得到縮減。

  • 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 核心算法的一次重新實現(xiàn),將原本的同步更新過程碎片化,避免主線程的長時間阻塞,使應用的渲染更加流暢。

在 React16 之前,更新組件時會調(diào)用各個組件的生命周期函數(shù),計算和比對 Virtual DOM,更新 DOM 樹等,這整個過程是同步進行的,中途無法中斷。當組件比較龐大,更新操作耗時較長時,就會導致瀏覽器唯一的主線程都是執(zhí)行組件更新操作,而無法響應用戶的輸入或動畫的渲染,很影響用戶體驗。

Fiber 利用分片的思想,把一個耗時長的任務分成很多小片,每一個小片的運行時間很短,在每個小片執(zhí)行完之后,就把控制權(quán)交還給 React 負責任務協(xié)調(diào)的模塊,如果有緊急任務就去優(yōu)先處理,如果沒有就繼續(xù)更新,這樣就給其他任務一個執(zhí)行的機會,唯一的線程就不會一直被獨占。

因此,在組件更新時有可能一個更新任務還沒有完成,就被另一個更高優(yōu)先級的更新過程打斷,優(yōu)先級高的更新任務會優(yōu)先處理完,而低優(yōu)先級更新任務所做的工作則會完全作廢,然后等待機會重頭再來。所以 React Fiber 把一個更新過程分為兩個階段:

  • 第一個階段 Reconciliation Phase,F(xiàn)iber 會找出需要更新的 DOM,這個階段是可以被打斷的;

  • 第二個階段 Commit Phase,是無法別打斷,完成 DOM 的更新并展示;

在使用 Fiber 后,需要要檢查與第一階段相關(guān)的生命周期函數(shù),避免邏輯的多次或重復調(diào)用:

  • componentWillMount

  • componentWillReceiveProps

  • shouldComponentUpdate

  • componentWillUpdate

與第二階段相關(guān)的生命周期函數(shù):

  • componentDidMount

  • componentDidUpdate

  • componentWillUnmount

React v16.1

Call Return(react-call-return npm)

react-call-return 目前還是一個獨立的 npm 包,主要是針對 父組件需要根據(jù)子組件的回調(diào)信息去渲染子組件場景 提供的解決方案。

在 React16 之前,針對上述場景一般有兩個解決方案:

  • 首先讓子組件初始化渲染,通過回調(diào)函數(shù)把信息傳給父組件,父組件完成處理后更新子組件 props,觸發(fā)子組件的第二次渲染才可以解決,子組件需要經(jīng)過兩次渲染周期,可能會造成渲染的抖動或閃爍等問題;

  • 首先在父組件通過 children 獲得子組件并讀取其信息,利用 React.cloneElement 克隆產(chǎn)生新元素,并將新的屬性傳遞進去,父組件 render 返回的是克隆產(chǎn)生的子元素。雖然這種方法只需要使用一個生命周期,但是父組件的代碼編寫會比較麻煩;

React16 支持的 react-call-return,提供了兩個函數(shù) unstable_createCall 和 unstable_createReturn,其中 unstable_createCall 是 父組件使用,unstable_createReturn 是 子組件使用,父組件發(fā)出 Call,子組件響應這個 Call,即 Return。

  • 在父組件 render 函數(shù)中返回對 unstable_createCall 的調(diào)用,第一個參數(shù)是 props.children,第二個參數(shù)是一個回調(diào)函數(shù),用于接受子組件響應 Call 所返回的信息,第三個參數(shù)是 props;

  • 在子組件 render 函數(shù)返回對 unstable_createReturn 的調(diào)用,參數(shù)是一個對象,這個對象會在unstable_createCall 第二個回調(diào)函數(shù)參數(shù)中訪問到;

  • 當父組件下的所有子組件都完成渲染周期后,由于子組件返回的是對 unstable_createReturn 的調(diào)用所以并沒有渲染元素,unstable_createCall 的第二個回調(diào)函數(shù)參數(shù)會被調(diào)用,這個回調(diào)函數(shù)返回的是真正渲染子組件的元素;

針對普通場景來說,react-call-return 有點過度設(shè)計的感覺,但是如果針對一些特定場景的話,它的作用還是非常明顯,比如,在渲染瀑布流布局時,利用 react-call-return 可以先緩存子組件的 ReactElement,等必要的信息足夠之后父組件再觸發(fā) 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 上且不需要為這些元素提供額外的父節(jié)點,相當于 render 返回數(shù)組元素。

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

React v16.3

createContext

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

  • React.createContext 是一個函數(shù),它接收初始值并返回帶有 Provider 和 Consumer 組件的對象;

  • Provider 組件是數(shù)據(jù)的發(fā)布方,一般在組件樹的上層并接收一個數(shù)據(jù)的初始值;

  • Consumer 組件是數(shù)據(jù)的訂閱方,它的 props.children 是一個函數(shù),接收被發(fā)布的數(shù)據(jù),并且返回 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 規(guī)范了 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 的轉(zhuǎn)發(fā), 它能夠讓父組件訪問到子組件的 Ref,從而操作子組件的 DOM。 React.forwardRef 接收一個函數(shù),函數(shù)參數(shù)有 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>
    )
  }
}

生命周期函數(shù)的更新

React16 采用了新的內(nèi)核架構(gòu) Fiber,F(xiàn)iber 將組件更新分為兩個階段:Render Parse 和 Commit Parse,因此 React 也引入了 getDerivedStateFromProps 、 getSnapshotBeforeUpdate 及 componentDidCatch 等三個全新的生命周期函數(shù)。同時也將 componentWillMount、componentWillReceiveProps 和 componentWillUpdate 標記為不安全的方法。

static getDerivedStateFromProps(nextProps, prevState)

getDerivedStateFromProps(nextProps, prevState) 其作用是根據(jù)傳遞的 props 來更新 state。它的一大特點是無副作用,由于處在 Render Phase 階段,所以在每次的更新都會觸發(fā)該函數(shù), 在 API 設(shè)計上采用了靜態(tài)方法,使其無法訪問實例、無法通過 ref 訪問到 DOM 對象等,保證了該函數(shù)的純粹高效。

為了配合未來的 React 異步渲染機制,React v16.4 對 getDerivedStateFromProps 做了一些改變, 使其不僅在 props 更新時會被調(diào)用,setState 時也會被觸發(fā)。

  • 如果改變 props 的同時,有副作用的產(chǎn)生,這時應該使用 componentDidUpdate;

  • 如果想要根據(jù) props 計算屬性,應該考慮將結(jié)果 memoization 化;

  • 如果想要根據(jù) props 變化來重置某些狀態(tài),應該考慮使用受控組件;

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) 函數(shù)的第三個參數(shù),常常用于 scroll 位置定位等場景。

componentDidCatch(error, info)

componentDidCatch 函數(shù)讓開發(fā)者可以自主處理錯誤信息,諸如錯誤展示,上報錯誤等,用戶可以創(chuàng)建自己的 Error Boundary 來捕獲錯誤。

componentWillMount(nextProps, nextState)

componentWillMount 被標記為不安全,因為在 componentWillMount 中獲取異步數(shù)據(jù)或進行事件訂閱等操作會產(chǎn)生一些問題,比如無法保證在 componentWillUnmount 中取消掉相應的事件訂閱,或者導致多次重復獲取異步數(shù)據(jù)等問題。

componentWillReceiveProps(nextProps) / componentWillUpdate(nextProps, nextState)

componentWillReceiveProps / componentWillUpdate 被標記為不安全,主要是因為操作 props 引起的 re-render 問題,并且對 DOM 的更新操作也可能導致重新渲染。

Strict Mode

StrictMode 可以在開發(fā)階段開啟嚴格模式,發(fā)現(xiàn)應用存在的潛在問題,提升應用的健壯性,其主要能檢測下列問題:

  • 識別被標志位不安全的生命周期函數(shù)

  • 對棄用的 API 進行警告

  • 探測某些產(chǎn)生副作用的方法

  • 檢測是否使用 findDOMNode

  • 檢測是否采用了老的 Context API

class App extends React.Component {
  render() {
    return (
      <div>
        <React.StrictMode>
          <ComponentA />
        </React.StrictMode>
      </div>
    )
  }
}

React v16.4

Pointer Events

指針事件是為指針設(shè)備觸發(fā)的 DOM 事件。它們旨在創(chuàng)建單個 DOM 事件模型來處理指向輸入設(shè)備,例如鼠標,筆 / 觸控筆或觸摸(例如一個或多個手指)。指針是一個與硬件無關(guān)的設(shè)備,可以定位一組特定的屏幕坐標。擁有指針的單個事件模型可以簡化創(chuàng)建 Web 站點和應用程序,并提供良好的用戶體驗,無論用戶的硬件如何。但是,對于需要特定于設(shè)備的處理的場景,指針事件定義了一個 pointerType 屬性,用于檢查產(chǎn)生事件的設(shè)備類型。

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

這些事件只能在支持 指針事件 規(guī)范的瀏覽器中工作。如果應用程序依賴于指針事件,建議使用第三方指針事件 polyfill。

React v16.5

Profiler

React 16.5 添加了對新的 profiler DevTools 插件的支持。這個插件使用 React 的 Profiler 實驗性 API 去收集所有 component 的渲染時間,目的是為了找出 React App 的性能瓶頸,它將會和 React 即將發(fā)布的 時間片 特性完全兼容。

React v16.6

memo

React.memo() 只能作用在簡單的函數(shù)組件上,本質(zhì)是一個高階函數(shù),可以自動幫助組件執(zhí)行shouldComponentUpdate(),但只是執(zhí)行淺比較,其意義和價值有限。

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

lazy / Suspense

React.lazy() 提供了動態(tài) import 組件的能力,實現(xiàn)代碼分割。

Suspense 作用是在等待組件時 suspend(暫停)渲染,并顯示加載標識。

目前 React v16.6 中 Suspense 只支持一個場景,即使用 React.lazy() 和 <React.Suspense> 實現(xiàn)的動態(tài)加載組件。

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) 允許開發(fā)者在 render 完成之前渲染 Fallback UI,該生命周期函數(shù)觸發(fā)的條件是子組件拋出錯誤,getDerivedStateFromError 接收到這個錯誤參數(shù)后更新 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 <h2>Something went wrong.</h2>;
    }
    
    return this.props.children; 
  }
}

React v16.7(~Q1 2019)

Hooks

Hooks 要解決的是狀態(tài)邏輯復用問題,且不會產(chǎn)生 JSX 嵌套地獄,其特性如下:

  • 多個狀態(tài)不會產(chǎn)生嵌套,依然是平鋪寫法;

  • Hooks 可以引用其他 Hooks;

  • 更容易將組件的 UI 與狀態(tài)分離;

Hooks 并不是通過 Proxy 或者 getters 實現(xiàn),而是通過數(shù)組實現(xiàn),每次 useState 都會改變下標,如果 useState 被包裹在 condition 中,那每次執(zhí)行的下標就可能對不上,導致 useState 導出的 setter 更新錯數(shù)據(jù)。

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 并發(fā)渲染模式是在不阻塞主線程的情況下渲染組件樹,使 React 應用響應性更流暢,它允許 React 中斷耗時的渲染,去處理高優(yōu)先級的事件,如用戶輸入等,還能在高速連接時跳過不必要的加載狀態(tài),用以改善 Suspense 的用戶體驗。

目前 Concurrent Rendering 尚未正式發(fā)布,也沒有詳細相關(guān)文檔,需要等待 React 團隊的正式發(fā)布。

React v16.9(~mid 2019)

Suspense for Data Fetching

Suspense 通過 ComponentDidCatch 實現(xiàn)用同步的方式編寫異步數(shù)據(jù)的請求,并且沒有使用 yield / async / await,其流程:調(diào)用 render 函數(shù) -> 發(fā)現(xiàn)有異步請求 -> 暫停渲染,等待異步請求結(jié)果 -> 渲染展示數(shù)據(jù)。

無論是什么異常,JavaScript 都能捕獲,React就是利用了這個語言特性,通過 ComponentDidCatch 捕獲了所有生命周期函數(shù)、render函數(shù)等,以及事件回調(diào)中的錯誤。如果有緩存則讀取緩存數(shù)據(jù),如果沒有緩存,則會拋出一個異常 promise,利用異常做邏輯流控制是一種擁有較深的調(diào)用堆棧時的手段,它是在虛擬 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有哪些新特性”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI