将 SVG 图标导入 Svelte 应用程序的最佳方式

And*_*Mao 12 javascript svg typescript rollupjs svelte

我有大约 80 个自定义 SVG 图标,我将它们导入到 Svelte 前端应用程序中。它基于https://github.com/sveltejs/template构建,使用 Rollup 构建,包括 Typescript、Tailwind 和所有现代好东西。

在此处输入图片说明

困境在于如何将图标添加到应用程序中。作为 SVG,图标是不超过 2kB 的简短 XML 文本字符串。

选项 1:作为图像资产

  1. 将所有图标上传foo.svgpublic/assets/icons.
  2. 创建一个<Icon type="foo' />使用<img src="foo.svg>.

这种方法意味着图标不是代码的一部分。

优点:图标可以由前端代码按需动态加载。无需将所有图标捆绑到应用程序代码中。

缺点:如果有很多新图标,页面加载速度会很慢,并且浏览器必须获取十几个 1kB 文件。将应用程序部署为 PWA 意味着我们需要手动告诉它预先缓存图标。

选项 2:作为应用程序构建的一部分

  1. 使用类似https://github.com/cristovao-trevisan/svelte-iconhttps://github.com/codefeathers/rollup-plugin-svelte-svg 之类的东西将每个图标直接导入到代码中:import Home from './icons/home.svg';
  2. 创建一个 svelte 组件,选择正确的导入组件或 SVG 字符串并显示它。

在这里,图标作为文本字符串与应用程序本身捆绑在一起。

优点:图标作为应用程序包的一部分提供。缓存是不必要的。可以在加载时动态修改一些图标代码,例如颜色、视图框等。

缺点:没有必要在应用程序中包含所有图标以减少到第一个字节的时间。不能在不增加复杂性的情况下进行捆绑拆分等。使渲染变慢,因为 Javascript 代码需要首先将字符串转换为 SVG,而不仅仅是加载图像。

问题

  • 从这个分析来看,在应用程序中构建图标似乎是一个更好的方法,但我错过了什么吗?
  • 如果“图标”是 50-100kB 的详细图像而不是这里的小字符串,那么演算会改变吗?

Dan*_*l T 38

您只需将文件扩展名更改为.svelte并将 SVG 作为普通组件导入即可。


Bob*_*kes 19

您想保持 SVG 图标文件不变吗?

Vite 的导入选项?raw可用于此目的,导入 SVG 图标文件的原始内容。此选项在此描述https://vitejs.dev/guide/assets.html#importing-asset-as-string

使用 svelte{@html }选项,它将以原始 html 格式输出。

它将结合问题中的选项 1 如何解释不将图标作为代码的一部分,但仍导入图标以使其成为应用程序构建的一部分,如问题的选项 2 中所述。

主要好处是 SVG 图标库可以通过 *.svg 扩展名保持原始状态。例如:

import SearchIcon from '$lib/icon/search.svg?raw';
{@html SearchIcon}
Run Code Online (Sandbox Code Playgroud)

代码中只会包含应用程序功能所需的 SVG 图标。这确保了应用程序保持轻量级和高效。

