React 中 lazy, Suspense 以及錯誤邊界(Error Boundaries)的使用

React 中 lazy, Suspense 以及錯誤邊界(Error Boundaries)的使用

lazy

lazyreact 提供的組件懶加載的能力,在官方沒有支持 lazy以前,在 react 中咱們通常使用 react-loadable 來實現組件懶加載的能力,可是,lazy 並不支持 服務端渲染(SSR),因此在使用ssr的狀況下,lazy 暫時不能使用javascript

React.lazy 接受一個函數,這個函數內部調用 import() 動態導入。它必須返回一個 Promise,該 Promise 須要 resolve 一個 defalut exportReact 組件。html

具體如何使用呢,看下面例子java

import React, { lazy } from 'react'

  const AppTitle = lazy(() => import('./title'))

  class App extends React.Component {
    render () {
      return (
       <AppTitle></AppTitle>
      )
    }
  }

  export default App

複製代碼

當咱們把這段代碼運行起來的時候,卻發現程序報錯了react

Error: A React component suspended while rendering, but no fallback UI was specified.

  Add a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display.
      in Unknown (at lazy/index.jsx:8)
      in App (at src/index.js:7)
複製代碼

這個時候就須要 react 中內置的組件 Suspense 來配合使用,只須要將 Suspense 引入,而後將異步導入的組件包裹起來,同時也要指定 fallback 屬性, 能夠支持傳入一段 jsx, 或者一個組件實例,相似 <Loading></Loading>webpack

import React, { lazy, Suspense } from 'react'

const AppTitle = lazy(() => import('./title.jsx'))

class App extends React.Component {
    render () {
      return (
        <Suspense fallback={<div>Loading</div>}> <AppTitle></AppTitle> </Suspense>
      )
    }
}

export default App
複製代碼

當咱們使用 lazy 以後,咱們能夠打開 chromenetwork 而後選擇 js 來觀察 發起的請求,咱們發現這個時候會出現 1.chunk.js 這種 數字開頭的js文件,其實每一個 .js 就對應咱們每個組件,當咱們須要去 指定每一個 異步組件 打包的js文件名時,咱們只須要在 import() 函數中 添加 /* webpackChunkName: "title" */ 這段註釋,webpack 在打包時,自動會將咱們指定的名字做爲文件名git

import React, { lazy, Suspense } from 'react'

const AppTitle = lazy(
    () => import(/* webpackChunkName: "title" */'./title.jsx')
)

class App extends React.Component {
    render () {
      return (
        <Suspense fallback={<div>Loading</div>}> <AppTitle></AppTitle> </Suspense>
      )
    }
}
export default App

複製代碼

若是當一個組件異步加載下載js文件時,網絡錯誤,沒法下載 js 文件,那麼 react 會表現出什麼狀況呢github

Error: Loading chunk 3 failed.
  (error: http://192.168.4.183:3000/static/js/title.chunk.js)
複製代碼

很明顯,Suspense 沒法處理這種錯誤狀況, 在 react 中有一個 錯誤邊界 (Error Boundaries)的概念,用來解決這種問題,它是利用了 react 生命週期的 componetDidCatch 方法來處理web

有兩種方式,一種是 生命週期 componentDidCatch 來處理錯誤,還有一種 是 靜態方法 static getDerivedStateFromError 來處理錯誤,chrome

官網的建議是性能優化

請使用 static getDerivedStateFromError() 渲染備用 UI ,使用 componentDidCatch() 打印錯誤信息。

import React, { lazy, Suspense } from 'react'

const AppTitle = lazy(
    () => import(/* webpackChunkName: "title" */'./title.jsx')
)

class App extends React.Component {
    state = {
      isError: false
    }
    
    static getDerivedStateFromError(error) {
      return { isError: true };
    }
    
    componentDidCatch (err, info) {
      console.log(err, info)
    }
    
    render () {
      if (this.state.isError) {
        return (<div>error</div>)
      }
      return (
        <div> <Suspense fallback={<div>Loading</div>}> <AppTitle></AppTitle> </Suspense> </div>
      )
    }
}


export default App
複製代碼

注意:使用 create-react-app 建立的項目,在開發環境,就算使用了 componentDidCatch 或者 getDerivedStateFromError,錯誤依然會被拋出,在 build 後,錯誤將會捕獲,不會致使整個項目卸載

根據官方文檔所說,在 react 16 之後,任何未被錯誤邊界捕獲的錯誤將會致使整個 React 組件樹被卸載。 因此咱們在開發項目時,須要去捕獲錯誤邊界的錯誤,並提供一個備用UI

參考:

  1. 官方文檔-錯誤邊界

代碼地址

  1. GitHub

react 其餘文檔

  1. React 中 Context 的使用)的使用
  2. React 中性能優化、 memo、 PureComponent、shouldComponentUpdate 的使用

若是對你有幫助,點個贊吧

相關文章
相關標籤/搜索