如何实现 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 函数让你可以可以像导入将常规组件一样的渲染一个动态导入。

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

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

React.lazy 采用了必须调用动态 import()的函数。

这必须返回一个 Promise,该 Promise 解析为一个带有默认导出的模块,该模块包含一个 React组件。

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

  1. import React, { Suspense } from 'react';
  2. const OtherComponent = React.lazy(() => import('./OtherComponent'));
  3. function MyComponent() {
  4. return (
  5. <div>
  6. <Suspense fallback={<div>Loading...</div>}>
  7. <OtherComponent />
  8. </Suspense>
  9. </div>
  10. );
  11. }

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

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

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


  1. import React, { Suspense } from 'react';
  2. const OtherComponent = React.lazy(() => import('./OtherComponent'));
  3. const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
  4. function MyComponent() {
  5. return (
  6. <div>
  7. <Suspense fallback={<div>Loading...</div>}>
  8. <section>
  9. <OtherComponent />
  10. <AnotherComponent />
  11. </section>
  12. </Suspense>
  13. </div>
  14. );
  15. }

Error boundaries

错误边界

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

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

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

  1. import React, { Suspense } from 'react';
  2. import MyErrorBoundary from './MyErrorBoundary';
  3. const OtherComponent = React.lazy(() => import('./OtherComponent'));
  4. const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
  5. const MyComponent = () => (
  6. <div>
  7. <MyErrorBoundary>
  8. <Suspense fallback={<div>Loading...</div>}>
  9. <section>
  10. <OtherComponent />
  11. <AnotherComponent />
  12. </section>
  13. </Suspense>
  14. </MyErrorBoundary>
  15. </div>
  16. );

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

Route-based code splitting

基于路由的代码拆分

React Router & React.lazy

确定在应用程序中的何处引入代码拆分可能有些棘手

您要确保选择的位置能够平均拆分捆绑包,但不会破坏用户体验

路线是一个不错的起点

网络上的大多数人习惯了页面过渡,需要花费一些时间来加载

您还倾向于一次重新渲染整个页面,因此您的用户不太可能同时与页面上的其他元素进行交互

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

  1. import React, {
  2. Suspense,
  3. lazy,
  4. } from 'react';
  5. import {
  6. BrowserRouter as Router,
  7. Route,
  8. Switch,
  9. } from 'react-router-dom';
  10. const Home = lazy(() => import('./routes/Home'));
  11. const About = lazy(() => import('./routes/About'));
  12. const App = () => (
  13. <Router>
  14. <Suspense fallback={<div>Loading...</div>}>
  15. <Switch>
  16. <Route exact path="/" component={Home}/>
  17. <Route path="/about" component={About}/>
  18. </Switch>
  19. </Suspense>
  20. </Router>
  21. );

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

react-router

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

Named Exports

命名的导出

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

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

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


  1. // ManyComponents.js
  2. export const MyComponent = /* ... */;
  3. export const MyUnusedComponent = /* ... */;
  1. // MyComponent.js
  2. // 中间模块, 导出为默认模块
  3. export { MyComponent as default } from "./ManyComponents.js";
  1. // MyApp.js
  2. import React, { lazy } from 'react';
  3. const MyComponent = lazy(() => import("./MyComponent.js"));

webpack

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


  1. module.exports = {
  2. entry: {
  3. main: './src/app.js',
  4. },
  5. output: {
  6. // `filename` provides a template for naming your bundles (remember to use `[name]`)
  7. filename: '[name].bundle.js',
  8. // `chunkFilename` provides a template for naming code-split bundles (optional)
  9. chunkFilename: '[name].bundle.js',
  10. // `path` is the folder where Webpack will place your bundles
  11. path: './dist',
  12. // `publicPath` is where Webpack will load your bundles from (optional)
  13. publicPath: 'dist/'
  14. }
  15. };

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


  1. // Single target
  2. import(
  3. /* webpackChunkName: "my-chunk-name" */
  4. /* webpackMode: "lazy" */
  5. /* webpackExports: ["default", "named"] */
  6. 'module'
  7. );
  8. // Multiple possible targets
  9. import(
  10. /* webpackInclude: /\.json$/ */
  11. /* webpackExclude: /\.noimport\.json$/ */
  12. /* webpackChunkName: "my-chunk-name" */
  13. /* webpackMode: "lazy" */
  14. /* webpackPrefetch: true */
  15. /* webpackPreload: true */
  16. `./locale/${language}`
  17. );
  18. import(/* webpackIgnore: true */ 'ignored-module.js');

babel

https://babeljs.io/

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

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

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

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

  1. {
  2. "plugins": ["@babel/plugin-syntax-dynamic-import"]
  3. }

  1. // webpack config
  2. const config = {
  3. entry: [
  4. "core-js/modules/es.promise",
  5. "core-js/modules/es.array.iterator",
  6. path.resolve(__dirname, "src/main.js"),
  7. ],
  8. // ...
  9. };
  1. // or
  2. // src/main.js
  3. import "core-js/modules/es.promise";
  4. import "core-js/modules/es.array.iterator";
  5. // ...

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

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

.babelrc


  1. {
  2. "plugins": ["dynamic-import-node"]
  3. }

dynamic import

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

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

切换路由

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/

  1. const moduleA = `ESM (code splitting & dynamic import)`;
  2. export { moduleA };

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

  1. import React, { Component } from 'react';
  2. class App extends Component {
  3. handleClick = () => {
  4. import('./moduleA')
  5. .then(({ moduleA }) => {
  6. // Use moduleA
  7. })
  8. .catch(err => {
  9. // Handle failure
  10. });
  11. };
  12. render() {
  13. return (
  14. <div>
  15. <button onClick={this.handleClick}>Load</button>
  16. </div>
  17. );
  18. }
  19. }
  20. export default App;

