xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!

如何实现 React 模块动态导入

React 模块动态导入

代码分割

webpack & code splitting

https://reactjs.org/docs/code-splitting.html

https://zh-hans.reactjs.org/docs/code-splitting.html

Code-Splitting 可以创建多个可在运行时动态加载的包

https://webpack.js.org/guides/code-splitting/

https://rollupjs.org/guide/en/#code-splitting

https://github.com/browserify/factor-bundle

虽然您并未减少应用程序中的全部代码量,但避免了加载用户可能永远不需要的代码,并减少了初始加载过程中所需的代码量。



https://create-react-app.dev/docs/code-splitting/



https://nextjs.org/docs/advanced-features/dynamic-import

React.lazy and Suspense are not yet available for server-side rendering.

code-splitting & server-side rendering

https://github.com/gregberge/loadable-components

https://loadable-components.com/docs/server-side-rendering/

React.lazy

React.lazy 函数让你可以可以像导入将常规组件一样的渲染一个动态导入。

import OtherComponent from './OtherComponent';

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

首次呈现此组件时,它将自动加载包含OtherComponent的捆绑包。

React.lazy 采用了必须调用动态 import()的函数。
这必须返回一个 Promise,该 Promise 解析为一个带有默认导出的模块,该模块包含一个 React组件。

然后,应该将懒惰的组件呈现在Suspense组件中,这使我们可以在等待懒惰的组件加载时显示一些后备内容(例如加载指示符)。

import React, { Suspense } from 'react';

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

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <OtherComponent />
      </Suspense>
    </div>
  );
}

fallback prop 支持在等待组件加载时接受要渲染的任何React元素

您可以将 Suspense 组件放置在 lazy 组件上方的任何位置

您甚至可以用一个 Suspense 组件包装多个惰性组件。


import React, { Suspense } from 'react';

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

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

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <section>
          <OtherComponent />
          <AnotherComponent />
        </section>
      </Suspense>
    </div>
  );
}

Error boundaries

错误边界

如果另一个模块无法加载(例如,由于网络故障),它将触发错误

您可以处理这些错误,以显示良好的用户体验,并通过错误边界管理恢复

创建错误边界后,您可以在惰性组件上方的任何位置使用它来在出现网络错误时显示错误状态。

import React, { Suspense } from 'react';

import MyErrorBoundary from './MyErrorBoundary';

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

const MyComponent = () => (
  <div>
    <MyErrorBoundary>
      <Suspense fallback={<div>Loading...</div>}>
        <section>
          <OtherComponent />
          <AnotherComponent />
        </section>
      </Suspense>
    </MyErrorBoundary>
  </div>
);

https://reactjs.org/docs/error-boundaries.html



Route-based code splitting

基于路由的代码拆分

React Router & React.lazy

确定在应用程序中的何处引入代码拆分可能有些棘手
您要确保选择的位置能够平均拆分捆绑包,但不会破坏用户体验

路线是一个不错的起点
网络上的大多数人习惯了页面过渡,需要花费一些时间来加载
您还倾向于一次重新渲染整个页面,因此您的用户不太可能同时与页面上的其他元素进行交互

这是一个示例,说明如何使用带有 React.lazy 的 React Router 等库将基于路由的代码拆分为您的应用。

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

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

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>
);

https://reactjs.org/docs/code-splitting.html#route-based-code-splitting

react-router

https://reacttraining.com/react-router/

Named Exports

命名的导出

React.lazy 当前仅支持默认导出

如果要导入的模块使用命名的导出,则可以创建一个中间模块,将其重新导出为默认模块

这样可以确保摇树不停,并且不会拉扯未使用的组件


// 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"));

webpack

https://webpack.js.org/guides/code-splitting/


