Webpack的file-loader输出[object Module]

109

我正在使用HtmlWebpackPluginhtml-loaderfile-loader与webpack一起工作。我有一个简单的项目结构,没有使用任何框架,只使用typescript。因此,我直接将我的HTML代码编写到index.html中。我还将这个HTML文件用作HtmlWebpackPlugin的模板。

像所有网站一样,我需要放一个图片,它引用了我的资产文件夹中的PNG图片。 file-loader应该正确加载文件并将新的文件名放入src标签中,但实际上并没有。相反,作为src标签的值,我得到了[object Module]。我认为file-loader会发射某个对象,并在其运行.toString()方法时以这种方式表示。但是,我可以看到file-loader已成功处理了文件,并通过新名称发出到输出路径。我没有收到任何错误。这是我的webpack配置和index.html

const projectRoot = path.resolve(__dirname, '..');

{
  entry: path.resolve(projectRoot, 'src', 'app.ts'),
  mode: 'production',
  output: {
    path: path.resolve(projectRoot, 'dist'),
    filename: 'app.bundle.js'
  },
  resolve: {
    extensions: ['.ts', '.js']
  },
  module: {
    rules: [
      {
        test: /\.html$/i,
        use: 'html-loader'
      },
      {
        test: /\.(eot|ttf|woff|woff2|svg|png)$/i,
        use: 'file-loader'
      },
      {
        test: /\.scss$/i,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
              hmr: false
            }
          },
          {
            loader: 'css-loader',
            options: {
              sourceMap: false
            }
          },
          {
            loader: 'sass-loader',
            options: {
              sourceMap: false
            }
          }
        ]
      },
      {
        exclude: /node_modules/,
        test: /\.ts$/,
        use: 'ts-loader'
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlWebpackPlugin({
      template: path.resolve(projectRoot, 'src', 'index.html')
    }),
    new MiniCssExtractPlugin({
      filename: '[name].[hash].css',
      chunkFilename: '[id].[hash].css',
      ignoreOrder: false
    })
  ]
};

index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title></title>
  </head>
  <body class="dark">
    <header>
      <nav class="navigation">
        <div class="left">
          <img src="assets/logo.png" class="logo"> <!-- This logo is output as [object Module] -->
        </div>
        <div class="right">

        </div>
      </nav>
    </header>
  </body>
</html>

项目结构:

config/
    webpack.config.js
dist/
src/
    styles/
    assets/
        logo.png
    index.html
    app.ts

编辑 我的 package.json 依赖项:

"clean-webpack-plugin": "^3.0.0",
"css-loader": "^3.2.0",
"file-loader": "^5.0.2",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.8.0",
"node-sass": "^4.13.0",
"sass-loader": "^8.0.0",
"style-loader": "^1.0.0",
"ts-loader": "^6.2.1",
"typescript": "^3.7.2",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0"
11个回答

243

根据 file-loader 的 文档

默认情况下,file-loader 生成使用 ES 模块语法的 JS 模块。在某些情况下,使用 ES 模块是有益的,比如在模块拼接和树摇优化方面。

似乎 webpack 将 ES 模块的 require() 调用解析为一个看起来像这样的对象:{default: module},而不是扁平化的模块本身。这种行为在某种程度上引起了争议,并在此问题中讨论。

因此,要使您的 src 属性正确解析,您需要能够访问导出模块的 default 属性。如果您正在使用框架,则应该能够执行类似于以下操作:

<img src={require('assets/logo.png').default}/> <!-- React -->
<!-- OR -->
<img src="require('assets/logo.png').default"/> <!-- Vue -->

或者,您可以启用文件加载器的CommonJS模块语法,webpack将直接将其解析为该模块本身。在您的webpack配置中设置 esModule:false

webpack.config.js:

 {
        test: /\.(png|jpe?g|gif)$/i,
        use: [
          {
            loader: 'file-loader',
            options: {
              esModule: false,
            },
          },
        ],
      },

这个可行。但是仍有点神奇。如果你对为什么会出现这种情况有想法,也可以在你的回答中解释一下吗?谢谢。 - Bora
1
@Bora -- 我做了更多的研究并更新了答案。 - stellr42
4
在将Angular 8更新至Angular 9时,此问题困扰了我,因为这会将file-loader从版本4.2.0升级到6.0.0。使用require(...).default修复了我的问题。 - ebhh2001
1
非常感谢您指出 esModule: false 标志。我一直无法弄清楚为什么我的 js 可以正常处理图像,但我的 scss 却返回一个对象模块。我在这上面花了好几个小时! - sashaikevich
使用 require(...).default 对我很有效。谢谢。 - Muthulakshmi M
显示剩余2条评论

18

@stellr42建议在您的file-loader配置中使用esModule:false是当前最佳解决方法。

但是,这实际上是html-loader中的一个错误,可以在此处跟踪:https://github.com/webpack-contrib/html-loader/issues/203

看起来file-loadercss-loader和其他相关的支持ES模块,但html-loader被遗漏了。

一旦修复了这个错误,最好删除esModule:false并简单升级html-loader,因为ES模块提供了一些微小的优势(如docs中所述)

或者,如果(像我一样),您发现这个问题是因为在CSS中加载图像(而不是从HTML中加载),那么解决方法就是升级css-loader,无需禁用ES模块。


1
终于找到有人遇到同样的问题了!但是截至2022年7月,即使在我的开发依赖中使用了css-loader 6.7.0,我仍然需要在我的Webpack配置中设置esModule: false才能看到我的.svg文件显示。有什么线索吗? - informaticienzero
截至今天的04/2023,我仍然需要这个esModule: false :/,并且需要file-loader:6.2.0。 - jav974

8

刚刚更新了我的file-loader到^5.0.2。

我知道esModule: false被建议作为解决方案,但这对我没有起作用。

我的解决方法是<img src={require('assets/logo.png').default}/>,这很奇怪。第一次使用.default,但它有效。


5

这个问题出现在 file-loader 版本 5.0.2 上,早期版本没有调用 default 属性而正常工作。


3

不要写成这样:<img src="require('assets/logo.png').default"/>

应该写成这样:<img src={require('assets/logo.png').default}/>


这是最佳方法。 - Nagibaba

2

使用 "default" 和 require 来在 React.js 中显示动态图片

src={require('../images/'+image_name+'.png').default}


1

对于Next.JS

// require(...).default.src
<img src={require("../public/images/avatar.png").default.src} width={256} height={256} />


1

最近我将Laravel Mix从v4升级到v6后遇到了同样的问题。在我的Vue组件中这个可以正常工作。

<img :src="require('./assets/profile.png').default"/>

0

我在vuejs中遇到了同样的问题,esModule:false对我没有起作用。 相反,我使用了@kerubim的解决方案,并解决了问题,但只在生产模式下有效,在开发模式下会出现一些错误。

因此,我在util.js中编写了这个函数来解决我的问题。

maybeDefault: (module) => {

  if (typeof module === "object") {

    module = module.default;
  }

  return module;
},

使用示例:

let logo = 'logo.svg';
util.maybeDefault(require(`img/svg/logos/${logo}`));

@Dharman 抱歉,我认为很清楚了,是的。我使用了这个函数并解决了我的问题。 但在 vuejs 中,当然也可以用于其他目的。 - Farshadi
这在 Gatsby 中对我起作用,而无需重写 webpack 配置。 - lacy

0

这是一个奇怪的问题,我仍然不确定我的问题是如何解决的。

所以我删除了我的node_modulepackage-lock.json文件,并运行了npm install --force

之后它就正常工作了。


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