นำเข้ารูปภาพจากไดเร็กทอรีแบบไดนามิกโดยใช้ webpack


111

นี่คือเวิร์กโฟลว์ปัจจุบันของฉันสำหรับการนำเข้ารูปภาพและไอคอนใน webpack ผ่าน 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คาดหวังผลตอบแทน ในกรณีของคุณจะใช้ ol ที่ดีforEachแทน
Bram Vanroy

2
@BramVanroy หรือแค่ทำเป็นซับเดียวแล้วกลับr.keys.().map(...)โดยตรง ...
LinusGeffarth

คำตอบ:


128

ฉันรู้สึกว่าต้องมีวิธีการนำเข้าไฟล์ทั้งหมดแบบไดนามิกจากไดเร็กทอรีเฉพาะเป็นนามสกุลของชื่อแล้วใช้ไฟล์เหล่านั้นตามต้องการ

ไม่อยู่ใน ES6 จุดรวมของimportและexportคือการอ้างอิงสามารถกำหนดได้แบบคงที่กล่าวคือไม่มีการเรียกใช้โค้ด

แต่เนื่องจากคุณใช้ webpack ลองดูที่require.context. คุณควรจะทำสิ่งต่อไปนี้ได้:

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

const images = importAll(require.context('./', false, /\.(png|jpe?g|svg)$/));

น่าสนใจ ... ดังนั้นฉันกำลังใช้ 'file-loader' ในการกำหนดค่า webpack ของฉันเพื่อย้ายไฟล์เหล่านั้นไปยังตำแหน่งเดียวในแอปสาธารณะ นั่นไม่ได้เกิดขึ้นที่นี่ รถตักทำงานกับ require.context ได้อย่างไร?
klinore

1
"นั่นไม่ได้เกิดขึ้นที่นี่"คุณหมายความว่าไฟล์ไม่ปรากฏในโฟลเดอร์ผลลัพธ์ใช่หรือไม่? คุณยังคงได้รับเส้นทางไปยังพวกเขาด้วยรหัสด้านบนหรือไม่? ฉันไม่คิดว่าจะต้องทำอะไรเป็นพิเศษเพื่อสนับสนุนสิ่งนี้ ...
Felix Kling

2
ไม่แน่ใจว่าทำไมซื้อตัวโหลดของฉันไม่ถูกเรียกใช้และฉันได้รับเส้นทางเดิม ตัวโหลดทำงานได้ดีในขณะนี้และกำลังให้เส้นทางที่ถูกต้อง! น่ากลัว ขอบคุณสำหรับการแนะนำใน require.context: D!
klinore

1
จะใช้อะไรได้บ้างถ้าฉันมี create-react-app (cra)? ในcra importAllอะไรกลับมา
gmoniava

2
สิ่งนี้ใช้ได้กับฉัน แต่คุณจะเขียนสิ่งเดียวกันใน TypeScript ได้อย่างไร ประเภทที่ถูกต้องสำหรับมันคืออะไร?
Maximilian Lindsey

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 ได้
Felix Kling

คุณวางไฟล์ config webpack ที่นี่ได้ไหม
Robsonsjre

1
ฉันไม่แนะนำให้ใช้ global ต้องใช้แบบนี้ - ดู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;

6

แนวทางที่ใช้งานได้เพื่อแก้ปัญหานี้:

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 จะแยกวิเคราะห์และโหลดไฟล์ที่ต้องการ


0

ตัวอย่างการทำงาน:

<img src={require(`../../folder-path/${dynamic-filename}.png`).default} />

สิ่งนี้ใช้ไม่ได้ เกิดข้อผิดพลาดไม่พบโมดูล!
Simba
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.