利用React 16.6新特性優化應用性能

利用React v16.6 Lazy&Suspense提高應用性能

本篇文章示例代碼

github.com/Gavin1995/r…html

前言

利用懶加載(Lazy Loading)優化頁面性能不是什麼新概念,不過React 16.6可使用React.lazySuspense讓原生React實現Lazy Loading大大的簡化。前端

本篇文章也會拓展多種相似解決方案,見拓展部分react

主要關鍵詞說明

動態導入(Dynamic Import)

動態導入目前只是TC39的一個提案,不是JS(ES)標準的一部分。該功能能夠動態化加載咱們分割的組件/頁面/文件,具體提案見下方連接: TC39 proposal-dynamic-import提案webpack

目前要使用動態導入,須要使用到babel插件:git

babel-plugin-syntax-dynamic-importgithub

React.lazy

lazy函數提供了一種很是簡便的方法動態導入組件,實現按需加載與代碼分割web

點擊查看文檔性能優化

React.lazy使用方式很是簡單,示例:babel

// lazy 接受一個函數做爲參數
const MyComponent = React.lazy(() => import('path/MyComponent'));

// 展現時才加載(能夠自定義一些條件)
const App = () => (
  <div> <MyComponent /> </div>
)
複製代碼

Suspense

Suspense組件用於包裝lazy組件,在lazy組件尚未徹底加載時,將fallback內容呈現給用戶。網絡

用動態加載,編譯時會將文件分割,從加載文件到呈現會有時間延遲,此時可使用Suspense展現一個loading。

點擊查看文檔

使用示例

import React, { Suspense } from 'react';

const MyComponent = React.lazy(() => import('path/MyComponent'));
const Loading = (
  <h3>loading...</h3>
);

const App = () => (
  <div> <Suspense fallback={Loading}> <MyComponent /> </Suspense> </div>
)
複製代碼

ErrorBoundary

ErrorBoundary(錯誤邊界),可使用ErrorBoundary包裝Suspense,當Suspense出錯時,統一處理錯誤。固然ErrorBoundary不止可用於Suspense,能夠包裝任意組件,而後統一處理錯誤。

實際場景中,能夠本身先寫一些通用錯誤頁/組件繼承自ErrorBoundary,而後包裝要處理的組件。

也能夠在組件的componentDidCatch生命週期中單獨處理錯誤。

點擊查看文檔

應用

性能優化說明

針對React加載的優化,能夠分爲兩類:

  • 頁面加載優化
  • 組件加載優化

很明顯,React.lazy + Suspense能夠很方便的應用於組件加載優化場景。

示例場景

CSR(客戶端渲染)應用多頁面的中某個耗時組件的加載

路由層

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

import Header from './header';
import Index from '../pages';
import Welcome from '../pages/welcome';

export default () => (
  <Router>
    <div>
      <Header />

      <Route exact path="/" component={Index}/>
      <Route path="/welcome" component={Welcome}/>
    </div>
  </Router>
);
複製代碼

某個存在耗時組件的頁面

// welcome.js
import React, { Suspense } from 'react';

// 使用Suspense + React.lazy動態加載的耗時組件
const Content = React.lazy(() => import('../components/content'));

export default () => (
  <div> <h1>Welcome</h1> <Suspense fallback={<div>loading...</div>}> <Content/> </Suspense> </div>
);
複製代碼

說明

假設用webpack編譯上面代碼產出bundle.js。當進入首頁時,加載的bundle.js不會包含Welcome Content部分的代碼,切換頁面到Welcome時會走網絡加載須要的文件。

利用這種方法拆分多個組件時,能夠顯著的提高首屏時間。

完整示例項目

React-New-Features

說明:該項目主要用於React一些新功能及有意思的用法示例,會持續更新,後面會涉及到hooks及最新源碼相關,有興趣的能夠star。

拓展

針對SSR(服務端渲染)應用導入性能優化

可使用 Next.js ,Next.js已支持直接使用dynamic import,而且能夠指明何時動態導入(服務端/客戶端),Next.js對首屏作了不少優化,首屏時,服務端會尋找到最簡依賴渲染,具體能夠點擊前面連接查看文檔。示例:

// Next.js 動態導入
const MyComponent = dynamic(import('../components/MyComponent'), {
  ssr: false,
  loading: () => false,
});
複製代碼

針對組件的動態加載

針對圖片的lazy loading

針對動態請求

  • react-request:對於CSR應用,在不用hooks的狀況下,也能夠不在componentDidMount中處理請求,讓請求更加的優雅。
  • react-async:利用Render Props + React hooks + Context API統一處理請求,支持SSR使用

封裝react-request示例(統一請求處理)

import React from 'react';
import PropTypes from 'prop-types';
import { Fetch } from 'react-request';
// import ReactLoading from 'react-loading';

import config from '../../config';

const CustomFetch = (props) => {
  const { url, children } = props;
  return (
    <Fetch {...props} url={config.serverUrl + url} transformData={data => data.data} > {({ fetching, failed, data }) => { if (fetching) { // 正在請求:能夠展現loading動畫 // return <ReactLoading type="cylon" color="#2db53f" height="10%" width="10%" />; } if (failed) { // 當前組件請求失敗 // return <h1 className="home">請求失敗...</h1>; } if (data) { // 請求成功 return children(data); } return null; }} </Fetch> ); }; CustomFetch.propTypes = { url: PropTypes.string.isRequired, }; export default CustomFetch; 複製代碼

在組件中使用CustomFetch

buildLinks = () => (
    <div className="links"> <CustomFetch url="/getLinks" > {data => data.map((item, index) => ( <a className="item" href={item.url} key={`link-${index}`}> <img src={item.icon} className="icon" alt={item.name} /> <p>{item.name}</p> </a> ))} </CustomFetch> </div> ); 複製代碼

後記

其它性能優化方式,以及本身如何實現,能夠參考個人上篇文章:大前端性能總結

相關文章
相關標籤/搜索