最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Images not displaying on production build, only in dev (webpacksass-loader) - Stack Overflow

programmeradmin0浏览0评论

When running my app on webpack dev server, all my image files are working whether as img tags in my index.html or background-image: url()..

Running my project though in production build, I am getting file reference errors that they cannot be found.

GET file:///img/featured.png net::ERR_FILE_NOT_FOUND
GET file:///img/header-img1-1.jpg net::ERR_FILE_NOT_FOUND

I added the copy webpack plugin as I thought this would move all images from my src/img folder to my img folder inside dist.

Should I be using the contentBase option for webpack-dev-server? Or is the copy-webpack-plugin not getting the correct reference? Super confused

Project tree:

- webpack.config.js
- package.json
- .babelrc
- src
  - js
    - index.js
    - ...
  - img
    - ALL IMAGES LOCATED HERE
  - scss
    - layout
      - landing.scss
      - brands.scss 
    - base
  - index.html

inside landing.scss i have used

background: url('~/img/img-title.png')

same in other files like brands.scss

background: url('~/img/img-title.png')

Which has all worked fine, and I think I've confused myself with how images are referenced with webpack/sass loader, and can't seem to work out how to get the image paths to work for both dev/production, i can only seem to get one working at a time.

production tree:

- dist
  - css
  - img
  - js
  - index.html

webpack.config.js:

const path = require('path');
const autoprefixer = require('autoprefixer');

const ExtractTextPlugin = require('extract-text-webpack-plugin');
const extractPlugin = new ExtractTextPlugin({
  filename: 'css/main.css'
});

const HtmlWebpackPlugin = require('html-webpack-plugin');
const ScriptExtPlugin = require('script-ext-html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = (env) => {
  const isProduction = env.production === true

  return {
	entry: './src/js/index.js',
	output: {
		path: path.resolve(__dirname, 'dist'),
		filename: 'js/bundle.js', 
	},
	module: {
	  rules: [
		{
	      test: /\.js$/,
	      include: path.resolve(__dirname, 'src'),
		  use: 'babel-loader' 
		},
		{
		  test: /\.css$|\.scss$/,
		  include: path.resolve(__dirname, 'src'),
		  use: extractPlugin.extract({  
			fallback: "style-loader",
			use: [
			  { loader: 'css-loader', options: { importLoaders: 2, sourceMap: true }},
			  { loader: 'postcss-loader', options: { sourceMap: true, plugins: () => [autoprefixer] }},
			  { loader: 'sass-loader', options: { sourceMap: true }},
			],
		  }) 
		},
		{
		  test: /\.html$/,
		  use: ['html-loader']
		},
		{
		  test: /\.(jpe?g|png|gif)$/,
		  use: [
			{
		      loader: 'url-loader',
			  options: {
				limit: 1000,
				name: 'img/[name].[ext]',
			  }
			}
		  ]
		}
	  ]
	},
	plugins: [
	  extractPlugin,
	  new HtmlWebpackPlugin({ 
		filename: 'index.html',
		inject: true,
		template: './src/index.html'
	  }),
	  new ScriptExtPlugin({
	    defaultAttribute: 'async'
	  }),
	  new CleanWebpackPlugin(['dist']),
	  new CopyWebpackPlugin([
	    {from:'src/img',to:'img'} 
	  ]), 
	]
  }
}

When running my app on webpack dev server, all my image files are working whether as img tags in my index.html or background-image: url()..

Running my project though in production build, I am getting file reference errors that they cannot be found.

GET file:///img/featured.png net::ERR_FILE_NOT_FOUND
GET file:///img/header-img1-1.jpg net::ERR_FILE_NOT_FOUND

I added the copy webpack plugin as I thought this would move all images from my src/img folder to my img folder inside dist.

Should I be using the contentBase option for webpack-dev-server? Or is the copy-webpack-plugin not getting the correct reference? Super confused

Project tree:

- webpack.config.js
- package.json
- .babelrc
- src
  - js
    - index.js
    - ...
  - img
    - ALL IMAGES LOCATED HERE
  - scss
    - layout
      - landing.scss
      - brands.scss 
    - base
  - index.html

inside landing.scss i have used

background: url('~/img/img-title.png')

same in other files like brands.scss

background: url('~/img/img-title.png')

Which has all worked fine, and I think I've confused myself with how images are referenced with webpack/sass loader, and can't seem to work out how to get the image paths to work for both dev/production, i can only seem to get one working at a time.

production tree:

- dist
  - css
  - img
  - js
  - index.html

webpack.config.js:

const path = require('path');
const autoprefixer = require('autoprefixer');

const ExtractTextPlugin = require('extract-text-webpack-plugin');
const extractPlugin = new ExtractTextPlugin({
  filename: 'css/main.css'
});

