如何在Vue种使用类似于Font的SVG图标系统呢?今天跟大家一起来了解下吧!!!
做过VUE项目的童鞋可能体验过,有时在Vue应用程序中管理图标的自定义集合可能很困难。图标字体易于使用,但是要进行自定义,您必须依靠第三方字体生成器,并且合并冲突可能很难解决,因为字体是二进制文件。
相反,使用SVG文件可以消除这些痛点,但是我们如何确保它们同样易于使用,同时又可以轻松添加或删除图标?
这是我理想的图标系统的外观:
-
要添加图标,只需将它们放入指定的
icons
文件夹中。如果您不再需要图标,只需删除它即可。 -
要在模板中使用robot.svg图标,语法非常简单
<svg-icon icon="rocket" />
。 -
可以使用CSS
font-size
和color
属性(就像图标字体一样)对图标进行缩放和着色。 -
如果页面上出现同一图标的多个实例,则不会每次都重复SVG代码。
-
无需编辑Webpack配置。
这是我们通过编写两个小的单文件组件来构建的。此实现有一些特定的要求,尽管我敢肯定你们中的许多向导可以针对其他框架和构建工具对该系统进行重新设计:
-
webpack:如果您使用Vue CLI来搭建应用程序,则说明您已经在使用webpack。
-
svg-inline-loader:这使我们可以加载所有SVG代码并清理不需要的部分。继续并
npm install svg-inline-loader --save-dev
从终端运行开始。
SVG Sprite组件
为了满足我们对页面上每个图标实例不重复SVG代码的要求,我们需要构建一个SVG"精灵"。如果您以前从未听说过SVG子画面,请将其视为包含其他SVG的隐藏SVG。在需要显示图标的任何地方,我们都可以通过引用<use>
标签内的图标ID来将其复制到sprite之外,如下所示:
<svg><use xlink:href="#rocket" /></svg>
这段代码本质上就是我们<SvgIcon>
组件的工作方式,但是让我们继续先创建该<SvgSprite>
组件。这是整个SvgSprite.vue
文件;一开始它似乎有些令人生畏,但我将其分解。
<!-- SvgSprite.vue -->
<template>
<svg width="0" height="0" style="display: none;" v-html="$options.svgSprite" />
</template>
<script>
const svgContext = require.context(
'!svg-inline-loader?' +
'removeTags=true' + // remove title tags, etc.
'&removeSVGTagAttrs=true' + // enable removing attributes
'&removingTagAttrs=fill' + // remove fill attributes
'!@/assets/icons', // search this directory
true, // search subdirectories
/\w+\.svg$/i // only include SVG files
)
const symbols = svgContext.keys().map(path => {
// get SVG file content
const content = svgContext(path)
// extract icon id from filename
const id = path.replace(/^\.\/(.*)\.\w+$/, '$1')
// replace svg tags with symbol tags and id attribute
return content.replace('<svg', `<symbol id="${id}"`).replace('svg>', 'symbol>')
})
export default {
name: 'SvgSprite',
svgSprite: symbols.join('\n'), // concatenate all symbols into $options.svgSprite
}
</script>
在模板中,我们的lone <svg>
元素的内容绑定到$options.svgSprite
。如果您不熟悉$options
它包含直接附加到我们的Vue组件的属性。我们可以附加svgSprite
到组件的data
,但是我们真的不需要Vue为此设置响应性,因为我们的SVG加载器仅在构建应用程序时运行。
在脚本中,我们用于require.context
检索所有SVG文件,并在使用时清理它们。我们svg-inline-loader
使用与查询字符串参数非常相似的语法来调用并传递几个参数。我将它们分成多行以使它们更易于理解。
const svgContext = require.context(
'!svg-inline-loader?' +
'removeTags=true' + // remove title tags, etc.
'&removeSVGTagAttrs=true' + // enable removing attributes
'&removingTagAttrs=fill' + // remove fill attributes
'!@/assets/icons', // search this directory
true, // search subdirectories
/\w+\.svg$/i // only include SVG files
)
我们在这里基本上要做的是清理位于特定目录中的SVG文件,以(/assets/icons
使它们处于良好的状态以在需要的任何地方使用。
该removeTags
参数剔除标签,我们并不需要为我们的图标,如不title
和style
。我们特别想删除title
标签,因为这些标签可能会导致不必要的工具提示。如果要在图标中保留任何硬编码的样式,请添加removingTags=title
作为附加参数,以便仅title
删除标记。
我们还告诉加载程序删除fill
属性,以便fill
稍后可以使用CSS 设置自己的颜色。您可能会想要保留自己的fill
颜色。如果是这种情况,则只需删除removeSVGTagAttrs
和removingTagAttrs
参数。
最后一个加载程序参数是SVG图标文件夹的路径。然后require.context
,我们提供了另外两个参数,以便它搜索子目录并仅加载SVG文件。
为了将所有SVG元素嵌套在SVG精灵中,我们必须将它们从<svg>
元素转换为SVG <symbol>
元素。这就像更改标签并为每个标签赋予唯一性一样简单id
,然后从文件名中提取出唯一性。
const symbols = svgContext.keys().map(path => {
// extract icon id from filename
const id = path.replace(/^\.\/(.*)\.\w+$/, '$1')
// get SVG file content
const content = svgContext(path)
// replace svg tags with symbol tags and id attribute
return content.replace('<svg', `<symbol id="${id}"`).replace('svg>', 'symbol>')
})
我们如何处理这个<SvgSprite>
组件?我们将其放置在我们页面上的所有依赖它的图标之前。我建议将其添加到App.vue
文件的顶部。
<!-- App.vue -->
<template>
<div id="app">
<svg-sprite />
<!-- ... -->
图标组件
现在,我们来构建SvgIcon.vue
组件。
<!-- SvgIcon.vue -->
<template>
<svg class="icon" :class="{ 'icon-spin': spin }">
<use :xlink:href="`#${icon}`" />
</svg>
</template>
<script>
export default {
name: 'SvgIcon',
props: {
icon: {
type: String,
required: true,
},
spin: {
type: Boolean,
default: false,
},
},
}
</script>
<style>
svg.icon {
fill: currentColor;
height: 1em;
margin-bottom: 0.125em;
vertical-align: middle;
width: 1em;
}
svg.icon-spin {
animation: icon-spin 2s infinite linear;
}
@keyframes icon-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
}
</style>
这个组件要简单得多。如前所述,我们利用<use>
标记来引用精灵中的ID。那id
来自我们组件的icon
道具。
我spin
在那里添加了一个道具,可以根据需要切换.icon-spin
类作为动画的可选位。例如,这对于加载微调器图标可能很有用。
<svg-icon v-if="isLoading" icon="spinner" spin />
根据您的需要,您可能需要添加其他道具,例如rotate
或flip
。您可以根据需要直接将类直接添加到组件中,而无需使用道具。
我们组件的大部分内容是CSS。除了旋转动画之外,大多数动画都用于使我们的SVG图标更像图标字体。为了使图标与文本基线对齐,我发现在大多数情况下都可以应用vertical-align: middle
,底边0.125em
为。我们还将fill
属性值设置为currentColor
,这使我们可以像为文本一样为图标着色。
<p style="font-size: 2em; color: red;">
<svg-icon icon="exclamation-circle" /><!-- This icon will be 2em and red. -->
Error!
</p>
而已!如果要在应用程序中的任何位置使用图标组件,而不必将其导入到需要它的每个组件中,请确保在main.js
文件中注册该组件:
// main.js
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon.vue'
Vue.component('svg-icon', SvgIcon)
// ...
总结
这里有一些改进的想法,我故意遗漏一些,以使该解决方案易于处理:
-
缩放具有非正方形尺寸的图标以保持其比例
-
无需其他组件即可将SVG Sprite注入页面。
-
使它与vite一起使用,vite是Vue创作者Evan You的一个新的,快速的(且无webpack的)构建工具。
-
利用Vue 3 Composition API。
如果您想快速使用这些组件,我已经基于默认的vue-cli模板创建了一个演示应用程序。如果对于DEMO有兴趣可以加群讨论拿源码,希望这可以帮助您开发适合您应用程序需求的实现!