Динамически импортировать изображения из каталога с помощью webpack

104

Итак, вот мой текущий рабочий процесс для импорта изображений и значков в веб-пакет через 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} />

Это быстро становится грязным. Вот что я хочу:

import * from './images'

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

Я чувствую, что должен быть какой-то способ динамически импортировать все файлы из определенного каталога в качестве их имени без расширения, а затем использовать эти файлы по мере необходимости.

Кто-нибудь видел, как это делается, или есть какие-то мысли о том, как лучше это сделать?


ОБНОВИТЬ:

Используя выбранный ответ, я смог сделать это:

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']} />
клиноре
источник
8
Я просто хочу отметить, что .mapтакой тип ожидает возвращаемого значения. В вашем случае forEachвместо этого можно было бы использовать старый добрый .
Брэм Ванрой
1
@BramVanroy или просто сделайте его однострочным и верните r.keys.().map(...)напрямую ...
LinusGeffarth

Ответы:

124

Я чувствую, что должен быть какой-то способ динамически импортировать все файлы из определенного каталога в качестве их имени без расширения, а затем использовать эти файлы по мере необходимости.

Не в ES6. Весь смысл importи в exportтом, что зависимости можно определять статически , т.е. без выполнения кода.

Но поскольку вы используете веб-пакет, взгляните на require.context. Вы должны уметь делать следующее:

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

const images = importAll(require.context('./', false, /\.(png|jpe?g|svg)$/));
Феликс Клинг
источник
Интересно ... Итак, в настоящее время я использую файл-загрузчик в своей конфигурации веб-пакета, чтобы переместить все эти файлы в одно место в общедоступном каталоге моих приложений. Здесь этого не происходит. Как загрузчики работают с require.context?
klinore
1
«Этого здесь не происходит». Вы имеете в виду, что файлы не отображаются в выходной папке? Вы все еще получаете путь к ним с помощью приведенного выше кода? Я не думаю, что нужно делать что-то особенное, чтобы поддержать это ...
Феликс Клинг
2
Не уверен, почему мой загрузчик не запускался, и я получал исходный путь. Загрузчик теперь работает нормально и указан правильный путь! Потрясающие. Спасибо за введение в require.context: D!
klinore
1
Что использовать, если у меня есть приложение create-react-app (cra)? в cra importAllничего не вернул.
giorgim
2
У меня это работает, но как бы вы написали то же самое на TypeScript? Какие для него подходящие типы?
Максимилиан Линдси,
10

Это просто. Вы можете использовать require(статический метод, импорт предназначен только для динамических файлов) внутри render. Как в примере ниже:

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

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

    return (
    <img src={graphImage}/>
    )
}
Робсоншре
источник
1
Я думаю, что нужно сделать больше, чтобы эта работа работала с webpack.
Феликс Клинг,
Можете ли вы вставить сюда свой файл конфигурации webpack?
Robsonsjre
3
Это великолепно. Благодарность!
poweratom
1
Я бы не рекомендовал использовать такие глобальные требования - см. Eslint.org/docs/rules/global-require
markyph
7

У меня есть каталог флагов стран png с названиями au.png, nl.png и т. Д. Итак, у меня есть:

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

Я читал такой файл:

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

Функциональный подход к решению этой проблемы:

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)$/)
);
Патрик Сантос
источник
Спасибо! Прекрасно работает!
Закалве,
4

ОБНОВЛЕНИЕ Похоже, я не совсем понял вопрос. @ Феликс понял все правильно, так что проверьте его ответ. Следующий код будет работать только в среде Nodejs.

Добавить index.jsфайл в imagesпапку

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;

Это позволит вам импортировать все из другого файла, а Wepback проанализирует его и загрузит необходимые файлы.

kbariotis
источник