Importe imágenes dinámicamente desde un directorio usando webpack

100

Así que aquí está mi flujo de trabajo actual para importar imágenes e íconos en el paquete web a través de 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} />

Esto se complica rápidamente. Esto es lo que quiero:

import * from './images'

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

Siento que debe haber alguna forma de importar dinámicamente todos los archivos de un directorio específico con su nombre sin extensión, y luego usar esos archivos según sea necesario.

¿Alguien vio esto hecho, o tiene alguna idea sobre la mejor manera de hacerlo?


ACTUALIZAR:

Usando la respuesta seleccionada, pude hacer esto:

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
fuente
7
Solo me gustaría señalar que .mapespera un valor de retorno. En su caso, uno usaría un buen ol 'en su forEachlugar.
Bram Vanroy
1
@BramVanroy o simplemente conviértalo en una sola línea y regrese r.keys.().map(...)directamente ...
LinusGeffarth

Respuestas:

120

Siento que debe haber alguna forma de importar dinámicamente todos los archivos de un directorio específico con su nombre sin extensión, y luego usar esos archivos según sea necesario.

No en ES6. El objetivo de importy exportes que las dependencias se pueden determinar estáticamente , es decir, sin ejecutar código.

Pero dado que está usando webpack, eche un vistazo a require.context. Debería poder hacer lo siguiente:

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

const images = importAll(require.context('./', false, /\.(png|jpe?g|svg)$/));
Felix Kling
fuente
Interesante ... Entonces, actualmente estoy usando 'file-loader' en mi configuración de paquete web para mover todos esos archivos a una sola ubicación en mi directorio público de aplicaciones. Eso no está sucediendo aquí. ¿Cómo funcionan los cargadores con require.context?
klinore
1
"Eso no está sucediendo aquí" ¿ Quiere decir que los archivos no aparecen en la carpeta de salida? Sin embargo, ¿todavía obtienes la ruta hacia ellos con el código anterior? No creo que sea necesario hacer nada especial para apoyar esto ...
Felix Kling
2
No estoy seguro de por qué, comprar mi cargador no se estaba ejecutando y estaba obteniendo la ruta original. ¡El cargador está funcionando bien ahora y se está proporcionando la ruta correcta! Increíble. Gracias por presentarnos en require.context: D!
klinore
1
¿Qué usar si tengo create-react-app (cra)? en cra importAllno devolvió nada.
giorgim
2
Esto funciona para mí, pero ¿cómo escribirías lo mismo en TypeScript? ¿Cuáles serían los tipos correctos para ello?
Maximilian Lindsey
10

Es fácil. Puede usar require(un método estático, la importación es solo para archivos dinámicos) dentro del render. Como el siguiente ejemplo:

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

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

    return (
    <img src={graphImage}/>
    )
}
Robsonsjre
fuente
1
Creo que se necesita hacer más para que esto funcione con webpack.
Felix Kling
¿Puedes pegar el archivo de configuración de tu paquete web aquí?
Robsonsjre
3
Esto es glorioso. ¡Gracias!
poweratom
1
No recomendaría el uso de requisitos globales como este; consulte eslint.org/docs/rules/global-require
markyph
7

Tengo un directorio de banderas de países png llamado como au.png, nl.png, etc. Así que tengo:

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

Leí un archivo como este:

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
fuente
5

Un enfoque funcional para resolver este 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)$/)
);
Patrick Santos
fuente
¡Gracias! ¡Funciona perfectamente!
Zakalwe
4

ACTUALIZAR Parece que no entendí bien la pregunta. @Felix lo hizo bien, así que verifique su respuesta. El siguiente código funcionará solo en un entorno Nodejs.

Agregar un index.jsarchivo en la imagescarpeta

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;

Esto le permitirá importar todo desde otro archivo y Wepback lo analizará y cargará los archivos necesarios.

kbariotis
fuente