前端SVG图片自动化引入

  • 以下有Vue3、Vue2

Vue3 ts

插件 vite-plugin-svg-icons

vite.config.ts

import path from 'path'
import { fileURLToPath, URL } from 'node:url'

import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import Components from 'unplugin-vue-components/vite'
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
import legacy from '@vitejs/plugin-legacy'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, './')

  return {
    base: env.VITE_BASE_URL,
    plugins: [
      vue(),
      vueJsx(),
      Components({
        resolvers: [
          AntDesignVueResolver({
            importStyle: 'less' // css in js
          })
        ],
        version: 3
      }),
      createSvgIconsPlugin({
        iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
        symbolId: 'icon-[dir]-[name]'
      }),
      legacy({
        targets: ['ie > 11'],
        additionalLegacyPolyfills: ['regenerator-runtime/runtime']
      })
    ],
    resolve: {
      alias: {
        '@': fileURLToPath(new URL('./src', import.meta.url)),
        lodash: 'lodash-es'
      }
    },
    server: {
      host: '0.0.0.0',
      port: 3000,
      cors: true,
      proxy: {
        '/dev/api': {
          target: 'http://10.10.22.133:8580',
          // target: 'http://10.10.25.71',
          // target: 'http://10.10.25.83',
          changeOrigin: true,
          rewrite: (path) => path.replace(/^\/dev\/api/, '')
        }
      },
      hmr: { overlay: false }
    },
    build: {
      outDir: env.VITE_OUT_DIR,
      assetsDir: 'assets',
    }
  }
})

svg-icon.vue

import 'virtual:svg-icons-register';
<template>
  <svg aria-hidden="true" class="svg-icon" :style="style" v-bind="$attrs">
    <use :href="symbolId" :fill="color || '#333'" />
  </svg>
</template>

<script setup lang="ts">
import { isNumber } from 'lodash-es'
import { computed } from 'vue'
import px2rem from '@/utils/flexble'

const props = defineProps<{
  prefix?: string
  name: string
  color?: string
  size?: number | [number, number]
}>()

const symbolId = computed(() => `#${props.prefix || 'icon'}-${props.name}`)

const width = computed(() => {
  if (!props.size) return ''
  return isNumber(props.size) ? props.size : props.size[0]
})

const height = computed(() => {
  if (!props.size) return ''
  return isNumber(props.size) ? props.size : props.size[1]
})

const style = computed(() => {
  const style: { width?: string; height?: string } = {}
  if (width.value) style.width = px2rem(width.value) as string
  if (height.value) style.height = px2rem(height.value) as string
  return style
})
</script>

vue2

svg-icon.js

import Vue from "vue";
import SvgIcon from "@/components/SvgIcon/index.vue"; // svg component

// register globally
Vue.component("svg-icon", SvgIcon);

// 在svg-icon.js同层文件夹svg下引入svg图片
const req = require.context("./svg", true, /\.svg$/);
const requireAll = (requireContext) =>
  requireContext.keys().map(requireContext);
requireAll(req);

SvgIcon/index.vue

<template>
  <div
    v-if="isExternal"
    :style="styleExternalIcon"
    class="svg-external-icon svg-icon"
    v-on="$listeners"
  />
  <svg
    v-else
    :class="svgClass"
    aria-hidden="true"
    :style="styles"
    v-bind="$attrs"
    v-on="$listeners"
  >
    <use :xlink:href="iconName" />
  </svg>
</template>

<script>
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
function isExternal(path) {
  return /^(https?:|mailto:|tel:)/.test(path);
}

export default {
  name: "SvgIcon",
  props: {
    iconClass: {
      type: String,
      required: true
    },
    className: {
      type: String,
      default: ""
    },
    size: {
      type: Number,
      default: 0
    },
    rotate: {
      type: Number,
      default: 0
    }
  },
  computed: {
    isExternal() {
      return isExternal(this.iconClass);
    },
    iconName() {
      return `#icon-${this.iconClass}`;
    },
    svgClass() {
      if (this.className) {
        return `svg-icon ${this.className}`;
      }
      return "svg-icon";
    },
    styleExternalIcon() {
      return {
        mask: `url(${this.iconClass}) no-repeat 50% 50%`,
        "-webkit-mask": `url(${this.iconClass}) no-repeat 50% 50%`
      };
    },
    styles() {
      let css = {};
      if (this.size) {
        css = {
          ...css,
          width: `${this.size}px`,
          height: `${this.size}px`
        };
      }
      if (this.rotate) {
        css = {
          ...css,
          transform: `rotate(${this.rotate}deg)`
        };
      }
      return css;
    }
  }
};
</script>

<style scoped>
.svg-icon {
  width: 16px;
  height: 16px;
  vertical-align: middle;
  fill: currentColor;
  overflow: hidden;
}

.svg-external-icon {
  background-color: currentColor;
  mask-size: cover !important;
  display: inline-block;
}
</style>


©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容