I am using webpack to manage a reactjs project. I want to load images in javascript by webpack file-loader
. Below is the webpack.config.js:
const webpack = require('webpack'); const path = require('path'); const NpmInstallPlugin = require('npm-install-webpack-plugin'); const PATHS = { react: path.join(__dirname, 'node_modules/react/dist/react.min.js'), app: path.join(__dirname, 'src'), build: path.join(__dirname, './dist') }; module.exports = { entry: { jsx: './app/index.jsx', }, output: { path: PATHS.build, filename: 'app.bundle.js', }, watch: true, devtool: 'eval-source-map', relativeUrls: true, resolve: { extensions: ['', '.js', '.jsx', '.css', '.less'], modulesDirectories: ['node_modules'], alias: { normalize_css: __dirname + '/node_modules/normalize.css/normalize.css', } }, module: { preLoaders: [ { test: /.js$/, loader: "source-map-loader" }, ], loaders: [ { test: /.html$/, loader: 'file?name=[name].[ext]', }, { test: /.jsx?$/, exclude: /node_modules/, loader: 'babel-loader?presets=es2015', }, {test: /.css$/, loader: 'style-loader!css-loader'}, {test: /.(jpe?g|png|gif|svg)$/i, loader: "file-loader?name=/public/icons/[name].[ext]"}, { test: /.js$/, exclude: /node_modules/, loaders: ['babel-loader?presets=es2015'] } ] }, plugins: [ new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false, }, output: { comments: false, }, }), new NpmInstallPlugin({ save: true // --save }), new webpack.DefinePlugin({ "process.env": { NODE_ENV: JSON.stringify("production") } }), ], devServer: { colors: true, contentBase: __dirname, historyApiFallback: true, hot: true, inline: true, port: 9091, progress: true, stats: { cached: false } } }
I used this line to load image files and copy them to dist/public/icons directory and keep the same file name.
{test: /.(jpe?g|png|gif|svg)$/i, loader: "file-loader?name=/public/icons/[name].[ext]"}
But I have two problems when using it. When I run webpack
command, the image file was copied to dist/public/icons/ directory as expected. However it was also copied to dist directory with this file name “df55075baa16f3827a57549950901e90.png”.
Below is my project structure:
Another problem is that I used below code to import this image file but it is not showing on the browser. If I am using url ‘public/icons/imageview_item_normal.png’ on the img tag, it works fine. How to use the object imported from the image file?
import React, {Component} from 'react'; import {render} from 'react-dom'; import img from 'file!../../public/icons/imageview_item_normal.png' export default class MainComponent extends Component { render() { return ( <div style={styles.container}> download <img src={img}/> </div> ) } } const styles = { container: { width: '100%', height: '100%', } }
Advertisement
Answer
Regarding problem #1
Once you have the file-loader configured in the webpack.config, whenever you use import/require it tests the path against all loaders, and in case there is a match it passes the contents through that loader. In your case, it matched
{ test: /.(jpe?g|png|gif|svg)$/i, loader: "file-loader?name=/public/icons/[name].[ext]" } // For newer versions of Webpack it should be { test: /.(jpe?g|png|gif|svg)$/i, loader: 'file-loader', options: { name: '/public/icons/[name].[ext]' } }
and therefore you see the image emitted to
dist/public/icons/imageview_item_normal.png
which is the wanted behavior.
The reason you are also getting the hash file name, is because you are adding an additional inline file-loader. You are importing the image as:
'file!../../public/icons/imageview_item_normal.png'.
Prefixing with file!
, passes the file into the file-loader again, and this time it doesn’t have the name configuration.
So your import should really just be:
import img from '../../public/icons/imageview_item_normal.png'
Update
As noted by @cgatian, if you actually want to use an inline file-loader, ignoring the webpack global configuration, you can prefix the import with two exclamation marks (!!):
import '!!file!../../public/icons/imageview_item_normal.png'.
Regarding problem #2
After importing the png, the img
variable only holds the path the file-loader “knows about”, which is public/icons/[name].[ext]
(aka "file-loader? name=/public/icons/[name].[ext]"
). Your output dir “dist” is unknown.
You could solve this in two ways:
- Run all your code under the “dist” folder
- Add
publicPath
property to your output config, that points to your output directory (in your case ./dist).
Example:
output: { path: PATHS.build, filename: 'app.bundle.js', publicPath: PATHS.build },