- 在线图标库iconify 的离线使用
- 在vben-admin框架中使用离线Iconify图标库
一、Iconify图标库离线使用
1. 安装依赖
npm install @iconify/json @iconify/vue
npm install @vue/composition-api
2. 提取图标数据
// 从 @iconify/json 中提取特定图标集的 JSON 数据(例如 Material Design Icons):
// 示例:src/utils/icons.js
import { addCollection } from '@iconify/vue';
import mdiData from '@iconify/json/json/mdi.json'; // 路径可能因版本变化
// 加载整个图标集
addCollection(mdiData);
// 或按需加载单个图标(推荐减少体积)
const { icons } = mdiData;
addCollection({
prefix: 'mdi',
icons: {
// 选择需要的图标
account: icons.account,
home: icons.home,
// ...
}
});
3. 全局注册 Icon 组件
在 Vue 入口文件(如 main.js)中注册组件:
// main.js
import { createApp } from 'vue';
import App from './App.vue';
import { Icon } from '@iconify/vue';
import './utils/icons'; // 导入图标数据初始化
const app = createApp(App);
app.component('Icon', Icon); // 全局注册组件
app.mount('#app');
4. 在组件中使用图标
<template>
<!-- 使用图标 -->
<Icon icon="mdi:account" />
<Icon icon="mdi:home" :inline="true" /> <!-- inline 模式 -->
</template>
5. 按需加载优化
若需进一步优化体积,可动态加载图标:
// utils/icons.js
import { addIcon } from '@iconify/vue';
// 动态加载单个图标
export function loadIcon(iconName) {
import(`@iconify/json/json/mdi.json`).then((mdiData) => {
const iconData = mdiData.icons[iconName];
addIcon(`mdi:${iconName}`, iconData);
});
}
// 在组件中使用:
// 在需要的地方调用 loadIcon('account') 预加载
6. 构建工具配置
确保构建工具(Vite/Webpack)正确处理 JSON 文件:
Vite 配置(vite.config.js)
javascript
复制
export default {
assetsInclude: ['**/*.json'],
};
Vue CLI/Webpack 配置
javascript
复制
// vue.config.js
module.exports = {
chainWebpack: (config) => {
config.module
.rule('json')
.test(/\.json$/)
.type('javascript/auto')
.use('json-loader')
.loader('json-loader');
}
};
二、在vben-admin中使用离线图标Iconify
1. 下载依赖
npm install @iconify/json@2.2.224
npm install @purge-icons/generated@^0.9.0
npm install @iconify/json @iconify/vue
2 . 提取图标数据
// 从 @iconify/json 中提取特定图标集的 JSON 数据(例如 Material Design Icons):
示例: src\settings\iconSettings.ts
import { addCollection } from 'iconify-icon';
import antDesignIcons from '@iconify/json/json/ant-design.json';
import bpmnIcons from '@iconify/json/json/bpmn.json';
import bxIcons from '@iconify/json/json/bx.json';
import ionIcons from '@iconify/json/json/ion.json';
import lineMdIcons from '@iconify/json/json/line-md.json';
import mdiIcons from '@iconify/json/json/mdi.json';
import vaadinIcons from '@iconify/json/json/vaadin.json';
// 注册图标
addCollection(antDesignIcons);
addCollection(bpmnIcons);
addCollection(bxIcons);
addCollection(ionIcons);
addCollection(lineMdIcons);
addCollection(mdiIcons);
addCollection(vaadinIcons);
3. 【Icon.vue】(完整代码)
src\components\Icon\src\Icon.vue
<template>
<SvgIcon
:size="size"
:name="getSvgIcon"
v-if="isSvgIcon"
:class="[$attrs.class, 'anticon']"
:spin="spin"
/>
<span
v-else
:class="[$attrs.class, 'app-iconify anticon', spin && 'app-iconify-spin']"
:style="getWrapStyle"
>
<iconify-icon
:icon="icon"
:rotate="`${rotate}deg`"
:width="size"
:style="{
color: color,
}"
/>
</span>
</template>
<script lang="ts">
import type { PropType } from 'vue';
import {
defineComponent,
ref,
watch,
onMounted,
nextTick,
unref,
computed,
CSSProperties,
} from 'vue';
import SvgIcon from './SvgIcon.vue';
import Iconify from '@purge-icons/generated';
import { isString } from '/@/utils/is';
import { propTypes } from '/@/utils/propTypes';
const SVG_END_WITH_FLAG = '|svg';
export default defineComponent({
name: 'Icon',
components: { SvgIcon },
props: {
// icon name
icon: propTypes.string,
// icon color
color: propTypes.string,
// icon size
size: {
type: [String, Number] as PropType<string | number>,
default: 16,
},
rotate: {
type: [String, Number],
default: '0',
required: false,
},
spin: propTypes.bool.def(false),
prefix: propTypes.string.def(''),
},
setup(props) {
const elRef = ref<ElRef>(null);
const isSvgIcon = computed(() => props.icon?.endsWith(SVG_END_WITH_FLAG));
const getSvgIcon = computed(() => props.icon.replace(SVG_END_WITH_FLAG, ''));
const getIconRef = computed(() => `${props.prefix ? props.prefix + ':' : ''}${props.icon}`);
const update = async () => {
if (unref(isSvgIcon)) return;
const el = unref(elRef);
if (!el) return;
await nextTick();
const icon = unref(getIconRef);
if (!icon) return;
const svg = Iconify.renderSVG(icon, {});
if (svg) {
el.textContent = '';
el.appendChild(svg);
} else {
const span = document.createElement('span');
span.className = 'iconify';
span.dataset.icon = icon;
el.textContent = '';
el.appendChild(span);
}
};
const getWrapStyle = computed((): CSSProperties => {
const { size, color } = props;
let fs = size;
if (isString(size)) {
fs = parseInt(size, 10);
}
return {
fontSize: `${fs}px`,
color: color,
display: 'inline-flex',
};
});
watch(() => props.icon, update, { flush: 'post' });
onMounted(update);
return { elRef, getWrapStyle, isSvgIcon, getSvgIcon };
},
});
</script>
<style lang="less">
.app-iconify {
display: inline-block;
// vertical-align: middle;
&-spin {
svg {
animation: loadingCircle 1s infinite linear;
}
}
}
span.iconify {
display: block;
min-width: 1em;
min-height: 1em;
background-color: @iconify-bg-color;
border-radius: 100%;
}
</style>