使用Webpack动态导入目录中的图像

165

以下是我目前通过ES6在webpack中导入图像和图标的工作流程:

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']} />

14
我想指出的是,.map 函数需要有一个返回值。在你的情况下,你应该使用 forEach 函数。 - Bram Vanroy
4
@BramVanroy,或者将其简化为一行,并直接返回 r.keys.().map(...) 即可... - LinusGeffarth
6个回答

170

我觉得有一种方法可以动态地从特定目录导入所有文件并使用它们,文件名不带扩展名。

在ES6中无法实现。 importexport 的整个重点是可以通过静态方式(即不执行代码)来确定依赖关系。

但由于您正在使用webpack,请查看require.context。 您应该能够执行以下操作:

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

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

1
“这里不会发生这种事情”。你的意思是这些文件没有出现在输出文件夹中吗?但是你仍然能够通过上面的代码获取到它们的路径吗?我认为不需要做任何特殊处理来支持这个... - Felix Kling
2
不确定为什么,但我的加载器没有运行,而且我得到的是原始路径。现在加载器正常工作,提供了正确的路径!太棒了。感谢介绍 require.context :D! - klinore
1
如果我使用create-react-app(cra),而在cra中使用importAll时却返回了空值,应该使用什么? - Giorgi Moniava
5
这对我有用,但你会如何用 TypeScript 写相同的内容?它的正确类型是什么? - Maximilian Lindsey
1
如果有人想知道如何使用返回的数组,那么返回数组中的每个项目都将具有一个带有“default”键的对象,可以直接用作<img src={item.default} /> - Vivek Tiwary
显示剩余3条评论

14

很简单,您可以在 render 中使用 require (一个静态方法,而 import 只用于动态文件)。像下面的例子一样:

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
你能把你的webpack配置文件粘贴在这里吗? - Robsonsjre
1
我不建议像这样使用全局要求 - 请参阅https://eslint.org/docs/rules/global-require - markyph

13

解决这个问题的一种功能性方法:

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)$/)
);

12

我有一个按照国家命名的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;

在 Next.js 13 中,为了避免警告 - “只有普通对象可以从服务器组件传递到客户端组件。不支持模块对象。”,您需要使用 context(key).default 而不是 context(key) - Ankit Jain

7
这是我在Reactjs中制作的一个功能组件,用于简单地显示项目中media文件夹(与组件处于同一级别)中的所有图像,使用了webpack文档和这里的一些答案。
import React from 'react';

const cache = {};

function importAll(r) {
    r.keys().forEach((key) => (cache[key] = r(key)));
}
// Note from the docs -> Warning: The arguments passed to require.context must be literals!
importAll(require.context("./media", false, /\.(png|jpe?g|svg)$/));

const images = Object.entries(cache).map(module => module[1].default);


const MediaPage = () => {
    return (
        <>
            <p>Media Page..</p>

            {images.map(image => (
                <img style={{width: 100}} src={image} />
            ))}
        </>
    );
}

export default MediaPage;

来自媒体目录的渲染图像

按照我加载图像的方式,从cache对象映射时文件名会丢失,但如果你需要它们,可以直接使用cache对象,因为键是文件名。

// example with styles just for clarity
return (
    <>
        <p>Media Page..</p>

        {Object.entries(cache).map(module => {
            const image = module[1].default;
            const name = module[0].replace("./","");
            return (
                <div style={{float: 'left', padding: 10, margin: 10, border: '2px solid white' }}>
                    <img style={{width: 100, margin: 'auto', display: 'block'}} src={image} />
                    <p>{name}</p>
                </div>
            )
        })}
    </>
);

images with filenames


显示“require未定义”错误。 - Qazi Ammar
对我来说也不起作用 - undefined
src={//localhost:3000/images/${name}} 对我来说起作用了。 - undefined

5

更新 看起来我没有完全理解问题。@Felix说得对,所以请检查他的答案。以下代码仅适用于Nodejs环境。

images文件夹中添加一个index.js文件。

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将解析它并加载所需的文件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接