共计 6023 个字符,预计需要花费 16 分钟才能阅读完成。
须要最初达到的应用目标
<!-- 引入 mdi 下的 account 图标作为图标 -->
<v-icon icon="mdi-account" />
<!-- 引入 src/icons/dashboard.svg 自定义图标作为图标 -->
<v-icon icon="dashboard" />
<!-- 引入 mdi 下的 arrow-up-bold-circle 图标作为前置按钮图标 -->
<v-button prepend-icon="mdi-arrow-up-bold-circle"> 按钮 </v-button>
VIcon 组件
假如咱们首先封装一个 v-icon
组件在 src/components/VIcon.vue
。
<template>
<i class="v-icon">
<slot />
</i>
</template>
<style scoped>
.v-icon {
display: inline-block;
width: 1em;
height: 1em;
font-size: 1em;
}
.v-icon > svg {
width: 100%;
height: 100%;
}
</style>
应用 unplugin-icons 作为图标按需加载
在目前的解决方案中,可能会应用 unplugin-icons 插件在 SFC
中动态按需加载图标。
<script setup>
import IconAccessibility from '~icons/carbon/accessibility'
</script>
<template>
<v-icon>
<icon-accessibility />
</v-icon>
</template>
配合 unplugin-vue-components 主动导入省去 script
的 import
局部。
<template>
<v-icon>
<i-carbon-accessibility />
</v-icon>
</template>
这在单纯应用 v-icon
组件的时候没什么问题,然而如果基于 v-icon
封装其余组件,再应用就会有点繁琐。
例如咱们封装一个 v-button
在 src/components/VButton.vue
,并增加一个前置图标的插槽,代码如:
<template>
<button class="v-button">
<v-icon v-if="$slots['prepend-icon']">
<slot name="prepend-icon" />
</v-icon>
<span><slot /></span>
</button>
</template>
咱们的应用形式如下:
<template>
<v-button>
<template #prepend-icon>
<i-carbon-accessibility />
</template>
按钮
</v-button>
</template>
如果 v-button
在嵌套在别的父组件中,应用这个父组件时传递一个按钮的前置图标将会十分繁琐,所以我可能更想这样去应用:
<template>
<v-button prepend-icon="carbon-accessibility"> 按钮 </v-button>
</template>
如何转化成这样去用呢,最简略的解决办法是将 v-button
属性 prepend-icon
的动态值 "carbon-accessibility"
字符串替换成 <i-carbon-accessibility />
组件导入。
如何做模板组件属性值的动态替换
咱们能够在 @vue/compiler-sfc
模板编译后的代码中,通过正则替换文本值,例如模板代码为:
<script setup>
import VButton from './components/VButton.vue'
</script>
<template>
<v-button prepend-icon="mdi-delete"> 按钮 </v-button>
</template>
模板会编译成
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(),
_createBlock($setup["VButton"], {"prepend-icon": "mdi-delete"}, {default: _withCtx(()=>[_hoisted_1]),
_: 1 /* STABLE */
}))
}
通过正则
/_create(?:VNode|Block)\((?:_component_v_button|\$setup\["VButton"\]), .*?{.*?("prepend-icon"): (.+?)([,|}].*?\))/gs
匹配出动态局部,再转化为
import __icons_0 from '~icons/mdi/delete'
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(),
_createBlock($setup["VButton"], {"prepend-icon": __icons_0}, {default: _withCtx(()=>[_hoisted_1]),
_: 1 /* STABLE */
}))
}
就实现了组件属性值从字符串转化为传入一个组件。
应用 vite-plugin-iconify 插件
这一过程我封装了一个 vite-plugin-iconify 插件,如上述只须要配置 replaceableProps
。
// vite.config.ts
import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import Iconify from 'vite-plugin-iconify'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(),
Iconify({
replaceableProps: [{ component: 'VButton', props: ['prependIcon'] },
{component: 'VIcon', props: ['icon'] },
]
}),
]
})
革新组件 v-icon
以反对属性值传入组件
<script setup lang="ts">
defineProps<{icon?: object}>()
</script>
<template>
<i class="v-icon">
<slot>
<component :is="icon" />
</slot>
</i>
</template>
<style scoped>
.v-icon {
display: inline-block;
width: 1em;
height: 1em;
font-size: 1em;
}
.v-icon > svg {
width: 100%;
height: 100%;
}
</style>
革新组件 v-button
以反对属性值传入组件
<script setup lang="ts">
import VIcon from './VIcon.vue'
defineProps<{prependIcon?: object}>()
</script>
<template>
<button class="v-button">
<v-icon v-if="$slots['prepend-icon'] || prependIcon" :icon="prependIcon">
<slot name="prepend-icon" />
</v-icon>
<span><slot /></span>
</button>
</template>
<style>
.v-button {
display: inline-flex;
align-items: center;
}
</style>
就能够间接在模板中应用
<script setup>
import VButton from './components/VButton.vue'
import VIcon from './components/VIcon.vue'
</script>
<template>
<v-icon icon="mdi-delete" />
<v-button prepend-icon="mdi-delete"> 按钮 </v-button>
</template>
依据文件目录导入自定义图标
在以往的习惯用法中,咱们通常喜爱下载 svg
文件到某个目录作为自定义图标载入,vite-plugin-iconify 也反对了,默认会载入 src/icons
目录中 .svg
后缀的文件,通过 ~icons
导入组件集。
那如何将自定义图标联合 v-icon
应用呢,新增文件如下:
// src/icons/dashboard.svg
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M168.106667 621.44l120.746666 57.962667 223.274667 108.138666 215.317333-104.32 128.768-61.674666a64 64 0 0 1-29.952 84.970666l-286.229333 138.624a64 64 0 0 1-55.808 0L197.994667 706.517333A64 64 0 0 1 168.106667 621.44z m687.829333-133.930667a64 64 0 0 1-29.674667 85.546667L540.010667 711.68a64 64 0 0 1-55.808 0L197.994667 573.056A64 64 0 0 1 166.826667 490.88l317.013333 149.525333 28.288 13.696 286.229333-138.624-0.149333-0.064 57.728-27.882666zM540.032 185.792l286.208 138.602667a64 64 0 0 1 0 115.2l-286.208 138.624a64 64 0 0 1-55.808 0L197.994667 439.594667a64 64 0 0 1 0-115.2L484.224 185.813333a64 64 0 0 1 55.808 0z m-27.904 57.6l-286.229333 138.602667 286.229333 138.624 286.229333-138.624-286.229333-138.602667z"></path></svg>
咱们想通过简略指定 icon="dashboard"
就能应用
<v-icon icon="dashboard" />
这里提供一个应用形式
首先咱们创立一个 icons
的组合式 API
// src/compositions/icons.ts
// Utils
import {inject} from 'vue'
// Types
import type {InjectionKey, DefineComponent} from 'vue'
export interface IconsInstance
{
/**
* @zh 所有图标的别名
*/
aliases: Record<string, DefineComponent>
}
export const IconsKey: InjectionKey<IconsInstance> = Symbol.for('app:icons')
/**
* @zh 创立图标集
*
* @param options
*/
export function createIcons (options: IconsInstance) {return options}
/**
* @zh 应用图标集
*/
export function useIcons () {const icons = inject(IconsKey)
if (!icons) throw new Error('Could not find icons instance')
return icons
}
在 main.ts
中全局注册提供者
// Utils
import {createApp} from 'vue'
import App from './App.vue'
// Icons
import icons from '~icons'
// Compositions
import {createIcons, IconsKey} from './compositions/icons'
const app = createApp(App)
app.provide(IconsKey, createIcons({aliases: icons}))
app.mount('#app')
批改 v-icon
组件如下
<script setup lang="ts">
// src/components/VIcon.vue
// Utils
import {computed} from 'vue'
// Compositions
import {useIcons} from '../compositions/icons'
const props = defineProps<{icon?: string | object}>()
const icons = useIcons()
const componentIcon = computed(() => {if (!props.icon) return
if (typeof props.icon === 'string' && props.icon in icons.aliases) {return icons.aliases[props.icon]
}
return props.icon
})
</script>
<template>
<i class="v-icon">
<slot>
<component :is="componentIcon" />
</slot>
</i>
</template>
<style scoped>
.v-icon {
display: inline-block;
width: 1em;
height: 1em;
font-size: 1em;
}
.v-icon > svg {
width: 100%;
height: 100%;
}
</style>
你就能够欢快的这样应用了
<v-icon icon="dashboard" />
示例代码
示例残缺代码查看 examples/vite-vue3,具体配置和用法查看 vite-plugin-iconify。