簡體   English   中英

如何同時延遲加載多個組件

[英]How to lazy load multiple components at a same time

我有 2 個組件,我想使用延遲加載來加載所有組件,例如

const A = lazy(() => import("../test/A")); 
const B = lazy(() => import("../test/B")); 

這將創建 2 個單獨的包,並在需要時導入它們。

但是,我想創建一個包,當該包加載時,我應該能夠使用上述兩個組件。

我也不想創建包含上述兩個組件的單個組件,因為我想要為它們兩個單獨的路由

我試圖做這樣的事情https://codesandbox.io/s/eager-raman-mdqzc?file=/src/App.js

請問有人會解釋我這種功能是否可能,如果是,那么我做錯了什么以及做錯了什么

代碼拆分器中可能有一些調整選項可以更好地完成您想要實現的目標。 但是,如果您不想弄亂這些(或者因為您使用的是預設配置而無法更改它們),那么也許您可以將模塊組合到一個文件中,然后延遲加載該“組合模塊”。

要做到這一點,我們首先需要知道如何lazy決定模塊中要加載的組件,以及它期望的對象類型。 從文檔:

React.lazy function 允許您將動態導入呈現為常規組件。

React.lazy需要一個必須調用動態import()的 function 。 這必須返回一個Promise解析為具有包含 React 組件的默認導出的模塊。

然后應該在Suspense組件中呈現惰性組件,這允許我們在等待惰性組件加載時顯示一些備用內容(例如加載指示器)。

您可以將Suspense組件放置在惰性組件上方的任何位置。 你甚至可以用一個Suspense組件包裝多個惰性組件。

所以根據這個,如果你想使用lazy()來包裝模塊,那么你必須有一個組件作為模塊的default屬性。 因此它不允許您自動使用使用命名導出作為組件的模塊。 但是,您可以輕松地制作一個 promise 將命名導出轉換為默認導出,並將其包裝在惰性中:

// in comboModule.js:
export A from '../test/A'
export B from '../test/B'

// in the code that needs lazy modules
  const A = lazy(() => import('./comboModule').then((module) => ({default: module.A})))
  const B = lazy(() => import('./comboModule').then((module) => ({default: module.B})))

請注意,我們必須在傳遞給lazy的初始化程序 function 中調用import ,否則導入將立即開始。 lazy的部分好處是讓您等到父組件在加載之前呈現惰性組件。 但是, import()應該緩存第一次導入的結果,並且只加載一次代碼。

在初始化程序 function 中,我們使用thenimport()的結果從Promise({A: <Component>, B: <Component>})轉換為惰性對初始化程序 function: Promise({default: <Component>})

現在我們有兩個惰性組件,它們都來自一個模塊文件。

資源:

您可以使用 Suspense 等待他們兩個。 有兩個捆綁包,但您可以等待加載它們

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

我寫了一個實用程序 function 概括了@garrett-motzner 在他的回答中所說的內容:

import {lazy} from 'react';

export const multiLazy = moduleLoaderArray => {
  const promises = Promise.all(moduleLoaderArray.map(loader => loader()));
  return moduleLoaderArray.map((m, index) =>
    lazy(() => promises.then(results => results[index]))
  );
}

像這樣的魅力工作:

const [A, B] = multiLazy([
  () => import("../test/A"),
  () => import("../test/B"),
]);

所以不需要創建中間組件文件(當然除了實用程序 function 之外)

我選擇這種語法以盡可能接近React.lazy語法,但如果喜歡並且經常使用它可以修改為multiLazy(['../test/A', '../test/B'])用過的。

你可以對import語句使用代理包裝器來為React.lazy提供它想要的東西:

const bundle = new Proxy(import("../CombineModule"),{
  "get":function (esm,key){
    return esm.then(function (m){
      return {"__esMODULE":true,"default":m.default[key]};
    });
  }
});

var CompA = React.lazy(() => bundle.A)
var CompB = React.lazy(() => bundle.B)

根據 Garrett Motzner 的回答,我制作了這個幫助程序來加載命名組件:

// in comboModule.js:
export default from '../test/A'
export B from '../test/B'

// in the code that needs lazy modules
const m = () => import('comboModule')
const ADefault = lazy(m);
const BNamed = lazy(m, m => m.B);


// the new lazy helper function:

export function lazy<T extends React.ComponentType<any>, Q extends { default: T }>(
    factory: () => Promise<Q>,
    selector?: (arg: Q) => () => JSX.Element
) {
    if (!selector) return React.lazy(factory);
    return React.lazy(() =>
        factory().then((module) => ({ default: selector(module) }))
    );
}

它確實需要默認導出才能存在,否則 TypeScript 會抱怨這一點,但您也可以從模塊中加載命名組件。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM