Importa dinamicamente le immagini da una directory utilizzando webpack


100

Quindi ecco il mio attuale flusso di lavoro per importare immagini e icone nel webpack tramite 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} />

Questo diventa disordinato velocemente. Ecco cosa voglio:

import * from './images'

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

Sento che ci deve essere un modo per importare dinamicamente tutti i file da una directory specifica come loro nome senza estensione e quindi utilizzare quei file secondo necessità.

Qualcuno l'ha visto, o ha qualche idea sul modo migliore per farlo?


AGGIORNARE:

Utilizzando la risposta selezionata, sono stato in grado di farlo:

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']} />

7
Mi piace solo sottolineare che si .mapaspetta un valore di ritorno. Nel tuo caso, invece, si userebbe un buon vecchio forEach.
Bram Vanroy

1
@BramVanroy o semplicemente trasformalo in una battuta e torna r.keys.().map(...)direttamente ...
LinusGeffarth

Risposte:


120

Sento che ci deve essere un modo per importare dinamicamente tutti i file da una directory specifica come loro nome senza estensione e quindi utilizzare quei file secondo necessità.

Non in ES6. Il punto centrale di imported exportè che le dipendenze possono essere determinate staticamente , cioè senza eseguire codice.

Ma dal momento che stai usando webpack, dai un'occhiata a require.context. Dovresti essere in grado di fare quanto segue:

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

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

Interessante ... Quindi, attualmente sto utilizzando 'file-loader' nella mia configurazione del pacchetto web per spostare tutti quei file in una singola posizione nella directory pubblica delle mie app. Non sta succedendo qui. Come funzionano i caricatori con require.context?
klinore

1
"Non sta succedendo qui" Vuoi dire che i file non vengono visualizzati nella cartella di output? Tuttavia, ottieni ancora il percorso per raggiungerli con il codice sopra? Non credo che ci sia bisogno di fare nulla di speciale per supportare questo ...
Felix Kling

2
Non so perché, acquista il mio caricatore non era in esecuzione e stavo ottenendo il percorso originale. Il caricatore ora funziona correttamente e viene fornito il percorso corretto! Eccezionale. Grazie per aver introdotto in require.context: D!
klinore

1
Cosa usare se ho create-react-app (cra)? in cra importAllrestituito nulla.
giorgim

2
Questo funziona per me, ma come scriveresti la stessa cosa in TypeScript? Quali sarebbero i tipi corretti per questo?
Maximilian Lindsey

10

È facile. Puoi usare require(un metodo statico, l'importazione è solo per i file dinamici) all'interno del render. Come nell'esempio seguente:

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

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

    return (
    <img src={graphImage}/>
    )
}

1
Penso che sia necessario fare di più per far funzionare questo con webpack.
Felix Kling

Puoi incollare il tuo file di configurazione del webpack qui?
Robsonsjre

3
Questo è glorioso. Grazie!
poweratom

1
Non consiglierei di utilizzare requisiti globali come questo - vedi eslint.org/docs/rules/global-require
markyph

7

Ho una directory di bandiere di paesi png denominate come au.png, nl.png ecc. Quindi ho:

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

Ho letto un file come questo:

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;

5

Un approccio funzionale per risolvere questo problema:

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

Grazie! Funziona perfettamente!
Zakalwe,

4

AGGIORNARE Sembra che non ho capito bene la domanda. @Felix ha capito bene, quindi controlla la sua risposta. Il codice seguente funzionerà solo in un ambiente Nodejs.

Aggiungi un index.jsfile nella imagescartella

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;

Ciò ti consentirà di importare tutto da un altro file e Wepback lo analizzerà e caricherà i file richiesti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.