Vue3中的Icon理方案
在前端项目开发中,SVG(可缩放矢量图形)凭借其无损缩放、体积小、可编辑性强等诸多优势,成为了处理图标(Icon)的热门选择。本文将为大家详细介绍几种在项目中处理 SVG 图作为 Icon 的有效方法。
用组件的方式引入Svg将其作为Icon-vite-svg-loader
vite-svg-loader不仅仅可以通过组件形式导入,还可以通过url、string的形式进行导入。
vite.config.js
import vue from '@vitejs/plugin-vue'
import SvgLoader from 'vite-svg-loader'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
return {
base: env.VITE_APP_BASE_URL + '/',
plugins: [
vue(),
svgLoader()
]
})
结合业务场景为svgLoader做一些额外配置
import vue from '@vitejs/plugin-vue'
import SvgLoader from 'vite-svg-loader'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
return {
base: env.VITE_APP_BASE_URL + '/',
plugins: [
vue(),
svgLoader(
{
svgoConfig: {
plugins: [
{
name: 'removeAttrs',
// 保留这些属性,但不包括id
params: { attrs: '(data-name|class|style)' },
},
{
// 使用 cleanupIds 插件但要小心配置以避免误删除或修改ID
name: 'cleanupIds',
params: {
// 或者使用 minify: false 来禁用 minify 功能,以此保留原始 Ids,不追加的话,svgo会修改svg的id,svg的显示会互相影响
minify: false,
},
},
],
},
}
)
]
})
将svg图导出
icon
├── assets // 存放图片
│ └── table.svg
└── src // 抛出所有svg图
└── index.ts
导出方式一: Icon会有代码提示
除了可以按组件的形式导出,还可以按Url的方式导出
// icon->src->index.ts
// 所有svg图都在此处追加
export { default as table } from '../assets/table.svg?component'
<template>
<div>
<table></table>
</div>
</template>
<script setup lang="ts">
import { table } from '@/components/icon'
defineOptions({
name: 'Icon',
})
</script>
导出方式二:这种方式导出的话,没有代码提示
const svgModules = import.meta.glob('../assets/*.svg', {
eager: true,
query: 'component',
})
// 抛出一个对象,对象中的键名是文件名称,值是`svgModules` 中对应 `path` 的模块
const icons = Object.keys(svgModules).reduce((pre, path) => {
const file = path.split('/')
const fileName = file[file.length - 1].split('.')[0]
pre[fileName] = svgModules[path]
return pre
}, {} as any)
export default icons
<template>
<div>
<table></table>
</div>
</template>
<script setup lang="ts">
import icons from '@/components/icon'
const { table } = icons
defineOptions({
name: 'Icon',
})
使用@iconify/vue插件
npm install --save-dev @iconify/vue
在线方式
<template>
<Icon icon="carbon:bot" />
</template>
<script setup lang="ts">
import { Icon } from '@iconify/vue'
</script>
<style scoped lang="scss"></style>
离线方式
默认Iconify是在线加载的,访问有可能不稳定,很多时候私有化部署不能加载外网数据,因此我们需要离线加载。因为Iconify是在线加载元数据,因此,我们可以改为本地自动引入。
导入整个数据集
整个导入图标集,会导致图标集里的图片全部被打包进assets的js文件中,dist文件会特别大。
npm install @iconify/vue @iconify/json
// main.ts
import { createApp } from 'vue';
import App from './App.vue';
import { Icon } from '@iconify/vue';
// 引入需要的图标集,这里以 Material Design Icons 为例
import materialDesignIcons from '@iconify/json/json/mdi.json';
import { addCollection } from '@iconify/core';
// 添加图标集到 Iconify
addCollection(materialDesignIcons);
const app = createApp(App);
app.component('Icon', Icon);
app.mount('#app');
app.vue
<template>
<div>
<!-- 使用 mdi 图标集中的图标 -->
<Icon icon="mdi:home" />
</div>
</template>
<script setup>
// 无需额外导入,因为已经在 main.js 中全局注册
</script>
按需加载方式 - 手动按需加载
npm install @iconify/vue
全部数据
npm add -D @iconify/json
部分数据(以Material Design Icons为例)
npm add -D @iconify-json/mdi
图片按需加载工具
npm add -D unplugin-icons
tsconfig.json
"compilerOptions": {
"types": ["unplugin-icons/types/vue"],
}
装Vite的图标自动引入插件
// vite.config.ts
import Icons from 'unplugin-icons/vite' //图标按需加载工具
export default defineConfig({
plugins: [
Icons(), //图标按需加载工具
],
})
<script setup>
import IconAccessibility from '~icons/carbon/accessibility'
import IconAccountBox from '~icons/mdi/account-box'
</script>
<template>
<icon-accessibility/>
<icon-account-box style="font-size: 2em; color: red"/>
</template>
按需加载方式 - 自动按需加载
安装按需加载插件
npm add unplugin-vue-components -D
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
//icon按需引入
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
import Components from 'unplugin-vue-components/vite'
export default defineConfig({
plugins: [
vue(),
Components({
resolvers: [
IconsResolver({
prefix: 'icon' // 更改图标前缀
}),
],
}),
Icons(),
],
})
在组件中使用
<template>
<! --使用icon-->
<icon-mdi-home />
</template>
<script setup>
</script>
参考内容
https://zhuanlan.zhihu.com/p/688842750
https://juejin.cn/post/7087827571861585956#heading-0
使用vite-plugin-svg-icons插件渲染svg类型的Icon
// svg-icon.vue组件
<template>
<svg
aria-hidden="true"
class="svg-icon"
:style="'width:' + width + ';height:' + height"
>
<use :xlink:href="symbolId" :fill="color" />
</svg>
</template>
<script setup lang="ts">
import { computed } from 'vue';
const props = defineProps({
prefix: {
type: String,
default: 'icon'
},
iconClass: {
type: String,
required: false
},
color: {
type: String
},
width: {
type: String,
default: '12px'
},
height: {
type: String,
default: '12px'
}
});
const symbolId = computed(() => `#${props.prefix}-${props.iconClass}`);
</script>
<style scoped>
.svg-icon {
vertical-align: -0.15em;
overflow: hidden;
fill: currentColor;
}
</style>
// vite.config.ts
import vue from "@vitejs/plugin-vue";
import path from "path";
import { UserConfig, ConfigEnv, loadEnv } from "vite";
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
export default ({ mode }: ConfigEnv): UserConfig =>{
// 获取.env环境配置
const env = loadEnv(mode, process.cwd());
return {
base: env.VITE_APP_BASE+'/',
plugins: [
vue(),
createSvgIconsPlugin({
// 指定需要缓存的图标文件夹
iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
// 指定symbolId格式
symbolId: 'icon-[dir]-[name]'
})
],
resolve: {
// Vite路径别名配置
alias: {
'@': path.resolve('./src')
}
}
}
};
// main.ts
import { createApp } from 'vue';
import App from './App.vue';
// 引入svg注册脚本
import 'virtual:svg-icons-register';
// 引入svg组件
import SvgIcon from '@/components/svg-icon/index.vue';
const app = createApp(App);
// 注册全局组件
app
.component('svg-icon', SvgIcon)
export default app;
<template>
<div class="bottom-bar">
<!-- icon-class的值与src/assets/icons内的文件名相同 -->
<svg-icon icon-class="version"></svg-icon>
</div>
</template>
<script setup lang="ts">
</script>
<style lang="scss" scoped>
</style>