React 的將來,Suspense,lazy 異步加載組件[性能優化]

Suspense,lazy 是 React 16.x 的新特性 話很少說讓咱們開始吧

  • 在你的應用中引入代碼分割的最佳方式是經過動態 import() 語法。

注意: 動態 import() 語法目前只是一個 ECMAScript (JavaScript) 提案, 而不是正式的語法標準。預計在不遠的未來就會被正式接受。react

Webpack 解析到該語法時,它會自動地開始進行代碼分割。若是你使用 Create React App,該功能已配置好,你能馬上使用 這個特性。Next.js 也已支持該特性而無需再配置。 若是你本身配置 Webpack,你可能要閱讀下 Webpack 關於代碼分割的指南。你的 Webpack 配置應該相似於此。 當使用 Babel 時,你要確保 Babel 可以解析動態 import 語法而不是將其進行轉換。對於這一要求你須要 babel-plugin-syntax-dynamic-import 插件。服務器

React.lazy

注意: React.lazy 和 Suspense 技術還不支持服務端渲染。若是你想要在使用服務端渲染的應用中使用,咱們推薦 Loadable Components 這個庫。它有一個很棒的服務端渲染打包指南。babel

React.lazy 函數能讓你像渲染常規組件同樣處理動態引入(的組件)。網絡

const OtherComponent = React.lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <div> <OtherComponent /> </div>
  );
}
複製代碼

這個函數他返回了一個Promise,須要用函數去調用,並且仍是主動拋錯的,resolvereact-router

  • 這樣作就完成了組件的異步加載,可是這樣作有個問題那就是: 若是在 Home 渲染完成後,包含 OtherComponent 的模塊尚未被加載完成,咱們可使用加載指示器爲此組件作優雅降級。這裏咱們使用 Suspense 組件來解決。
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const TowComponent = React.lazy(() => import('./TowComponent'))

function MyComponent() {
  return (
   <Suspense fallback={<div>Loading...</div>}> <OtherComponent /> <TowComponent /> </Suspense>
  );
}
複製代碼
  • 若是在 MyComponent 渲染完成後,包含 OtherComponent 的模塊尚未被加載完成,咱們可使用加載指示器爲此組件作優雅降級。這裏咱們使用 Suspense 組件來解決。
  • Suspense 做用域取件能夠接受多個組件

MyErrorBoundary

那麼 咱們繼續問題,懶加載的組件,在加載的過程當中遇到一個問題如(網絡問題)咱們該怎麼樣來維護用戶體驗呢?——不至於讓用戶看到空白 它會觸發一個錯誤。你能夠經過異常捕獲邊界**(Error boundaries)**dom

注意 錯誤邊界僅能夠捕獲其子組件的錯誤,它沒法捕獲其自身的錯誤。若是一個錯誤邊界沒法渲染錯誤信息,則錯誤會冒泡至最近的上層錯誤邊界,這也相似於 JavaScript 中 catch {} 的工做機制。異步

仍是上面代碼:函數

import MyErrorBoundary from './MyErrorBoundary';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const TowComponent = React.lazy(() => import('./TowComponent'))

function MyComponent() {
  return (
  <MyErrorBoundary>
	   <Suspense fallback={<div>Loading...</div>}>
	    	<OtherComponent />
	   		<TowComponent />
	   </Suspense>
  <MyErrorBoundary>
  
  );
}
複製代碼

MyErrorBoundary 以下【選自官方文檔】this

class MyErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // 更新 state 使下一次渲染可以顯示降級後的 UI
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    // 你一樣能夠將錯誤日誌上報給服務器
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      // 你能夠自定義降級後的 UI 並渲染
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}
export default MyErrorBoundary ;
複製代碼

基於路由代碼分割

好讓咱們回來 在SPA 單頁面程序中,咱們可能要在多個頁面中用到組件的懶加載,這樣的話咱們就能夠吧他定義在路由中 很簡單 套進去就能夠了spa

import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import React, { Suspense, lazy } from 'react';

const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));

const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
      <Switch>
        <Route exact path="/" component={Home}/>
        <Route path="/about" component={About}/>
      </Switch>
    </Suspense>
  </Router>
);
複製代碼

命名導出(Named Exports)

接下來咱們就會想了,既然是import 那是否是也能夠嚮導入組件那種 進行 重命名 錯誤示範

const Tow = React.lazy(() => import('./TowComponent'))
複製代碼

注意:不能夠直接這樣子作 ,eact.lazy 目前只支持默認導出(default exports)。若是你想被引入的模塊使用命名導出(named exports),你能夠建立一箇中間模塊,來從新導出爲默認模塊。這能保證 tree shaking 不會出錯,而且沒必要引入不須要的組件。

// ManyComponents.js
export const MyComponent = /* ... */;
export const MyUnusedComponent = /* ... */;
複製代碼
// MyComponent.js
export { MyComponent as default } from "./ManyComponents.js";
複製代碼
// MyApp.js
import React, { lazy } from 'react';
const MyComponent = lazy(() => import("./MyComponent.js"));
複製代碼

可是 真的有必要這樣子作嗎?

相關文章
相關標籤/搜索