手把手撸后台-svg
vite 篇
官方文档
安装
bash
npm i unocss @iconify/utils -D
在根目录新建 uno.config.js
- 注意
注意
注意文件中的 iconDir 的路径,是你存放 svg 的地址
我这里就是 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>