Динамически импортировать изображения из каталога с помощью 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 проанализирует его и загрузит необходимые файлы.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.