Vite 中SvgIcon更好的实现

634 阅读2分钟

Vite 中SvgIcon更好的实现

vite中的svg

vite 本身支持将 svg 资源引入为 静态url字符串内容raw,参见 静态资源处理

但这并不是我们期待的引入内容,所以会有一些方案实现:

vite-svg-loader

vite-svg-loader 使项目支持引入svg为 component,且支持开启svgo

vite-plugin-svg-icons

使用说明见 vite-plugin-svg-icons

  • 预加载 在项目运行时就生成所有图标,只需操作一次 dom
  • 高性能 内置缓存,仅当文件被修改时才会重新生成

通过生成指定目录下所有 svg 为雪碧图,即将多个 SVG 图标整合到一个 SVG 文件中,每个图标作为这个大 SVG 文件的一部分,它们可以通过 <symbol> 标签的 id 被单独引用和使用。

SVG 雪碧图的优点有:只需要发送一次 HTTP 请求就可以获取所有图标,可以减少 HTTP 请求的次数,提高网页的加载速度。

同时,SVG 雪碧图内的每个图标还可以被单独引用和缓存,增加了图标的复用性。

如果项目svg增多,svg雪碧图生成文件会更大,且无法单独引用的特性存在性能短板;

更好的尝试

既然 vite-svg-loader 支持将svg资源引入为 component,尝试在项目中将指定目录下所有svg引入为component(当然是动态引入),并将其作为异步组件被使用

使用方面,我们可以通过定义一个全局组件 SvgIcon 方便使用

import { defineComponent, defineAsyncComponent, h } from "vue";

const svgModules = import.meta.glob("@/assets/images/svg/**/*.svg", {
  query: "?component",
});

export default defineComponent({
  name: "SvgIcon",
  props: {
    name: { type: String, default: "" },
  },
  setup(props) {
    const SvgComponent = defineAsyncComponent(
      svgModules[`/src/assets/images/svg/${props.name}.svg`]
    );
    return () => h(SvgComponent);
  },
});

或者SFC单文件组件形式

<script setup>
import { defineAsyncComponent } from "vue";
const props = defineProps({
  name: { type: String, default: "" },
});

const svgModules = import.meta.glob("@/assets/images/svg/**/*.svg", {
  query: "?component",
});

const SvgComponent = defineAsyncComponent(
  svgModules[`/src/assets/images/svg/${props.name}.svg`]
);
</script>

<template>
  <SvgComponent />
</template>

<style lang="" scoped></style>

如上,通过 import.meta.glob 批量动态导入所有svg为component,使用组件时传入svg名称或路径+名称即可

<script setup>

</script>

<template>
  <!-- 分别显示 svg/edit.svg 和 svg/tool/save.svg -->
  <SvgIcon name="edit"></SvgIcon>
  <SvgIcon name="tool/edit"></SvgIcon>
</template>

<style lang="less" scoped></style>