侧边栏壁纸
  • 累计撰写 21 篇文章
  • 累计创建 11 个标签
  • 累计收到 9 条评论

Vue 自定义vite插件加载svg图片

好巧
2023-05-13 / 0 评论 / 1 点赞 / 106 阅读 / 4822 字
温馨提示:
本文最后更新于 2023-10-26,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

前言

本文示例是官方的 vite 脚手架, svg 图标使用的是阿里的字体图标,自行下载

新建文件,例:src/plugins/svg-loader.js

  • findSvgFile 函数会查找当前文件路径下的所有 svg 文件, 包括子文件夹里面的

  • 需保证 svg 文件不重名

import { readdirSync, readFileSync } from 'fs';

const svgLabel = /<svg([^>+].*?)>/;
const pathLabel = /<path([^>+].*?)>/g;
const clearReturn = /(\r)|(\n)/g;
const clearWidthHeight = /(width|height)="([^>+].*?)"/g;
const hasViewBox = /(viewBox="[^>+].*?")/g;

/**
 * @Description 替换 svg 文件信息
 * @param dir svg 目录
 */
function findSvgFile(dir) {
  const svgRes = [];
  const dirEntries = readdirSync(dir, {
    withFileTypes: true
  });

  for (const dirent of dirEntries) {
    if (dirent.isDirectory()) {
      svgRes.push(...findSvgFile(dir + dirent.name + '/'));
    } else {
      const svg = readFileSync(dir + dirent.name)
        .toString()
        .replace(clearReturn, '')
        .replace(svgLabel, ($1, $2) => {
          let width = 0;
          let height = 0;
          let content = $2.replace(clearWidthHeight, (s1, s2, s3) => {
            s3 = s3.replace('px', '');
            if (s2 === 'width') {
              width = s3;
            } else if (s2 === 'height') {
              height = s3;
            }

            return '';
          });
          if (!hasViewBox.test($2)) {
            content += `viewBox="0 0 ${width} ${height}"`;
          }

          return `<symbol id="${dirent.name.replace('.svg', '')}" ${content}>`;
        })
        // 替换svg图标原先fill
        .replace(pathLabel, ($1, $2) => {
          return `<path fill="currentColor" ${$2}>`;
        })
        .replace('</svg>', '</symbol>');

      svgRes.push(svg);
    }
  }

  return svgRes;
}

/**
 * @Description 加载svg 文件
 * @param path svg图标文件路径
 *
 */
export default function (path) {
  if (path === '') return;

  const res = findSvgFile(path);

  return {
    name: 'svg-transform',
    // vite 插件 直接替换 html 页签内容
    transformIndexHtml(html) {
      return html.replace(
        '<body>',
        `
          <body>
            <svg
              xmlns="http://www.w3.org/2000/svg"
              xmlns:xlink="http://www.w3.org/1999/xlink"
              style="position: fixed; width: 0; height: 0"
            >
              ${res.join('')}
            </svg>
          </body>
        `
      );
    }
  };
}

配置 vite.config.js

import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import svgLoader from './src/plugins/svg-loader';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    svgLoader('./src/assets/')
  ]
});

直接使用

#号后面为 svg 文件名

<template>
  <svg class="svg-icon" aria-hidden="true">
    <use xlink:href="#like"></use>
  </svg>
</template>

<style scoped>
.svg-icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  /* 填充色 */
  fill: currentColor;
  color: #8a8a8a;
  overflow: hidden;
}

.svg-icon:hover {
  color: #d81e06;
}
</style>

封装成组件,例:src/components/SvgIcon.vue

<template>
  <svg class="svg-icon" aria-hidden="true">
    <use :xlink:href="iconName"></use>
  </svg>
</template>

<script lang="ts" setup>
import { computed } from 'vue';

const props = defineProps({
  name: {
    type: String,
    required: true
  }
});

const iconName = computed(() => `#${props.name}`);
</script>

<style scoped>
.svg-icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  /* 填充色 */
  fill: currentColor;
  color: inherit;
  overflow: hidden;
}
</style>

组件使用

name 值为 svg 文件名

<template>
  <SvgIcon class="svg-icon" name="like" />
</template>

<script setup>
import SvgIcon from './components/SvgIcon.vue';
</script>

<style scoped>
.svg-icon {
  color: #8a8a8a;
}

.svg-icon:hover {
  color: #d81e06;
}
</style>

效果图

1

评论区