Skip to content

手把手撸后台-svg

vite 篇

官方文档

官方文档

安装

bash

npm i  unocss @iconify/utils -D

在根目录新建 uno.config.js

  • 注意

注意

  1. 注意文件中的 iconDir 的路径,是你存放 svg 的地址

  2. 我这里就是 src/assets/icons

  • 代码
ts
import { defineConfig } from "unocss";
import presetIcons from "@unocss/preset-icons";
import { FileSystemIconLoader } from "@iconify/utils/lib/loader/node-loaders";
import fs from "fs";

// 本地 SVG 图标存放目录
const iconsDir = "./src/assets/icons";

// 读取本地 SVG 目录,自动生成 `safelist`
const generateSafeList = () => {
  try {
    return fs
      .readdirSync(iconsDir)
      .filter((file) => file.endsWith(".svg"))
      .map((file) => `i-svg:${file.replace(".svg", "")}`);
  } catch (error) {
    console.error("无法读取图标目录:", error);
    return [];
  }
};

export default defineConfig({
  presets: [
    presetIcons({
      // 设置全局图标默认属性
      extraProperties: {
        width: "1em",
        height: "1em",
        display: "inline-block",
      },
      // 注册本地 SVG 图标集合
      collections: {
        // svg 是图标集合名称,使用 `i-svg:图标名` 调用
        svg: FileSystemIconLoader(iconsDir, (svg) => {
          // 如果 SVG 文件未定义 `fill` 属性,则默认填充 `currentColor`
          // 这样图标颜色会继承文本颜色,方便在不同场景下适配
          return svg.includes('fill="currentColor"')
            ? svg
            : svg.replace(/^<svg /, '<svg fill="currentColor" ');
        }),
      },
    }),
  ],
  safelist: generateSafeList(), // 动态生成 `safelist`
});

修改 vite.config.js 导入 unocss

  • 代码
ts
import { fileURLToPath, URL } from "node:url";

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import vueDevTools from "vite-plugin-vue-devtools";

import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite"; // 按需加载自定义组件

import Unocss from "unocss/vite";

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    Unocss(),
    vueDevTools(),
    AutoImport({
      imports: ["vue", "vue-router"],
      dts: "src/auto-import.d.ts",
    }),
    Components({
      // 解决命名冲突
      directoryAsNamespace: true,
      dts: true, //
      dirs: ["src/components"], // 按需加载的文件夹
      //     resolvers: [   // 设置UI框架自动加载
      //       ElementPlusResolver(),  // Antd   按需加载
      //       AntDesignVueResolver()  // ElementPlus按需加载
      //  ]
    }),
  ],
  resolve: {
    alias: {
      "@": fileURLToPath(new URL("./src", import.meta.url)),
    },
    extensions: [".js", ".json", ".ts", ".vue"],
  },
  // 配置scss
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: "",
        javascriptEnabled: true,
      },
    },
  },
});

修改 main.ts 文件

ts
import { createApp } from "vue";
import { createPinia } from "pinia";

// 引入初始化css

import "normalize.css";

// 引入svg

import "virtual:uno.css"; //添加这个

import App from "./App.vue";
import router from "./router";

const app = createApp(App);

app.use(createPinia());
app.use(router);

app.mount("#app");

封装 svg 组件

vue
<template>
  <!-- 判断是否是外部链接 -->
  <img
    v-if="isExternall"
    :src="props.icon"
    v-bind="$attrs"
    :style="{ width: size + 'px', height: size + 'px' }"
  />
  <!-- 判断是否是内置图标 -->
  <span
    v-else
    :class="iconName"
    :style="{ width: size + 'px', height: size + 'px', color: color }"
  ></span>
</template>

<script setup>
const props = defineProps({
  //图标名称,会根据传入的信息来判断是否为外链
  icon: {
    type: String,
    required: true,
  },
  //svg图标颜色
  color: {
    type: String,
  },
  //svg图标大小
  size: {
    type: String,
    default: 30,
    required: false,
  },
});
const isExternall = computed(() => /(https?:|mailto:tel:)/.test(props.icon));
const iconName = computed(() => `i-svg:${props.icon} size`);
</script>