async / await & promise

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

  1. // async / await
  2. async handleClick = () => {
  3. const moduleA = await import('./moduleA');
  4. moduleA.then(({ moduleA }) => {
  5. // Use moduleA
  6. })
  7. .catch(err => {
  8. // Handle failure
  9. });
  10. };

chunks

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

refs



xgqfrms 2012-2020

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


如何实现 React 模块动态导入的更多相关文章

  1. 反射attr以及模块动态导入

    一.实现自省的四个函数 1.hasattr判断一个对象中有没有一个name字符串对应的方法或属性 class BlackMedium: feture="Ugly" def __in ...

  2. python 反射 动态导入模块 类attr属性

    1.反射 hasattr getattr delattr setattr 优点:事先定义好接口,接口只有在被完成后才能真正执行,这实现了即插即用,这其实是一种“后期绑定”,即先定义好接口, 然后是再去 ...

  3. 封装,封装的原理,Property ,setter ,deleter,多态,内置函数 ,__str__ , __del__,反射,动态导入模块

    1,封装 ## 什么是封装 what 对外隐藏内部的属性,以及实现细节,并给外部提供使用的接口 学习封装的目的:就是为了能够限制外界对内部数据的方法 注意 :封装有隐藏的意思,但不是单纯的隐藏 pyt ...

  4. day26 封装、多态、内置函数、反射、动态导入

    今日内容 1.封装 什么是封装? 封装从字面意思上看就只将某种东西封起来装好,当我们代码中的某些方法与属性不想让外界进行访问时,就对这些属性进行特殊的处理,使这种属性或者方法不能被外界直接进行访问或者 ...

  5. python面向对象反射-框架原理-动态导入-元类-自定义类-单例模式-项目的生命周期-05

    反射 reflect 反射(reflect)其实是反省,自省的意思 反省:指的是一个对象应该具备可以检测.修改.增加自身属性的能力 反射:通过字符串获取对象或者类的属性,进行操作 设计框架时需要通过反 ...

  6. Python之路-python(面向对象进阶(模块的动态导入、断言、Socket Server))

    模块的动态导入 断言 Socket Server 一.模块的动态导入 class C(object): def __init__(self): self.name = "zhangsan&q ...

  7. Python 动态导入模块

    动态导入模块 目录结构: zhangsandeMacBook-Air:1110 zhangsan$ tree . . ├── lib │   └── aa.py ├── test1.py lib目录下 ...

  8. Python 实现接口类的两种方式+邮件提醒+动态导入模块+反射(参考Django中间件源码)

    实现接口类的两种方式 方式一 from abc import ABCMeta from abc import abstractmethod class BaseMessage(metaclass=AB ...

  9. python importlib动态导入模块

    一般而言,当我们需要某些功能的模块时(无论是内置模块或自定义功能的模块),可以通过import module 或者 from * import module的方式导入,这属于静态导入,很容易理解. 而 ...

随机推荐

  1. 超详细oracle 11g安装步骤 win版本

    1. 打开网址: https://edelivery.oracle.com 使用oracle 任意账号登录 账号:2696671285@qq.com 密码:Oracle123 感谢来自某位好心大佬的共 ...

  2. jmeter报Address already in use: connect

    jmeter报Address already in use: connect   用windows进行jmeter压测出现java.net.BindException: Address already ...

  3. has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

    前端显示: has been blocked by CORS policy: Response to preflight request doesn't pass access control che ...

  4. (011)每日SQL学习:SQL开窗函数

    开窗函数:在开窗函数出现之前存在着很多用 SQL 语句很难解决的问题,很多都要通过复杂的相关子查询或者存储过程来完成.为了解决这些问题,在 2003 年 ISO SQL 标准加入了开窗函数,开窗函数的 ...

  5. JasperReports 取消自动分页/忽略分页

    因为需要将合同比价单由PDF文档形式改为HTML页面方式,虽然转换文档类型了,但是发现HTML页面中间到了一定行数就出现了空行把Detail给隔开了.之前总想着怎样消除中间空行,以为是报表top页面边 ...

  6. log4j 动态配置,重启项目配置失效问题

    公司项目升级之后,成功去掉了log4j.properties配置文件,实现页面动态配置日志级别. 很经典的两个配置,但是最终还是随着时代的进步而被优化,最终弄成了可配置项 但是随之问题就来了,当我启动 ...

  7. linux shell 条件判断if else, if elif else....

    在linux的shell中 if 语句通过关系运算符判断表达式的真假来决定执行哪个分支.Shell 有三种 if ... else 语句: if ... fi 语句: if ... else ... ...

  8. [SpringSecurity] UserDetailsService 详解

    UserDetailsService 接口 当什么也没有配置的时候,账号和密码是由 Spring Security 定义生成的. 而在实际项目中账号和密码都是从数据库中查询出来的. 所以我们要通过自定 ...

  9. jvm学习第一天

    视频教程链接 第一部分-jvm初识 0.jvm概览图 JVM由三个主要的子系统构成 类加载子系统 运行时数据区(内存结构) 执行引擎运行时数据区(内存结构) 1.什么是jvm 定义: ①. JVM 是 ...

  10. cassandra权威指南读书笔记--安全

    认证和授权driver,JMX和cassandra服务器支持SSL/TLS,cassandra节点间也支持SSL/TLS.密码认证器cassandra还支持自定义,可插拔的认证机制.默认的认证器:or ...