首页 > 解决方案 > 更改 webpack 为导入的图像生成的图像域

问题描述

尽管我的开发服务器正在运行localhost:3000,但我已将主机文件设置为指向www.mysite.comlocalhost。在我的 JavaScript 中,我的代码如下:

import myImage from '../assets/my-image.jpg'

const MyCmp => <img src={myImage} />

使用 Webpack file-loader,它将导入转换为托管图像的 URL。但是,它使用该localhost图像的路径,但我希望它使用www.mysite.com域。我查看了publicPathpostTransformPublicPath的选项file-loader,但它们似乎只允许您修改域之后的路径部分。

标签: javascriptwebpack

解决方案


我个人不喜欢在构建输出中静态定义主机信息的概念。这应该在运行时根据您实际放置文件的位置来确定。

如果你和我一样,那么这里有两个选择。两者都涉及调用您在窗口/全局范围内定义的全局方法。全局方法的目的是在运行时解析根路径(域等)。

定义一个全局方法

因此,假设您在启动代码中的某个位置在全局范围内定义了一个方法,如下所示:

(<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 脚本。


推荐阅读