Porozumění dynamickým importům, Lazy a Suspense pomocí React Hooks

Vzrušující cesta

S výzvou #2articles1week od HashNode to byla zatím vzrušující cesta. Tolik poučení ze čtení skvělých článků, které z toho vycházejí. Věřím, že všichni autoři, kteří se toho účastní, jsou skutečnými vítězi.

Hrdě jsem ve 4. týdnu (minulý týden) výzvy a zde je můj 9. článek jako její součást. Doufám, že se vám to bude líbit.

Co se dnes naučíme?

Pokud s vývojem webu začínáte, pravděpodobně se učíte o modularitě kódu, komponentách, sdružování atd. Pokud jste veterán, většinou to již děláte. Toto je několik klíčových aspektů, které bychom se měli naučit a přemýšlet o nich, bez ohledu na knihovnu nebo rámec, který používáme pro vývoj webu.

V tomto článku vám vysvětlím výhody znalosti několika technik od reakce, abyste ji zvládli lépe,

  • Sdružování a rozdělování kódu.
  • Dynamické načítání kódu na vyžádání (a-la-carte vs. jídlo na talíři).
  • Získejte výkon aplikace, první načtení atd.
  • Vybudujte flexibilitu technologie plug and play.

Dozvíme se o Dynamic import komponent reakce, použití React.Lazy a Suspense . Opravdu nepotřebujete žádné předchozí zkušenosti, abyste mohli postupovat podle tohoto článku. Výhodou však bude mít velmi základní znalosti o reakci.

Tvar, barva a velikost

Nechte nás vytvořit aplikaci, která nám při kliknutí na tlačítko poskytne nějaké tvary, barvy a velikosti. Zde je snímek obrazovky, který ukazuje tři různé stavy aplikace po kliknutí na příslušná tlačítka.

Chcete si nyní s aplikací hrát? Není problem.

  • Ukázka:Běží na Netlify.
  • Github:Zdrojový kód naleznete zde.

Komponenty

Dovolte mi zde představit tři jednoduché komponenty, ShapeDemo , ColorDemo a SizeDemo zobrazuje některé náhodné tvary, barvy a velikosti. K jejich vytvoření používám ikony prolnutí.

Komponenta ShapeDemo

import React from 'react';
import { Square, Triangle, Circle, Box, Heart, Hexagon } from 'react-feather';

const ShapeDemo = () => {
    return(
        <>
            <h1>Shape Demo</h1>
            <div className="demo">
                <Square color="black" size={128} /> { ' '}
                <Triangle color="black" size={128} /> { ' '}
                <Circle color="black" size={128} /> { ' '}
                <Box color="black" size={128} /> { ' '}
                <Heart color="black" size={128} /> { ' '}
                <Hexagon color="black" size={128} /> { ' '}
            </div>
        </>
    )
};

export default ShapeDemo;

Komponenta ColorDemo

import React from 'react';
import { Circle } from 'react-feather';

const ColorDemo = () => {
    const colorMap = ['#A63578', 'teal', '#000000', 'orange', 'red',
        'green', 'blue', 'purple', 'yellow'];
    return(
        <>
            <h1>Color Demo</h1>
            <div className="demo">
                {
                    colorMap.map((color, index) => (
                        <Circle 
                              color={color} 
                              fill={color} 
                              key={index} 
                              size={128} /> 
                    ))
                }
            </div>
        </>
    )
};

export default ColorDemo;

SizeDemo Component


import React from 'react';
import { Circle } from 'react-feather';

const SizeDemo = () => {
    const sizeMap = ['16', '32', '48', '64', '96', '128', '144'];

    return(
        <>
            <h1>Size Demo</h1>
            <div className="demo">
                {
                    sizeMap.map((size, index) => (
                        <Circle 
                              color="black" 
                              fill="black" 
                              key={index} 
                              size={size} /> 
                    ))
                }
            </div>
        </>
    )
};

export default SizeDemo;

Jedná se o jednoduché komponenty reakce, které se chystáme importovat a vykreslit kliknutím na příslušné tlačítko.

Explicitní import a rychlé načítání

Jedním ze způsobů, jak toho dosáhnout, je importovat všechny tři komponenty do hlavní komponenty (řekněme App) a dosáhnout požadovaných funkcí.

  • Nejdříve importy
     import ShapeDemo from './demo/shape-demo';
     import ColorDemo from './demo/color-demo';
     import SizeDemo from './demo/size-demo';
    
  • Spravujte stav pro zobrazení příslušného typu ukázky.
     const [demo, setDemo] = useState();
    
  • Rozmístěte tlačítka
     <Button onClick={() => selectDemo('shape')}>Shape Demo</Button>
     <Button onClick={() => selectDemo('color')}>Color Demo</Button>
     <Button onClick={() => selectDemo('size')}>Size Demo</Button>
    
     const selectDemo = type => {
        setDemo(type);
     }
    
  • Nakonec vykreslete komponenty na základě vybraných typů
     <div className="demo-ground">
          { demo === 'shape' && <ShapeDemo /> }
          { demo === 'color' && <ColorDemo /> }
          { demo === 'size' && <SizeDemo /> }
     </div>
    
    Kompletní zdrojový soubor si můžete prohlédnout zde.

Tento přístup funguje dobře a dosáhneme požadovaného výsledku. Takže, kde je problém?