const HtmlWebpackPlugin = require('html-webpack-plugin');
const ScriptExtPlugin = require('script-ext-html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = (env) => {
  const isProduction = env.production === true

  return {
	entry: './src/js/index.js',
	output: {
		path: path.resolve(__dirname, 'dist'),
		filename: 'js/bundle.js', 
	},
	module: {
	  rules: [
		{
	      test: /\.js$/,
	      include: path.resolve(__dirname, 'src'),
		  use: 'babel-loader' 
		},
		{
		  test: /\.css$|\.scss$/,
		  include: path.resolve(__dirname, 'src'),
		  use: extractPlugin.extract({  
			fallback: "style-loader",
			use: [
			  { loader: 'css-loader', options: { importLoaders: 2, sourceMap: true }},
			  { loader: 'postcss-loader', options: { sourceMap: true, plugins: () => [autoprefixer] }},
			  { loader: 'sass-loader', options: { sourceMap: true }},
			],
		  }) 
		},
		{
		  test: /\.html$/,
		  use: ['html-loader']
		},
		{
		  test: /\.(jpe?g|png|gif)$/,
		  use: [
			{
		      loader: 'url-loader',
			  options: {
				limit: 1000,
				name: 'img/[name].[ext]',
			  }
			}
		  ]
		}
	  ]
	},
	plugins: [
	  extractPlugin,
	  new HtmlWebpackPlugin({ 
		filename: 'index.html',
		inject: true,
		template: './src/index.html'
	  }),
	  new ScriptExtPlugin({
	    defaultAttribute: 'async'
	  }),
	  new CleanWebpackPlugin(['dist']),
	  new CopyWebpackPlugin([
	    {from:'src/img',to:'img'} 
	  ]), 
	]
  }
}

Share Improve this question edited Apr 1, 2018 at 4:01 joshuaaron asked Apr 1, 2018 at 3:42 joshuaaronjoshuaaron 9062 gold badges19 silver badges29 bronze badges
Add a ment  | 

4 Answers 4

Reset to default 5

I think you're using different folder structure on production than on local, i.e. on local, it's just http://localhost:PORT/app, but on prod, it must be similar to http://produrl/Some_Folder/app

Now ing to actual issue - It's your CSS loader.

By default css-loader, has url=true, causing all URLs to be mapped relative to root. Hence this works for you -

background: url('~/img/img-title.png')

but this doesn't

background: url('../../img/img-title.png')

Just set url=false, and you'll be able to provide relative URL and it'll load for all enviorments correctly.

While the accepted answer may works in your specific scenario, I think there is a better solution that do not involve disabling css-loader url() handling and will works better in most of situation.

Tilde ~ problem

When you use ~ to import something in css, css-loader will search for that file inside node_modules. Your images are inside src/img folder so you do not need tilde ~ to import your images.

url() problem

sass-loader doesn't not resolve url() correctly if they are not in the same directory of the entry-file.

In your specific example you import some urls inside src/scss/layout/landing.scss and src/scss/layout/brands.scss but I guess your main scss entry point is inside src/scss folder.

Note: for "main scss entry point" I mean the scss file that you import inside your javascript entry point src/js/index.js

So, in your example, every images imported within a scss file that is not inside src/scss folder will trow an error.

To solve this problem use resolve-url-loader which resolves relative paths in url() statements based on the original source file.

[
  { loader: 'css-loader'}, // set `importLoaders: 3` if needed but should works fine without it
  { loader: 'postcss-loader'},
  { loader: 'resolve-url-loader' }, // add this
  { loader: 'sass-loader',
    // sourceMap is required for 'resolve-url-loader' to work
    options: { sourceMap: true }
  }
]

CopyWebpackPlugin is optional

Based on your configuration you do not need CopyWebpackPlugin because your images are already handle by url-loader.

Note: url-loader doesn't output your images but inline them, images are outputted by file-loader when the file is greater than the limit (in bytes).

file-loader will output your images in dist/img folder, because you set name: 'img/[name].[ext]', and you set output.path: path.resolve(__dirname, 'dist').

Hey I think your issue is just in how you're referencing the image in your scss.

Based on your folder structure it should be:

background: url('../../img/img-title.png')

You could modify the "publicPath" URL (which is relative to the server root) to match the file structure.

//webpack.config.js
 ...
   module: {
    rules: [
      {
        test: /\.(jpe?g|png|gif|svg)$/i,
        use: [
          {
            loader: "file-loader",
            options: {
              name: "[name].[ext]",
              outputPath: "assets/images/",
              publicPath: "/other-dir/assets/images/"
            }
          }
        ]
      }
    ]
  }

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论