Impor gambar secara dinamis dari direktori menggunakan webpack

104

Jadi, inilah alur kerja saya saat ini untuk mengimpor gambar dan ikon di webpack melalui 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} />

Ini menjadi cepat berantakan. Inilah yang saya inginkan:

import * from './images'

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

Saya merasa pasti ada cara untuk mengimpor semua file secara dinamis dari direktori tertentu sebagai nama mereka tanpa ekstensi, dan kemudian menggunakan file tersebut sesuai kebutuhan.

Adakah yang melihat ini dilakukan, atau memiliki pemikiran tentang cara terbaik untuk melakukannya?


MEMPERBARUI:

Dengan menggunakan jawaban yang dipilih, saya dapat melakukan ini:

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
sumber
8
Saya hanya ingin menunjukkan bahwa .mapmengharapkan nilai pengembalian. Dalam kasus Anda, seseorang akan menggunakan ol yang bagus forEachsebagai gantinya.
Bram Vanroy
1
@BramVanroy atau cukup buat satu baris dan r.keys.().map(...)langsung kembali ...
LinusGeffarth

Jawaban:

124

Saya merasa pasti ada cara untuk mengimpor semua file secara dinamis dari direktori tertentu sebagai nama mereka tanpa ekstensi, dan kemudian menggunakan file tersebut sesuai kebutuhan.

Tidak di ES6. Inti dari importdan exportadalah bahwa dependensi dapat ditentukan secara statis , yaitu tanpa menjalankan kode.

Tetapi karena Anda menggunakan webpack, lihatlah require.context. Anda harus bisa melakukan hal berikut:

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

const images = importAll(require.context('./', false, /\.(png|jpe?g|svg)$/));
Felix Kling
sumber
Menarik ... Jadi, saat ini saya menggunakan 'file-loader' di konfigurasi webpack saya untuk memindahkan semua file tersebut ke satu lokasi di direktori publik aplikasi saya. Itu tidak terjadi di sini. Bagaimana cara kerja loader dengan require.context?
klinore
1
"Itu tidak terjadi di sini" Maksudmu file tidak muncul di folder keluaran? Apakah Anda masih mendapatkan jalur ke mereka dengan kode di atas? Saya tidak berpikir perlu melakukan sesuatu yang khusus untuk mendukung ini ...
Felix Kling
2
Tidak yakin mengapa, beli loader saya tidak dijalankan dan saya mendapatkan jalur aslinya. Loader sekarang berfungsi dengan baik dan jalur yang benar telah disediakan! Hebat. Terima kasih telah memperkenalkan ke require.context: D!
klinore
1
Apa yang harus digunakan jika saya memiliki create-react-app (cra)? dalam cra importAllmengembalikan apa-apa.
giorgim
2
Ini berfungsi untuk saya, tetapi bagaimana Anda akan menulis hal yang sama di TypeScript? Apa tipe yang tepat untuk itu?
Maximilian Lindsey
10

Mudah. Anda dapat menggunakan require(metode statis, impor hanya untuk file dinamis) di dalam render. Seperti contoh di bawah ini:

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

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

    return (
    <img src={graphImage}/>
    )
}
Robsonsjre
sumber
1
Saya pikir lebih banyak yang harus dilakukan untuk membuat ini berfungsi dengan webpack.
Felix Kling
Dapatkah Anda menempelkan file konfigurasi webpack Anda di sini?
Robsonsjre
3
Ini mulia. Terima kasih!
poweratom
1
Saya tidak akan merekomendasikan penggunaan persyaratan
markyph
7

Saya memiliki direktori bendera negara png bernama seperti au.png, nl.png dll. Jadi saya punya:

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

Saya membaca file seperti ini:

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

Pendekatan fungsional untuk menyelesaikan masalah ini:

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
sumber
Terima kasih! Bekerja dengan sempurna!
Zakalwe
4

MEMPERBARUI Sepertinya saya tidak begitu mengerti pertanyaannya. @Felix benar, jadi periksa jawabannya. Kode berikut hanya akan berfungsi di lingkungan Nodejs.

Tambahkan index.jsfile ke dalam imagesfolder

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;

Ini akan memungkinkan Anda untuk mengimpor semuanya dari file lain dan Wepback akan mengurai dan memuat file yang diperlukan.

kbariotis.dll
sumber