如何實現 React 模塊動態導入


如何實現 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 發布文章使用:只允許注冊用戶才可以訪問!



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM