Dynamicznie importuj obrazy z katalogu za pomocą pakietu internetowego

106

Oto mój bieżący proces importowania obrazów i ikon w pakiecie internetowym za pośrednictwem ES6:

import cat from './images/cat1.jpg'
import cat2 from './images/cat2.svg'
import doggy from './images/doggy.png'
import turtle from './images/turtle.png'

<img src={doggy} />

To szybko się brudzi. Oto, czego chcę:

import * from './images'

<img src={doggy} />
<img src={turtle} />

Wydaje mi się, że musi istnieć sposób na dynamiczne importowanie wszystkich plików z określonego katalogu jako ich nazwy bez rozszerzenia, a następnie używanie tych plików w razie potrzeby.

Czy ktoś widział, jak to się robi, lub ma jakieś przemyślenia na temat najlepszego sposobu, aby to zrobić?


AKTUALIZACJA:

Korzystając z wybranej odpowiedzi, udało mi się to zrobić:

function importAll(r) {
  let images = {};
  r.keys().map((item, index) => { images[item.replace('./', '')] = r(item); });
  return images;
}

const images = importAll(require.context('./images', false, /\.(png|jpe?g|svg)$/));

<img src={images['doggy.png']} />
klinore
źródło
8
Chciałbym tylko zaznaczyć, że tego .maprodzaju oczekuje wartości zwracanej. W twoim przypadku forEachzamiast tego użyłbyś starego dobrego .
Bram Vanroy
1
@BramVanroy lub po prostu zrób z tego jedną linijkę i wróć r.keys.().map(...)bezpośrednio ...
LinusGeffarth

Odpowiedzi:

125

Wydaje mi się, że musi istnieć sposób na dynamiczne importowanie wszystkich plików z określonego katalogu jako ich nazwy bez rozszerzenia, a następnie używanie tych plików w razie potrzeby.

Nie w ES6. Cały sens importi exportjest to, że można określić zależności statycznie , to znaczy bez wykonywania kodu.

Ale ponieważ używasz webpacka, spójrz na require.context. Powinieneś być w stanie wykonać następujące czynności:

function importAll(r) {
  return r.keys().map(r);
}

const images = importAll(require.context('./', false, /\.(png|jpe?g|svg)$/));
Felix Kling
źródło
Interesujące ... Tak więc, obecnie używam 'file-loader' w mojej konfiguracji webpack, aby przenieść wszystkie te pliki do jednej lokalizacji w publicznym katalogu moich aplikacji. To się tutaj nie dzieje. Jak działają programy ładujące z require.context?
klinore
1
„To się tutaj nie dzieje” Masz na myśli, że pliki nie pojawiają się w folderze wyjściowym? Czy nadal masz do nich ścieżkę za pomocą powyższego kodu? Nie sądzę, aby trzeba było zrobić coś specjalnego, aby to wesprzeć ...
Felix Kling,
2
Nie wiem, dlaczego, kup mój ładowacz nie był uruchamiany i otrzymywałem oryginalną ścieżkę. Program ładujący działa teraz poprawnie i zapewniona jest właściwa ścieżka! Niesamowite. Dzięki za wprowadzenie do require.context: D!
klinore
1
Czego używać, jeśli mam aplikację Create-React-App (CRA)? w cra importAllzwrócony nic.
giorgim,
2
To działa dla mnie, ale jak napisałeś to samo w TypeScript? Jakie byłyby do tego odpowiednie typy?
Maximilian Lindsey
10

To jest łatwe. Możesz użyć require(metoda statyczna, import jest tylko dla plików dynamicznych) wewnątrz render. Jak w poniższym przykładzie:

render() {
    const {
      someProp,
    } = this.props

    const graphImage = require('./graph-' + anyVariable + '.png')
    const tableImage = require('./table-' + anyVariable2 + '.png')

    return (
    <img src={graphImage}/>
    )
}
Robsonsjre
źródło
1
Myślę, że trzeba zrobić więcej, aby to działało z pakietem internetowym.
Felix Kling
Czy możesz wkleić tutaj plik konfiguracyjny pakietu internetowego?
Robsonsjre
3
To jest wspaniałe. Dzięki!
poweratom
1
Nie polecałbym używania takich wymagań
markyph
7

Mam katalog z flagami krajów png o nazwach au.png, nl.png itp. Mam:

-svg-country-flags
 --png100px
   ---au.png
   ---au.png
 --index.js
 --CountryFlagByCode.js

index.js

const context = require.context('./png100px', true, /.png$/);

const obj = {};
context.keys().forEach((key) => {
  const countryCode = key.split('./').pop() // remove the first 2 characters
    .substring(0, key.length - 6); // remove the file extension
  obj[countryCode] = context(key);
});

export default obj;

Czytałem taki plik:

CountryFlagByCode.js

import React from 'react';
import countryFlags from './index';

const CountryFlagByCode = (countryCode) => {
    return (
        <div>
          <img src={countryFlags[countryCode.toLowerCase()]} alt="country_flag" />
        </div>
      );
    };

export default CountryFlagByCode;
Tudor Morar
źródło
5

Funkcjonalne podejście do rozwiązania tego problemu:

const importAll = require =>
  require.keys().reduce((acc, next) => {
    acc[next.replace("./", "")] = require(next);
    return acc;
  }, {});

const images = importAll(
  require.context("./image", false, /\.(png|jpe?g|svg)$/)
);
Patrick Santos
źródło
Dziękuję Ci! Działa świetnie!
Zakalwe
4

AKTUALIZACJA Wygląda na to, że nie całkiem zrozumiałem pytanie. @Felix ma rację, więc sprawdź jego odpowiedź. Poniższy kod będzie działał tylko w środowisku Nodejs.

Dodaj index.jsplik do imagesfolderu

const testFolder = './';
const fs = require('fs');
const path = require('path')

const allowedExts = [
  '.png' // add any extensions you need
]

const modules = {};

const files = fs.readdirSync(testFolder);

if (files && files.length) {
  files
    .filter(file => allowedExts.indexOf(path.extname(file)) > -1)
    .forEach(file => exports[path.basename(file, path.extname(file))] = require(`./${file}`));
}

module.exports = modules;

Umożliwi to zaimportowanie wszystkiego z innego pliku, a Wepback przeanalizuje go i załaduje wymagane pliki.

kbariotis
źródło