Importez dynamiquement des images à partir d'un répertoire à l'aide de webpack

100

Voici donc mon flux de travail actuel pour l'importation d'images et d'icônes dans Webpack via 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} />

Cela devient vite compliqué. Voici ce que je veux:

import * from './images'

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

J'ai l'impression qu'il doit y avoir un moyen d'importer dynamiquement tous les fichiers d'un répertoire spécifique sous leur nom sans extension, puis d'utiliser ces fichiers selon les besoins.

Quelqu'un a-t-il vu cela fait, ou a-t-il des idées sur la meilleure façon de procéder?


METTRE À JOUR:

En utilisant la réponse sélectionnée, j'ai pu faire ceci:

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
la source
7
Je tiens simplement à souligner que ce .mapgenre d'attentes attend une valeur de retour. Dans votre cas, on utiliserait un bon vieux à la forEachplace.
Bram Vanroy
1
@BramVanroy ou faites-en simplement un one-liner et revenez r.keys.().map(...)directement ...
LinusGeffarth

Réponses:

120

J'ai l'impression qu'il doit y avoir un moyen d'importer dynamiquement tous les fichiers d'un répertoire spécifique sous leur nom sans extension, puis d'utiliser ces fichiers selon les besoins.

Pas dans ES6. L'intérêt de importet exportest que les dépendances peuvent être déterminées de manière statique , c'est-à-dire sans exécuter de code.

Mais puisque vous utilisez webpack, jetez un œil à require.context. Vous devriez pouvoir effectuer les opérations suivantes:

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

const images = importAll(require.context('./', false, /\.(png|jpe?g|svg)$/));
Félix Kling
la source
Intéressant ... Donc, j'utilise actuellement 'file-loader' dans ma configuration webpack pour déplacer tous ces fichiers vers un seul emplacement dans le répertoire public de mes applications. Cela ne se produit pas ici. Comment les chargeurs fonctionnent-ils avec require.context?
klinore
1
"Cela ne se produit pas ici" Vous voulez dire que les fichiers n'apparaissent pas dans le dossier de sortie? Avez-vous toujours le chemin d'accès avec le code ci-dessus? Je ne pense pas qu'il faille faire quelque chose de spécial pour soutenir cela ...
Felix Kling
2
Je ne sais pas pourquoi, acheter mon chargeur n'était pas en cours d'exécution et j'obtenais le chemin d'origine. Le chargeur fonctionne correctement maintenant et le chemin correct est fourni! Impressionnant. Merci pour l'introduction dans require.context: D!
klinore
1
Que dois-je utiliser si j'ai create-react-app (cra)? en cra importAllrien retourné.
giorgim le
2
Cela fonctionne pour moi, mais comment écririez-vous la même chose dans TypeScript? Quels seraient les types appropriés pour cela?
Maximilian Lindsey le
10

C'est facile. Vous pouvez utiliser require(une méthode statique, l'importation est juste pour les fichiers dynamiques) dans le render. Comme l'exemple ci-dessous:

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

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

    return (
    <img src={graphImage}/>
    )
}
Robsonsjre
la source
1
Je pense qu'il faut faire plus pour que cela fonctionne avec Webpack.
Felix Kling
Pouvez-vous coller votre fichier de configuration webpack ici?
Robsonsjre
3
C'est glorieux. Merci!
poweratom
1
Je ne recommanderais pas d'utiliser des exigences
markyph
7

J'ai un répertoire des drapeaux de pays png nommés comme au.png, nl.png etc. Donc j'ai:

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

J'ai lu un fichier comme celui-ci:

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
la source
5

Une approche fonctionnelle pour résoudre ce problème:

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
la source
Je vous remercie! Fonctionne parfaitement!
Zakalwe le
4

MISE À JOUR Il semble que je n'ai pas bien compris la question. @Felix a bien compris, alors vérifiez sa réponse. Le code suivant fonctionnera uniquement dans un environnement Nodejs.

Ajouter un index.jsfichier dans le imagesdossier

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;

Cela vous permettra d'importer tout à partir d'un autre fichier et Wepback l'analysera et chargera les fichiers requis.

kbariotis
la source