Zde jsou problémy:

  • Co když k tomu chci přidat další dvě ukázky, tj. VolumeDemo a MeasuringDemo ? Abychom importovali obě komponenty, musíme změnit kód v souboru App.js. Také potřebujeme mít několik dalších podmínek v renderu jako,

      { demo === 'volume' && <VolumeDemo /> }
      { demo === 'measuring' && <MeasuringDemo /> }
    

    To není tak cool. Ponechat tento kód tak otevřený změnám pro každé přidání požadavku není dost chytré.

  • Všechny tyto demo komponenty importujeme explicitně, protože je načítáme eagerly všechno najednou.

    Teď počkej, tohle je problematičtější. Čím více roste kód aplikace, tím roste balíček, a to znamená, že počáteční doba načítání aplikace poroste úměrně. Nemohu načíst kód potřebný na začátku a zbytek načíst na požádání? Ano, zde přichází na řadu dynamický import a líné načítání. O tom budeme diskutovat za chvíli.

Zde je rychlá ukázka toho, že při horlivém nakládání nenakládáme nic na požádání. Všimněte si, že protože vše načteno (zdroj načten) při prvním načtení aplikace, každé kliknutí na tlačítko nic nenačte. Žádné načítání na vyžádání (nebo načítání zdrojů).

Dynamický import a líné načítání

Jak jsme viděli problém s explicit import a eager loading , potřebujeme něco, co by nám pomohlo s velikostí balíčku a počátečním zatížením na limit. React představuje koncept dělení kódu, který nám může pomoci v splitting balíček generovaný nástroji jako Webpack , Rollup atd.

Zde je citát z respond doc o dělení kódu:

Nyní změníme kód naší aplikace tak, abychom mohli plně využít výhod tohoto konceptu.

Dynamické importy a React.Lazy

V této verzi aplikace budeme získávat informace o demo komponentách z obchodu (řekněme soubor json, tabulka db atd.) a dynamicky je importovat. Zde je jednoduchý soubor json, který popisuje informace o meta datech, jako je id komponenty, zobrazovaný název a název souboru (nebo název komponenty).

{
    "data": [
        {
            "id": "shape",
            "name": "Shape Demo",
            "file": "shape-demo"
        },
        {
            "id": "color",
            "name": "Color Demo",
            "file": "color-demo"
        },
        {
            "id": "size",
            "name": "Size Demo",
            "file": "size-demo"
    ]
}

Odstraníme všechny dříve provedené explicitní importy

Přidejte kód pro dynamické importy

const importDemo = file =>
  lazy(() =>
    import(`./demo/${file}`)
      .catch(() => console.log('Error in importing'))
);

Tady se děje málo věcí,

  • Máme funkci nazvanou importDemo který bere jako argument soubor. Tento argument souboru představuje vybrané demo na základě kliknutí na tlačítko.
  • Další lazy Funkce (nebo React.lazy) nám umožňuje vykreslit dynamický import jako běžnou komponentu. Jak vidíte, komponentu umístěnou ve složce demo nyní dynamicky importujeme.

Takže předtím:

  import ShapeDemo from './demo/shape-demo';

Po:

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

Tím se automaticky načte balíček obsahující ShapeDemo při prvním vykreslení této komponenty.

Zpracování kliknutí na tlačítka

Pamatujete si demo metadata json? Má všechny detaily demo komponent? Importovali jsme jej jako,

import * as meta from './meta/demo-data.json';

Tlačítka lze nastavit iterací přes meta informace,

mataDemoData.map((demoData, index) => (
     <React.Fragment key = {index}>
          <Button 
              variant="outline-info" 
              onClick={() => selectDemo(demoData.file)}>{demoData.name}
          </Button> {' '}
    </React.Fragment>
     ))

selectDemo() funkce odfiltruje další ukázku a vybere to na základě kliknutí na příslušné tlačítko,

const selectDemo = file => {
    const filtered = mataDemoData.filter(elem => {
      return elem.file === file;
    });
    loadDemo(filtered);
}

loadDemo() metoda dělá trik vyvoláním importDemo() metoda (ta, která používá líný pro dynamický import), kterou jsme vysvětlili výše.

async function loadDemo(filtered) {
    const promise =
      filtered.map(async demo => {
        const Demo = await importDemo(demo.file);
        return <Demo key={demo.id} />;
      });

    Promise.all(promise).then(setSelectedDemo);
  }

Všimli jste si toho? Demo nyní importujeme dynamicky a nestaráme se o to, abychom tento kód trochu změnili, pokud bude v budoucnu přidán nový požadavek na demo.

Napětí

Dobře, takže jsme v pohodě s dynamic imports a lazy loading zatím. Co takhle to vykreslit? Vítejte, Suspense . Líný komponent může být vykreslen uvnitř Suspense component . Umožňuje nám zobrazit nějaký záložní obsah (jako je indikátor načítání), zatímco čekáme, až se načte líná komponenta.

<div className="demo-ground">
  <React.Suspense fallback='Loading demo, hang on...'>
     {selectedDemo}
  </React.Suspense>
</div>

Tím jsme nyní vyřešili několik problémů:

  • Už žádné těsné spojení s importovanými a vykreslenými komponentami. Demo komponenty jsou nyní připojitelné.
  • Balík je nyní rozdělen na části a načítá se pouze tehdy, když je to potřeba. Viz změněné chování níže. Všimněte si, že bloky se načítají (načítají se prostředky) pouze po kliknutí na příslušná tlačítka. Také indikátor načítání s fallback se zobrazí, dokud se komponenta nenačte.

Závěr

Přiznávám, bylo to dlouhé. Ale pokud jste to dotáhli až sem, je to pro mě jako autora tohoto článku obrovský úspěch. Chci jen říct,

" data-card-controls="0" data-card-theme="light">

Pokud vám to bylo užitečné, dejte like/sdílejte, aby se to dostalo i k ostatním. Chcete-li dostávat upozornění e-mailem na mé nejnovější příspěvky, přihlaste se k odběru mého blogu kliknutím na Přihlásit se k odběru tlačítko v horní části stránky. Můžete mě také sledovat na twitteru @tapasadhikary.