Изображения не отображаются в производственной сборке, только в dev (webpack / sass-loader)

При запуске моего приложения на сервере webpack dev все мои файлы изображений работают независимо от того, являются ли они тегами img в моем index.html или background-image: url () ..

Выполняя свой проект в производственной сборке, я получаю ошибки ссылки на файлы, которые не могут быть найдены.

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

Я добавил плагин copy webpack, так как думал, что это переместит все изображения из моей папки src / img в мою папку img внутри dist.

Должен ли я использовать опцию contentBase для webpack-dev-server? Или плагин copy-webpack-plugin не получает правильную ссылку? Супер запутанный

Дерево проекта:

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

внутри Landing.scss я использовал

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

то же самое в других файлах, например, в файлах brand.scss

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

Что все работало нормально, и я думаю, что я запутался в том, как изображения ссылаются на загрузчик webpack / sass, и, похоже, не могу понять, как заставить пути к изображениям работать как для разработки, так и для производства, я могу только кажется, работают по одному.

производственное дерево:

- 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'} 
	  ]), 
	]
  }
}


person joshuaaron    schedule 01.04.2018    source источник


Ответы (4)


Я думаю, вы используете другую структуру папок на производстве, чем на локальном, то есть на локальном, это просто http://localhost:PORT/app, но на продукте он должен быть похож на http://produrl/Some_Folder/app

Теперь перейдем к актуальной проблеме - это ваш загрузчик CSS.

По умолчанию css-loader имеет url = true, в результате чего все URL-адреса сопоставляются относительно корневого. Следовательно, это работает для вас -

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

но это не

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

Просто установите url = false, и вы сможете указать относительный URL-адрес, и он будет правильно загружаться для всех сред.

person Bikas    schedule 02.04.2018
comment
Ах, спасибо вам огромное за это! Я бы никогда не подумал, что это css-loader, после прочтения в основном проблем с URL-адресами и загрузчиком sass. - person joshuaaron; 02.04.2018
comment
Пожалуйста. В настоящее время я нахожусь в одной лодке, но мои проблемы еще хуже, поскольку задействованы компоненты узла. - person Bikas; 02.04.2018

Хотя принятый ответ может работать в вашем конкретном сценарии, я думаю, что есть лучшее решение, которое не включает отключение css-loader url() обработки и будет работать лучше в большинстве ситуаций.

Тильда ~ проблема

Когда вы используете ~ для импорта чего-либо в css, css-loader будет искать этот файл внутри node_modules. Ваши изображения находятся внутри папки src / img, поэтому вам не нужна тильда ~ для импорта изображений.

url() проблема

sass-loader не разрешает url() правильно, если они не находятся в том же каталоге входного файла.

В вашем конкретном примере вы импортируете некоторые URL-адреса внутри src / scss / layout / landing.scss и src / scss / layout / Brands.scss, но я думаю, ваша основная точка входа в scss находится внутри папки src / scss.

Примечание. Под "основной точкой входа в scss" я подразумеваю файл scss, который вы импортируете внутри точки входа javascript src / js / index.js

Итак, в вашем примере каждое изображение, импортированное в файл scss, который не внутри папки src / scss, будет вызывать ошибку.

Чтобы решить эту проблему, используйте resolve-url-loader, который разрешает относительные пути в url () утверждения, основанные на оригинальном исходном файле.

[
  { 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 не является обязательным

В зависимости от вашей конфигурации вам не нужно CopyWebpackPlugin, потому что ваши изображения уже обрабатываются url-loader.

Примечание. url-loader не выводит ваши изображения, а встраивает их, изображения выводятся с помощью загрузчика файлов, когда размер файла превышает установленный предел (в байтах).

file-loader выведет ваши изображения в папку dist / img, потому что вы устанавливаете name: 'img/[name].[ext]', а вы устанавливаете output.path: path.resolve(__dirname, 'dist').

person pldg    schedule 05.04.2018

Эй, я думаю, ваша проблема заключается только в том, как вы ссылаетесь на изображение в своем scss.

В зависимости от структуры ваших папок это должно быть:

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

person RobIsAnxious    schedule 01.04.2018
comment
привет @RobIsAnxious, я пробовал это, но получаю сообщение об ошибке сборки: Модуль не найден: Ошибка: не удается разрешить '../../img/image-title.png' - person joshuaaron; 01.04.2018

Вы можете изменить URL-адрес publicPath (относящийся к корню сервера) в соответствии с файловой структурой.

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

person ksan    schedule 06.11.2020