javascript - 更改 webpack 为导入的图像生成的图像域
问题描述
尽管我的开发服务器正在运行localhost:3000
,但我已将主机文件设置为指向www.mysite.com
localhost。在我的 JavaScript 中,我的代码如下:
import myImage from '../assets/my-image.jpg'
const MyCmp => <img src={myImage} />
使用 Webpack file-loader
,它将导入转换为托管图像的 URL。但是,它使用该localhost
图像的路径,但我希望它使用www.mysite.com
域。我查看了publicPath
和postTransformPublicPath
的选项file-loader
,但它们似乎只允许您修改域之后的路径部分。
解决方案
我个人不喜欢在构建输出中静态定义主机信息的概念。这应该在运行时根据您实际放置文件的位置来确定。
如果你和我一样,那么这里有两个选择。两者都涉及调用您在窗口/全局范围内定义的全局方法。全局方法的目的是在运行时解析根路径(域等)。
定义一个全局方法
因此,假设您在启动代码中的某个位置在全局范围内定义了一个方法,如下所示:
(<any>window).getWebpackBundleRootPath = function (webpackLibraryId) {
if (webpackLibraryId == null) return throw "OOOPS DO SOMETHING HERE!";
// Preferably these variables should be loaded from a config-file of sorts.
if(webpackLibraryId == "yourwebpacklibrary1") return "https://www.yoursite.com/";
// If you have other libraries that are hosted somewhere else, put them here...
return "...some default path for all other libraries...";
};
下一步是配置 webpack 在尝试解析路径时调用此全局方法。正如我所提到的,有两种方法,一种是操纵 webpack 的输出,另一种是在 webpacks 配置中更加集成(虽然仅用于文件加载器,但我认为它应该足够了)。
值得一提的是,如果您只有一个包或将所有包托管在一个地方,则不需要全局方法。那么使用全局变量就足够了。修改下面的示例以适应这一点应该很容易。
第一个选项:配置 webpack 文件加载器以在解析路径时调用您的方法
此解决方案不需要在构建后进行任何操作。如果这符合您的需要并涵盖所有场景,我会选择此选项。
编辑你的 webpack 配置文件
var path = require('path');
let config = {
entry: {
'index': path.join(__dirname, '/public/index.js')
},
output: {
path: path.join(__dirname, '/dist/'),
filename: 'index-bundle.js',
publicPath: 'https://localhost:3000/',
library: 'yourwebpacklibrary1',
...
},
module: {
rules: [{
// Please note that this only defines the resolve behavior for ttf. If you want to resolve other files you need to configure the postTransformPublicPath for those too. This is a big caveat in my opinion and might be a reason for using second option.
test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/,
use: [{
loader: 'file-loader',
options: {
outputPath: 'assets/fonts', // The folder where you store your fonts.
name: '[name].[ext]',
// This is where the magic happens. This lets you override the output of webpack file resolves.
postTransformPublicPath: function (p) {
// Because of the way webpack file-loader works the input from to this method will look something like this: __webpack_public_path__ + "/assets/fonts/yourfont.ttf"
// But we are not interested in using the __webpack_public_path__ variable so lets remove that.
p = p.replace('__webpack_public_path__ + ', '');
// Return a stringified call to our global method and append the relative path to the root path returned.
return `getWebpackBundleRootPath("${config.output.library}") + ${p}`;
}
}
}]
},
},
...
};
module.exports = config;
正如您在 webpack 配置文件中的注释中可能已经注意到的那样,您需要为添加的每个文件加载器指定解析行为(如果有人知道更好的方法,请告诉我)。这就是为什么我仍然使用第二个选项。
第二种选择:在构建后步骤中操作 webpack 的输出
示例 webpack.config.js 文件
为了完整起见,这里是一个 webpack.config.js 文件的示例,其中包含 postbuild 脚本中使用的变量。
var path = require('path');
module.exports = {
entry: {
'index': path.join(__dirname, '/public/index.js')
},
output: {
path: path.join(__dirname, '/dist/'),
filename: 'index-bundle.js',
publicPath: 'https://localhost:3000/',
library: 'yourwebpacklibrary1',
...
},
...
}
创建一个 postbuild.js 文件
在你的 package.json 旁边创建一个 postbuild.js 文件,内容如下:
const fs = require('fs');
// We take the path to the webpack config file as input so that we can read settings from it.
const webpackConfigFile = process.argv[2];
// Read the webpack config file into memory.
const config = require(webpackConfigFile);
// The file to manipulate is the output javascript bundle that webpack produces.
const inputFile = config.output.path + config.output.filename;
// Load the file into memory.
let fileContent = fs.readFileSync(inputFile, 'utf8');
// Replace the default public path with the call to the method. Please note that if you specify a publicPath as '/' or something very common you might end up with a problem so make sure it is unique in the file to avoid other unrelated stuff being replaced as well.
fileContent = fileContent.replace('"' + config.output.publicPath + '"', 'getWebpackBundleRootPath("' + config.output.library + '")');
// Save the manipulated file back to disk.
fs.writeFileSync(inputFile, fileContent, 'utf8');
在构建时自动调用 postbuild.js
下一步是在每次构建后实际调用 postbuild.js 脚本。这可以在 package.json 中的 post脚本中完成,如下所示(在 package.json 的脚本部分中):
{
"scripts": {
"build": "webpack",
"postbuild": "node postbuild.js ./webpack.config.js"
}
}
从现在开始,每当您运行构建脚本时,它也会运行构建后脚本(来自 npm 或 yarn 等)。
当然,您也可以在每次构建后手动运行 postbuild.js 脚本。