<style scoped>
.size {
  width: 100px;
  height: 100px;
  color: red;
}
</style>

使用

  • 我这里把 SvgIcon 绑定成全局组件了 可以直接使用
vue
<template>
  <div>
    <SvgIcon icon="logo" color="blue" size="60"></SvgIcon>
  </div>
</template>

<script setup></script>

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

webpack 篇幅

安装

bash
npm i --save-dev svg-sprite-loader@6.0.9 --legacy-peer-deps

修改 vue.config.js

  • 特别注意的就是目录 src/icons 目录
js
const { defineConfig } = require("@vue/cli-service");
const path = require("path");

function resolve(dir) {
  return path.join(__dirname, dir);
}

module.exports = defineConfig({
  transpileDependencies: true,
  chainWebpack(config) {
    // 设置 svg-sprite-loader
    config.module.rule("svg").exclude.add(resolve("src/icons")).end();
    config.module
      .rule("icons")
      .test(/\.svg$/)
      .include.add(resolve("src/icons"))
      .end()
      .use("svg-sprite-loader")
      .loader("svg-sprite-loader")
      .options({
        symbolId: "icon-[name]",
      })
      .end();
  },
});

创建 sVgicon 组件

  • 在 components 文件夹下面创建 SvgIcon 文件夹里面创建 index.vue 文件
vue
<template>
  <!--展示外部图标-->
  <div
    v-if="isExternal"
    :style="styleExternalIcon"
    class="svg-external-icon"
    :class="className"
  ></div>
  <!--展示内部图标-->
  <svg v-else class="svg-icon" :class="className" aria-hidden="true">
    <use :xlink:href="iconName" />
  </svg>
</template>

<script setup>
import { defineProps, computed } from "vue";
const props = defineProps({
  // icon图标
  icon: {
    type: String,
    required: true,
  },
  // 图标类名
  className: {
    type: String,
    default: "",
  },
});
/* 判断法则 */
const external = (path) => {
  return /^(https?:|mailto:|tel:)/.test(path);
};
/*
当前图标是否为外部图标,
*/
const isExternal = computed(() => {
  return external(props.icon);
});

/*
外部图标样式,
*/
const styleExternalIcon = computed(() => {
  return {
    mask: `url(${props.icon}) no-repeat 50% 50%`,
    "-webkit-mask": `url(${props.icon}) no-repeat 50% 50%`,
  };
});
/*
内部图标
*/
const iconName = computed(() => {
  return `#icon-${props.icon}`;
});
</script>

<style lang="scss" scoped>
.svg-icon {
  width: 1em;
  height: 1em;
  vertical-align: 0.1em;
  fill: currentColor;
  overflow: hidden;
}
.svg-external-icon {
  background-color: currentColor;
  mask-size: cover !important;
  display: inline-block;
}
</style>
  • 在 src 目录下面新建 icons 文件夹,在下面创立 svg 文件夹把所有的图标都放到下面,然后在 icons 文件夹下面新建 index.js 文件
js
import SvgIcon from "@/components/SvgIcon";

// https://webpack.docschina.org/guides/dependency-management/#requirecontext
// 通过 require.context() 函数来创建自己的 context
// 第一个要搜索的目录,第二个表示是否搜索子目录,第三个表示正则表达式
const svgRequire = require.context("./svg", false, /\.svg$/);
// 此时返回一个 require 的函数,可以接受一个 request 的参数,用于 require 的导入。
// 该函数提供了三个属性,可以通过 require.keys() 获取到所有的 svg 图标
// 遍历图标,把图标作为 request 传入到 require 导入函数中,完成本地 svg 图标的导入
svgRequire.keys().forEach((svgIcon) => svgRequire(svgIcon));

export default (app) => {
  app.component("svg-icon", SvgIcon);
};

修改 main.js 文件

js
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
// 增加的
import installIcons from "@/icons";

const app = createApp(App);

// 增加的
installIcons(app);

app.use(store).use(router).mount("#app");

使用

js
<SvgIcon icon="eye" className="svgbox"></SvgIcon>