module.exports = {
  entry: {
    main: './src/app.js',
  },
  output: {
    // `filename` provides a template for naming your bundles (remember to use `[name]`)
    filename: '[name].bundle.js',
    // `chunkFilename` provides a template for naming code-split bundles (optional)
    chunkFilename: '[name].bundle.js',
    // `path` is the folder where Webpack will place your bundles
    path: './dist',
    // `publicPath` is where Webpack will load your bundles from (optional)
    publicPath: 'dist/'
  }
};

https://gist.github.com/gaearon/ca6e803f5c604d37468b0091d9959269

webpack & magic-comments

https://webpack.js.org/api/module-methods/#magic-comments

https://webpack.docschina.org/api/module-methods/#magic-comments


// Single target
import(
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  /* webpackExports: ["default", "named"] */
  'module'
);

// Multiple possible targets
import(
  /* webpackInclude: /.json$/ */
  /* webpackExclude: /.noimport.json$/ */
  /* webpackChunkName: "my-chunk-name" */
  /* webpackMode: "lazy" */
  /* webpackPrefetch: true */
  /* webpackPreload: true */
  `./locale/${language}`
);
import(/* webpackIgnore: true */ 'ignored-module.js');

babel

https://babeljs.io/

https://classic.yarnpkg.com/en/package/@babel/plugin-syntax-dynamic-import

$ yarn add -D @babel/plugin-syntax-dynamic-import

https://babeljs.io/docs/en/babel-plugin-syntax-dynamic-import

$ npm i -D @babel/plugin-syntax-dynamic-import

{
  "plugins": ["@babel/plugin-syntax-dynamic-import"]
}


// webpack config
const config = {
  entry: [
    "core-js/modules/es.promise",
    "core-js/modules/es.array.iterator",
    path.resolve(__dirname, "src/main.js"),
  ],
  // ...
};
// or

// src/main.js
import "core-js/modules/es.promise";
import "core-js/modules/es.array.iterator";

// ...

https://babeljs.io/blog/2019/07/03/7.5.0#dynamic-import-9552httpsgithubcombabelbabelpull9552-and-10109httpsgithubcombabelbabelpull10109

https://www.npmjs.com/package/babel-plugin-dynamic-import-node

https://github.com/airbnb/babel-plugin-dynamic-import-node

$ yarn add -D babel-plugin-dynamic-import-node

.babelrc


{
  "plugins": ["dynamic-import-node"]
}

dynamic import

https://webpack.js.org/guides/code-splitting/#dynamic-imports

import("./emoji-component").then(emoji => {
  // 使用 promise 
  console.log(emoji));
});

切换路由

react-router

lazy-load

延迟加载 / 懒加载

code splitting & dynamic import

import()类似函数的形式将模块名称作为参数,并返回一个Promise,该Promise始终解析为模块的名称空间对象

https://github.com/tc39/proposal-dynamic-import

http://2ality.com/2017/01/import-operator.html#loading-code-on-demand

https://create-react-app.dev/docs/code-splitting/

const moduleA = `ESM (code splitting & dynamic import)`;

export { moduleA };

这将使 moduleA.js 及其所有唯一依赖项成为单独的块,仅在用户单击“加载”按钮后才加载

import React, { Component } from 'react';

class App extends Component {
  handleClick = () => {
    import('./moduleA')
      .then(({ moduleA }) => {
        // Use moduleA
      })
      .catch(err => {
        // Handle failure
      });
  };
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>Load</button>
      </div>
    );
  }
}
export default App;

async / await & promise

如果愿意,还可以将其与 async / await 语法一起使用

// async / await 

 async handleClick = () => {
   const moduleA = await import('./moduleA');
  
   moduleA.then(({ moduleA }) => {
        // Use moduleA
    })
    .catch(err => {
        // Handle failure
     });
 };


chunks

https://create-react-app.dev/docs/production-build

refs



©xgqfrms 2012-2020

www.cnblogs.com 发布文章使用:只允许注册用户才可以访问!


原文地址:https://www.cnblogs.com/xgqfrms/p/13820944.html