将 svg-url-loader 与 Create React App 结合使用

Using svg-url-loader with Create React App

我正在使用 Create React App,我想将我的 SVG 内嵌作为数据 URI。

我知道 CRA 允许我将 SVG 作为组件导入和渲染,la:

import { ReactComponent as Logo } from './logo.svg';
// <Logo />

而且我知道我可以获得 SVG 的 URL:

import Logo from './logo.svg';
// <img src={Logo} />

但我想做的是将 SVG 的内容嵌入为数据 URI,如:

<img src="data:image/svg+xml;utf8,<svg>...</svg>">

我知道svg-url-loader can do this. So I set it up (using Rescripts,因为CRA限制修改Webpack配置):

function addSvgLoader(config) {
  config.module.rules.push({
    test: /\.svg/,
    use: {
      loader: "svg-url-loader",
      options: {
        limit: 15000,
      },
    },
  });

  return config;
}

module.exports = [addSvgLoader];

有了这个,理论上,我应该能够使用上面的第二个示例,而不是获取指纹 URL,而是获取内联数据 URI。然而,指纹 URL 仍然被渲染,SVG 文件的内容现在看起来像这样:

module.exports = "data:image/svg+xml,%3Csvg role='img' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 24V0h24v24H0zM ... [truncated] ... 03h-.52l-.23.02z'/%3E%3C/svg%3E"

我怀疑这是CRA的加载器之间的冲突svg-url-loader,但我一直没能找到解决办法。如果有 svg-url-loader 的替代方案,我也会考虑。

Webpack 规则在使用 import 语法时使用 test glob 作为规则来处理包含文件。由于规则不必相互排斥,多个规则可以轻松处理相同的 test 标准。

在您的示例中,听起来像是预先存在的 svgs CRA 规则 您的 svg-url-loader 规则正在处理导入的 SVG;顺序由您的 Webpack 配置在运行时分配的方式决定。

试试这样的东西:

  1. 在文件名中使用可识别的名字创建 SVG 的变体(例如 my-image.data-uri.svg
  2. 配置 svg-url-loader 插件以激活 *.data-uri.svg glob 模式

    {
      test: /\.data-uri.svg/,
      use: {
        loader: 'svg-url-loader',
        options: {
            encoding: 'base64'
        },
      },
    },
    

这里的文件名真的很像 intent 并且规则将在构建时通过自定义插件路由这些案例。

备注

如果在现代 CRA 之外尝试这样做,旧的静态编译策略建议在 require 语句中指定导入意图:

require('svg-url-loader?encoding=base64!./file.svg');

这通常已被删除,以支持静态映射,因此需要使用 glob 测试来激活您的特殊插件案例。

这确实是 CRA 的 svg 加载程序与您尝试使用的加载程序之间的冲突。但是,webpack规则可以有一个oneOf section, which will apply only the first matching rule - and CRA's svg rule is in it(我的版本是3.4.1)。

此外,规则的顺序很重要,您的规则应该在 CRA 定义的规则之前。

考虑到这两点,我像这样修改了 webpack 配置:

let oneOfRule = config.module.rules.find((r) => r.oneOf);
oneOfRule.oneOf.splice(0, 0, {
  test: /\.svg$/,
  use: ['svg-url-loader'],
});

我用 svg-sprite-loader 而不是 svg-url-loader 测试了这个,但一般原理保持不变,它应该可以工作。