奥莉莉亚(Aurelia)、webpack和动态引用图片

7
假设我们有以下的标记(包含多个tbody,我知道):
<tbody repeat.for="order of orders">
  <tr repeat.for="line of order.lines">
    <td>
      <img if.bind="order.urgent === 'T'" src="../app/alert.svg">
      <img if.bind="line.outOfSquare" src="../app/oos.svg">
    </td>
    <td class="min-width">
      <img src.bind="'../app/'+line.type+'.svg'" alt="${line.type}">
    </td>
  </tr>
</tbody>

在通过dotnet new Aurelia创建的默认项目中,由于图片较小,它们被内联作为DataUrls。这是合理的,但根据绑定数据,它们会在许多行中重复出现。我们可以通过调整webpack.config.js将阈值降低到1024字节来解决这个问题。
{ test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=1024' }

现在,图片以加密的名称出现在wwwroot/dist中,并且URL被重写。通过对webpack.config.js进行补充,计算的URL目标也被捆绑在一起。
  ,new GlobDependenciesPlugin({
    "boot": [
      "ClientApp/app/components/**/*.svg"
    ]
  })

很遗憾,计算机生成的URL没有被重写。

src.bind="'../app/'+line.type+'.svg'"

它们现在已经损坏了。

如何在运行时解决应用程序相对路径?

我们需要在运行时解决这个问题,但到目前为止我找不到任何支持此操作的方法。有几种可能性:

  • 完全禁止处理图像并使用构建任务将它们打包
  • 使用 require 在运行时转换 URL
  • 使用一个包含静态 URL 和原始 URL 作为 id 值的隐藏 div 来进行映射,然后在运行时使用它们来做映射。

我的研究表明,有 webpack 插件会将这些映射作为 json 发出,但我的浅薄理解不能让我利用它 —— 除此之外,我不知道如何使输出可供应用程序使用。

这似乎很相关,但我的无知阻碍了我。如何使用 webpack file-loader 加载图像文件

我的尝试使用 require 没有成功,但我怀疑在 Aurelia 模块中自动存在的 require 方法不是可以解决映射的 Webpack require。想必 webpack 可以在运行时加载和解码打包的应用程序,但我不太清楚,因为到目前为止它一直正常工作,让我在无知中运作。

我知道我可以通过处理每种行类型并使用对资源的静态引用将其嵌入页面,就像这样:

<img if.bind="line.type === 'AL'" src="../app/al.svg">
<img if.bind="line.type === 'GD'" src="../app/gd.svg">

但这是高维护代码。

另一种可能性是采取另一个方向。借鉴放置充满图片的隐藏div的建议,如果这些都内联,则可以通过绑定复制图像。


CopyWebpackPlugin 不是一个选项吗?只需将文件物理复制到 dist 文件夹中即可。 - Fred Kleuver
1个回答

7
使用 require.context,您可以告诉 webpack 捆绑所有符合特定模式的文件,然后使用创建的上下文在运行时动态解析它们的路径。
使用单个上下文和长路径解析所有图像
假设您有一个名为 src/assets/images 的文件夹,其中包含所有图像。其中一张图片的名称为 image-1.jpg。 您可以创建一个名为 src/images.js 的文件,如下所示:
const imageContext = require.context(
  ".",
  true,
  /^\.\/.*\.(jpe?g|png|gif)$/i
);
export { imageContext };

这个上下文将(递归地)包括src下的所有图像。然后,在例如app.js中,导入并使用它来解析您的图像:

import { imageContext } from "./images";

export class App {
    constructor() {
        this.img = imageContext("./assets/images/image-1.jpg"); 

        // resolved: '/src/assets/images/image-1.f1224ebcc42b44226aa414bce80fd715.jpg'
    }
}

<img src.bind="img">

解析特定文件夹中的图片,使用短路径

注意:传递给上下文来解析图像的路径必须是相对于您声明上下文时传递的路径的 相对路径。所以如果您有一个像这样的src/assets/images.js

const imageContext = require.context(
  "./images",
  true,
  /^\.\/.*\.(jpe?g|png|gif)$/i
);
export { imageContext };

然后在 src/app.js 文件中,你需要这样做:
import { imageContext } from "./images";

export class App {
    constructor() {
        this.img = imageContext("./image-1.jpg"); 

        // resolved: '/src/assets/images/image-1.f1224ebcc42b44226aa414bce80fd715.jpg'
    }
}

使用特定模式加载所有图片

如果您想要一起显示的多组图片,最好为每一组创建一个上下文:

src/pages/album/album.js:

const ctx = require.context(
  "../../assets/images",
  true,
  /^\.\/.*some-special-pattern.*\.(jpe?g|png|gif)$/i
);

export class Album {
    constructor() {
        this.images = ctx.keys().forEach(imageContext);
    }
}

<img repeat.for="img of images" src.bind="img">

让Aurelia去做一些神奇的事情

举个例子,可以创建一个ValueConverter:

src/resources/converters/image-context.js:

const imageContext = require.context(
  "../../",
  true,
  /^\.\/.*\.(jpe?g|png|gif)$/i
);

export class ImageContextValueConverter {
    toView(name) {
        const key = imageContext.keys().find(k => k.includes(name));
        return imageContext(key);
    }
}

src/resources/index.js

import { PLATFORM } from "aurelia-pal";

export function configure(config) {
    config.globalResources([
        PLATFORM.moduleName("resources/converters/image-context")
    ]);
}

然后在任何其他地方,例如获取src/assets/images/image-1.jpg:

<img src.bind="'image-1' | imageContext">

Url-loader问题

当我尝试在我的项目中执行此操作时,我遇到了url-loader的问题,无法使其正常工作。感觉像是最新版webpack下的url-loader出了问题,因为它也忽略了limit选项。我不得不完全放弃使用url-loader来处理图像。如果/当我解决这个问题时,我将更新我的答案。


1
这正是我所寻求的信息。显然,您理解我面临的问题和提问的动机,而您的答案足够完整,为进一步调查和自我教育提供了框架。太棒了! - Peter Wone
.find(k => k.contains(name)) 中,您是指 .find(k => k.includes(name)) 或者可能是 .find(k => k.startsWith(name)) 吗?Contains 是一个数组方法。 - Peter Wone
我指的是.contains()。它也是一个字符串方法 - 字符串有许多与数组相同的方法。最终它只是一个简单的例子,但你也可以传递一个函数名以获得更大的灵活性。例如正则表达式匹配toView(arg, method),然后.find(k => k[method](arg)),用法:/\d+/ | | imageContext:'match'查找所有名称中带有数字的图像。 - Fred Kleuver
我也是这么想的,但代码在Edge和Firefox上无法编译或运行。因为你的回答让我感到惊讶,所以我查了一下两个名称,发现有一个复杂的故事,最终导致contains被弃用,而推荐使用includes。https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes - Peter Wone
我真是太惊讶了,从来不知道.. 我花99%的时间在Chrome上。那我就把我的答案更新为“包含”吧。谢谢你告诉我! - Fred Kleuver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接