Webpack 4 为不同级别的文件夹结构中的 img 标签生成错误的 src 路径
Webpack 4 generates wrong src path for img tags in different levels of folder structure
我有一个 Webpack 4 项目来制作多语言 admin-dashboard 具有以下文件夹和文件结构:
admin-dashboard
|
|--build
| |--assets
| | |--img
| | |--font
| |--fa
| | |--index.html ----> rtl html output (gets wrong img src like "assets/img/img.png")
| |--index.html ----> ltr html output (gets right img src like "assets/img/img.png")
| |--style.css
| |--style-rtl.css
| |--script.js
| |--script-rtl.js
|
|--config ---> containing my Webpack config files for production and development
| |--webpack.dev.js
| |--webpack.prod.js
|
|--node_modules
|--src
| |--assets
| | |--img
| | |--font
| |
| |--i18n
| | |--fa
| | | |--index.html ----> rtl html template ---> <img src="../../assets/img/img.png" />
| | |--index.html ----> ltr html template ---> <img src="../assets/img/img.png" />
| |
| |--js
| |--locales
| |--scss
| |--templates
|
|--.babelrc
|--package.json
|--postcss.config.js
我的 src
文件夹中的 i18n
文件夹中的图像路径在开发项目时是正确的,但是当我构建项目时,它在项目的根目录中创建了一个 build
文件夹它自己的资产文件夹。正如您在上面的文件结构中看到的,build 文件夹中的 ltr html 文件具有放置在 build 文件夹中的资产文件夹的正确路径,但 fa
文件夹中的 rtl 版本指的是相同的路径,这是错误的.这 2 个文件基于 src/i18n
中的 2 html 个文件制作。加上两个 html 文件的 header 中指向图标图像的所有链接在文件的构建版本中保持不变,这是另一个问题,但由 Webpack html 插件完成的其他注入链接全部正确注入html 文件中的路径。
这是我的生产模式的 webpack 配置:
webpack.prod.js
:
const webpack = require('webpack');
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
entry: {
main: './src/js/main.js',
'main-rtl': './src/js/main-rtl.js',
},
output: {
path: path.join(__dirname, '../build'),
filename: '[name].[chunkhash:8].bundle.js',
chunkFilename: '[name].[chunkhash:8].chunk.js',
},
mode: 'production',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader',
],
},
{
test: /\.(png|svg|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/img',
esModule: false,
},
},
],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/font',
},
},
],
},
{
test: /\.html$/i,
use: {
loader: 'html-loader',
options: {
attributes: {
list: [
{
tag: 'img',
attribute: 'src',
type: 'src',
},
{
tag: 'img',
attribute: 'srcset',
type: 'srcset',
},
{
tag: 'img',
attribute: 'data-src',
type: 'src',
},
{
tag: 'img',
attribute: 'data-srcset',
type: 'srcset',
},
],
},
},
},
},
],
},
optimization: {
minimizer: [new TerserJSPlugin(), new OptimizeCSSAssetsPlugin()],
splitChunks: {
cacheGroups: {
commons: {
test: /[\/]node_modules[\/]/,
name: 'vendors',
chunks: 'all',
},
},
chunks: 'all',
},
runtimeChunk: {
name: 'runtime',
},
},
plugins: [
// load jQuery
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: '[name].[chunkhash:8].bundle.css',
chunkFilename: '[name].[chunkhash:8].chunk.css',
}),
new HtmlWebpackPlugin({
chunks: ['main'],
template: 'src/i18n/index.html',
filename: 'index.html',
}),
new HtmlWebpackPlugin({
chunks: ['main-rtl'],
template: 'src/i18n/fa/index.html',
filename: 'fa/index.html',
}),
],
};
所以,问题是当我使用 webpack.prod.js 配置构建项目时,它在构建文件夹中为我提供了 2 个 html 文件,用于 ltr 和 rtl 方向,在 rtl 版本中 index.html 它为 img 标签生成了错误的 src 路径,并且在两个文件中它都没有改变 favicon 链接的路径,并且提到的链接与源 html 文件保持相同,但其他链接注入了 Webpack html 插件在每个 html 文件中都正确注入了正确的路径。
我需要 2 html 个文件,根据文件在文件夹结构中的任何级别生成正确的 img 标签 src 路径。
任何帮助将不胜感激。
我做了很多研究,没有找到对问题有帮助的东西。因此,我想到了为项目的 ltr 和 rtl 输出设置两个单独的生产 webpack 配置文件的想法。因此,我修改了 webpack.prod.js
文件,如下所示:
const webpack = require('webpack');
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
entry: {
main: './src/js/main.js' // ---> the only entry we have is main
},
output: {
path: path.join(__dirname, '../build'),
filename: '[name].[chunkhash:8].bundle.js',
chunkFilename: '[name].[chunkhash:8].chunk.js',
},
mode: 'production',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader',
],
},
{
test: /\.(png|svg|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/img',
esModule: false,
},
},
],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/font',
},
},
],
},
{
test: /\.html$/i,
use: {
loader: 'html-loader',
options: {
attributes: {
list: [
{
tag: 'img',
attribute: 'src',
type: 'src',
},
{
tag: 'img',
attribute: 'srcset',
type: 'srcset',
},
{
tag: 'img',
attribute: 'data-src',
type: 'src',
},
{
tag: 'img',
attribute: 'data-srcset',
type: 'srcset',
},
],
},
},
},
},
],
},
optimization: {
minimizer: [new TerserJSPlugin(), new OptimizeCSSAssetsPlugin()],
splitChunks: {
cacheGroups: {
commons: {
test: /[\/]node_modules[\/]/,
name: 'vendors',
chunks: 'all',
},
},
chunks: 'all',
},
runtimeChunk: {
name: 'runtime',
},
},
plugins: [
// load jQuery
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: '[name].[chunkhash:8].bundle.css',
chunkFilename: '[name].[chunkhash:8].chunk.css',
}),
new HtmlWebpackPlugin({
chunks: ['main'],
template: 'src/i18n/index.html',
filename: 'index.html',
}),
// new HtmlWebpackPlugin({
// chunks: ['main-rtl'],
// template: 'src/i18n/fa/index.html', ----> this part removed too
// filename: 'fa/index.html',
// }),
],
};
然后我为项目的 rtl 输出创建了另一个名为 webpack.prod.rtl.js
的 webpack 配置文件,并更改了项目的 outputPath
file-loader
并且我还修改了 filename
我的 HtmlWebpackPlugin
如下所示:
const webpack = require('webpack');
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
entry: {
'main-rtl': './src/js/main-rtl.js', // main-rtl instead of main
},
output: {
path: path.join(__dirname, '../build/fa'),
filename: '[name].[chunkhash:8].bundle.js',
chunkFilename: '[name].[chunkhash:8].chunk.js',
},
mode: 'production',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader',
],
},
{
test: /\.(png|svg|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: '../assets/img',
esModule: false,
},
},
],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: '../assets/font',
},
},
],
},
{
test: /\.html$/i,
use: {
loader: 'html-loader',
options: {
attributes: {
list: [
{
tag: 'img',
attribute: 'src',
type: 'src',
},
{
tag: 'img',
attribute: 'srcset',
type: 'srcset',
},
{
tag: 'img',
attribute: 'data-src',
type: 'src',
},
{
tag: 'img',
attribute: 'data-srcset',
type: 'srcset',
},
],
},
},
},
},
],
},
optimization: {
minimizer: [new TerserJSPlugin(), new OptimizeCSSAssetsPlugin()],
splitChunks: {
cacheGroups: {
commons: {
test: /[\/]node_modules[\/]/,
name: 'vendors',
chunks: 'all',
},
},
chunks: 'all',
},
runtimeChunk: {
name: 'runtime',
},
},
plugins: [
// load jQuery
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: '[name].[chunkhash:8].bundle.css',
chunkFilename: '[name].[chunkhash:8].chunk.css',
}),
new HtmlWebpackPlugin({
chunks: ['main-rtl'],
template: 'src/i18n/fa/index.html',
filename: 'index.html', // ---> changed from fa/index.html to index.html
}),
],
};
另外,我安装了一个名为 npm-run-all
的 npm 包,用于 运行 一些并行的 npm cli 命令。因此,当我同时需要项目的 ltr 和 rtl 版本时,我可以通过 package.json
文件的 script section
中的这些命令构建它们:
"scripts": {
// other scripts,
"build-ltr": "webpack --config=config/webpack.prod.js",
"build-rtl": "webpack --config=config/webpack.prod.rtl.js",
"build": "npm-run-all --parallel build-ltr build-rtl"
},
例如,在 运行ning npm run build
之后,它为我提供了我在问题中提到的先前文件夹结构中的项目的 ltr 和 rtl 版本,但具有正确的图像 src 路径。
所以,我决定分享解决方案。肯定还有其他解决方案,但这是我现在想出的解决问题的办法。
希望这对其他人有所帮助。
我有一个 Webpack 4 项目来制作多语言 admin-dashboard 具有以下文件夹和文件结构:
admin-dashboard
|
|--build
| |--assets
| | |--img
| | |--font
| |--fa
| | |--index.html ----> rtl html output (gets wrong img src like "assets/img/img.png")
| |--index.html ----> ltr html output (gets right img src like "assets/img/img.png")
| |--style.css
| |--style-rtl.css
| |--script.js
| |--script-rtl.js
|
|--config ---> containing my Webpack config files for production and development
| |--webpack.dev.js
| |--webpack.prod.js
|
|--node_modules
|--src
| |--assets
| | |--img
| | |--font
| |
| |--i18n
| | |--fa
| | | |--index.html ----> rtl html template ---> <img src="../../assets/img/img.png" />
| | |--index.html ----> ltr html template ---> <img src="../assets/img/img.png" />
| |
| |--js
| |--locales
| |--scss
| |--templates
|
|--.babelrc
|--package.json
|--postcss.config.js
我的 src
文件夹中的 i18n
文件夹中的图像路径在开发项目时是正确的,但是当我构建项目时,它在项目的根目录中创建了一个 build
文件夹它自己的资产文件夹。正如您在上面的文件结构中看到的,build 文件夹中的 ltr html 文件具有放置在 build 文件夹中的资产文件夹的正确路径,但 fa
文件夹中的 rtl 版本指的是相同的路径,这是错误的.这 2 个文件基于 src/i18n
中的 2 html 个文件制作。加上两个 html 文件的 header 中指向图标图像的所有链接在文件的构建版本中保持不变,这是另一个问题,但由 Webpack html 插件完成的其他注入链接全部正确注入html 文件中的路径。
这是我的生产模式的 webpack 配置:
webpack.prod.js
:
const webpack = require('webpack');
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
entry: {
main: './src/js/main.js',
'main-rtl': './src/js/main-rtl.js',
},
output: {
path: path.join(__dirname, '../build'),
filename: '[name].[chunkhash:8].bundle.js',
chunkFilename: '[name].[chunkhash:8].chunk.js',
},
mode: 'production',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader',
],
},
{
test: /\.(png|svg|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/img',
esModule: false,
},
},
],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/font',
},
},
],
},
{
test: /\.html$/i,
use: {
loader: 'html-loader',
options: {
attributes: {
list: [
{
tag: 'img',
attribute: 'src',
type: 'src',
},
{
tag: 'img',
attribute: 'srcset',
type: 'srcset',
},
{
tag: 'img',
attribute: 'data-src',
type: 'src',
},
{
tag: 'img',
attribute: 'data-srcset',
type: 'srcset',
},
],
},
},
},
},
],
},
optimization: {
minimizer: [new TerserJSPlugin(), new OptimizeCSSAssetsPlugin()],
splitChunks: {
cacheGroups: {
commons: {
test: /[\/]node_modules[\/]/,
name: 'vendors',
chunks: 'all',
},
},
chunks: 'all',
},
runtimeChunk: {
name: 'runtime',
},
},
plugins: [
// load jQuery
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: '[name].[chunkhash:8].bundle.css',
chunkFilename: '[name].[chunkhash:8].chunk.css',
}),
new HtmlWebpackPlugin({
chunks: ['main'],
template: 'src/i18n/index.html',
filename: 'index.html',
}),
new HtmlWebpackPlugin({
chunks: ['main-rtl'],
template: 'src/i18n/fa/index.html',
filename: 'fa/index.html',
}),
],
};
所以,问题是当我使用 webpack.prod.js 配置构建项目时,它在构建文件夹中为我提供了 2 个 html 文件,用于 ltr 和 rtl 方向,在 rtl 版本中 index.html 它为 img 标签生成了错误的 src 路径,并且在两个文件中它都没有改变 favicon 链接的路径,并且提到的链接与源 html 文件保持相同,但其他链接注入了 Webpack html 插件在每个 html 文件中都正确注入了正确的路径。
我需要 2 html 个文件,根据文件在文件夹结构中的任何级别生成正确的 img 标签 src 路径。
任何帮助将不胜感激。
我做了很多研究,没有找到对问题有帮助的东西。因此,我想到了为项目的 ltr 和 rtl 输出设置两个单独的生产 webpack 配置文件的想法。因此,我修改了 webpack.prod.js
文件,如下所示:
const webpack = require('webpack');
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
entry: {
main: './src/js/main.js' // ---> the only entry we have is main
},
output: {
path: path.join(__dirname, '../build'),
filename: '[name].[chunkhash:8].bundle.js',
chunkFilename: '[name].[chunkhash:8].chunk.js',
},
mode: 'production',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader',
],
},
{
test: /\.(png|svg|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/img',
esModule: false,
},
},
],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/font',
},
},
],
},
{
test: /\.html$/i,
use: {
loader: 'html-loader',
options: {
attributes: {
list: [
{
tag: 'img',
attribute: 'src',
type: 'src',
},
{
tag: 'img',
attribute: 'srcset',
type: 'srcset',
},
{
tag: 'img',
attribute: 'data-src',
type: 'src',
},
{
tag: 'img',
attribute: 'data-srcset',
type: 'srcset',
},
],
},
},
},
},
],
},
optimization: {
minimizer: [new TerserJSPlugin(), new OptimizeCSSAssetsPlugin()],
splitChunks: {
cacheGroups: {
commons: {
test: /[\/]node_modules[\/]/,
name: 'vendors',
chunks: 'all',
},
},
chunks: 'all',
},
runtimeChunk: {
name: 'runtime',
},
},
plugins: [
// load jQuery
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: '[name].[chunkhash:8].bundle.css',
chunkFilename: '[name].[chunkhash:8].chunk.css',
}),
new HtmlWebpackPlugin({
chunks: ['main'],
template: 'src/i18n/index.html',
filename: 'index.html',
}),
// new HtmlWebpackPlugin({
// chunks: ['main-rtl'],
// template: 'src/i18n/fa/index.html', ----> this part removed too
// filename: 'fa/index.html',
// }),
],
};
然后我为项目的 rtl 输出创建了另一个名为 webpack.prod.rtl.js
的 webpack 配置文件,并更改了项目的 outputPath
file-loader
并且我还修改了 filename
我的 HtmlWebpackPlugin
如下所示:
const webpack = require('webpack');
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
entry: {
'main-rtl': './src/js/main-rtl.js', // main-rtl instead of main
},
output: {
path: path.join(__dirname, '../build/fa'),
filename: '[name].[chunkhash:8].bundle.js',
chunkFilename: '[name].[chunkhash:8].chunk.js',
},
mode: 'production',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader',
],
},
{
test: /\.(png|svg|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: '../assets/img',
esModule: false,
},
},
],
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: '../assets/font',
},
},
],
},
{
test: /\.html$/i,
use: {
loader: 'html-loader',
options: {
attributes: {
list: [
{
tag: 'img',
attribute: 'src',
type: 'src',
},
{
tag: 'img',
attribute: 'srcset',
type: 'srcset',
},
{
tag: 'img',
attribute: 'data-src',
type: 'src',
},
{
tag: 'img',
attribute: 'data-srcset',
type: 'srcset',
},
],
},
},
},
},
],
},
optimization: {
minimizer: [new TerserJSPlugin(), new OptimizeCSSAssetsPlugin()],
splitChunks: {
cacheGroups: {
commons: {
test: /[\/]node_modules[\/]/,
name: 'vendors',
chunks: 'all',
},
},
chunks: 'all',
},
runtimeChunk: {
name: 'runtime',
},
},
plugins: [
// load jQuery
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: '[name].[chunkhash:8].bundle.css',
chunkFilename: '[name].[chunkhash:8].chunk.css',
}),
new HtmlWebpackPlugin({
chunks: ['main-rtl'],
template: 'src/i18n/fa/index.html',
filename: 'index.html', // ---> changed from fa/index.html to index.html
}),
],
};
另外,我安装了一个名为 npm-run-all
的 npm 包,用于 运行 一些并行的 npm cli 命令。因此,当我同时需要项目的 ltr 和 rtl 版本时,我可以通过 package.json
文件的 script section
中的这些命令构建它们:
"scripts": {
// other scripts,
"build-ltr": "webpack --config=config/webpack.prod.js",
"build-rtl": "webpack --config=config/webpack.prod.rtl.js",
"build": "npm-run-all --parallel build-ltr build-rtl"
},
例如,在 运行ning npm run build
之后,它为我提供了我在问题中提到的先前文件夹结构中的项目的 ltr 和 rtl 版本,但具有正确的图像 src 路径。
所以,我决定分享解决方案。肯定还有其他解决方案,但这是我现在想出的解决问题的办法。
希望这对其他人有所帮助。