尽管我也会考虑将 SVG 文件重新制作成 .svelte 文件的方案,就像另一个答案中提出的那样。我刚刚提出了 Vite?raw功能,以防最好保持图标文件不变。与往常一样,使用时@html请确保图标是“受信任”的 html 文件并且无法(重新)生成。$lib/icon/*.svg因此,像原始 SVG 文件那样的文件路径是一个安全的解决方案。


小智 9

另一种方法是在公共文件夹中使用符号定义文件(例如:icons.svg)。然后在你的代码中做这样的事情:

图标.svelte

<script>
    export let name;
    export let width = "1.5rem";
    export let height = "1.5rem";
    export let focusable = false;
</script>

<svg class={$$props.class} {focusable} {width} {height}>
    <use href={`/icons.svg#${name}`} />
</svg>
Run Code Online (Sandbox Code Playgroud)

图标.svg

<svg aria-hidden="true" style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <defs>
        <symbol id="icon-warning" viewBox="0 0 20 20">
            <path d="M19.511 17.98l-8.907-16.632c-0.124-0.215-0.354-0.348-0.604-0.348s-0.481 0.133-0.604 0.348l-8.906 16.632c-0.121 0.211-0.119 0.471 0.005 0.68 0.125 0.211 0.352 0.34 0.598 0.34h17.814c0.245 0 0.474-0.129 0.598-0.34 0.124-0.209 0.126-0.469 0.006-0.68zM11 17h-2v-2h2v2zM11 13.5h-2v-6.5h2v6.5z">
            </path>
        </symbol>
    </defs>
</svg>
Run Code Online (Sandbox Code Playgroud)

应用程序.svelte

<Icon name="icon-warning" />
Run Code Online (Sandbox Code Playgroud)

这样你就可以使用一个 http 调用来加载 svg 文件。然后只需在标记中使用您需要的部分即可。


sk *_*aka 8

使用 like{@html SVG_CODE}从另一个组件或变量渲染 svg 代码是一个糟糕的选择。这有时会带来XSS 攻击的风险。特别是当图像来自外部源时。检查: https: //github.com/sveltejs/svelte/issues/2545

在插入 DOM 之前,Svelte 不会对 {@html ...} 内的表达式执行任何清理。换句话说,如果您使用此功能,手动转义来自您不信任的来源的 HTML 至关重要,否则您的用户将面临 XSS 攻击的风险。(来源

我认为使用像rollup-plugin-svelte-svg 这样的插件会是更好的选择

1.使用npm安装

npm i -D rollup-plugin-svelte-svg

2.只需在汇总配置中的 svelte 之前调用 svelteSVG 即可。


export default {
    
    plugins: [
        svelteSVG({
            // optional SVGO options
            // pass empty object to enable defaults
            svgo: {}
        }),
    ],
    ...
}
Run Code Online (Sandbox Code Playgroud)

3.然后你可以在你的JS中导入svg:

<script>
    import Logo from "./logo.svg";
</script>

<Logo width=20 />
Run Code Online (Sandbox Code Playgroud)

或者

使用文本编辑器打开svg图像,复制所有代码并粘贴到.svelte扩展名为 .svg 的文件中。

例如, icon.svg文件包含:

<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14.243 5.757a6 6 0 10-.986 9.284 1 1 0 111.087 1.678A8 8 0 1118 10a3 3 0 01-4.8 2.401A4 4 0 1114 10a1 1 0 102 0c0-1.537-.586-3.07-1.757-4.243zM12 10a2 2 0 10-4 0 2 2 0 004 0z" clip-rule="evenodd"></path></svg>
Run Code Online (Sandbox Code Playgroud)

将其复制到icon.svelte文件中

<script></script>
<style></style>
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14.243 5.757a6 6 0 10-.986 9.284 1 1 0 111.087 1.678A8 8 0 1118 10a3 3 0 01-4.8 2.401A4 4 0 1114 10a1 1 0 102 0c0-1.537-.586-3.07-1.757-4.243zM12 10a2 2 0 10-4 0 2 2 0 004 0z" clip-rule="evenodd"></path></svg>
Run Code Online (Sandbox Code Playgroud)

当你想使用icon.svelte组件时

<script>
import Icon from "$lib/icon.svelte"
</script>
<style></style>
<Icon />

Run Code Online (Sandbox Code Playgroud)

如果你愿意,你可以通过选修课

然后在icon.svelte

<script></script>
<style></style>
<svg class="{$$props.class}" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14.243 5.757a6 6 0 10-.986 9.284 1 1 0 111.087 1.678A8 8 0 1118 10a3 3 0 01-4.8 2.401A4 4 0 1114 10a1 1 0 102 0c0-1.537-.586-3.07-1.757-4.243zM12 10a2 2 0 10-4 0 2 2 0 004 0z" clip-rule="evenodd"></path></svg>
Run Code Online (Sandbox Code Playgroud)

当你想调用icon.svelte组件时

<script>
import Icon from "$lib/icon.svelte"
</script>
<style></style>
<Icon class="h-6 w-6" />

Run Code Online (Sandbox Code Playgroud)

  • -1“这会带来 XSS 和其他攻击的风险。” 这是一个误导性的说法。**仅当您通过“{@html ...}”呈现未经净化的用户输入**时才存在 XSS 风险;对于您自己的服务器上的 SVG 图标来说,您不是。所以不,这里使用“{@html svgString}”不存在安全问题。 (6认同)
  • 谢谢,每张图片都来自可靠的来源吗?我对此不太确定,想想一个简单的 svg 编辑网站,网站根据用户输入渲染 svg,如何才能信任。我指出了对 XXS 的担忧,因为 svelte 官方文档指出了这一点,我确实认为他们比我更了解,https://svelte.dev/tutorial/html-tags (2认同)

Dav*_*veo 6

以下方法具有以下优点:

  • 一个中心点来维护您的应用程序的所有图标
  • 减少了获取 SVG 图标的网络请求
  • 整个应用程序中可重复使用的图标,无需重复的 svg 元素

有一个专用的 Icon.svelte 组件设置,如下所示:

<script>
    export let name;
    export let width = "1rem";
    export let height = "1rem";
    export let focusable = false;
    let icons = [
        {
          box: 24,
          name: "save",
          svg: `<g stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><path d="M17 21v-8H7v8"/><path d="M7 3v5h8"/></g>`
        },
        {
          box: 32,
          name: "trash",
          svg: `<path d="M12 12h2v12h-2z" /><path d="M18 12h2v12h-2z" /><path d="M4 6v2h2v20a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8h2V6zm4 22V8h16v20z" /><path d="M12 2h8v2h-8z" />`
        }
    ];
    let displayIcon = icons.find((e) => e.name === name);
</script>
<svg
  class={$$props.class}
  {focusable}
  {width}
  {height}
  viewBox="0 0 {displayIcon.box} {displayIcon.box}">{@html displayIcon.svg}</svg>
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样使用它:

<Icon name="trash" class="this-is-optional" />
Run Code Online (Sandbox Code Playgroud)

  • 可以使用以图标名称作为键的对象来代替数组,这样您就不必在每次渲染图标时都执行“查找”操作。 (16认同)
  • @Oneezy `icons = {'save': {box:24, svg: '...'}, 'trash': {...}}` 然后 `displayIcon = Icons[name]` (2认同)

小智 4

一个可行的解决方案是将 SVG 保存在单独的 IconService.js 中:

export const chevron_down = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"feather feather-chevron-down\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg>";

export const chevron_right = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"feather feather-chevron-right\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg>";
Run Code Online (Sandbox Code Playgroud)

这可以轻松地在 svelte @html 函数中导入和使用。

<script>
    import {chevron_down, chevron_right} from 'IconService';
</script>

{@html chevron_down}
Run Code Online (Sandbox Code Playgroud)