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"));
复制代码

可是 真的有必要这样子作吗?

相关文章
相关标签/搜索