使用 Element plus Icon
https://element-plus.gitee.io/zh-CN/component/icon.html
自定义 SVG 图标
对于 element plus 的图标,我们可以直接通过 el-icon 来显示。
| <template> <div> <el-icon :size="size" :color="color"> <Edit /> </el-icon>
<Edit /> <Edit style="width: 1em; height: 1em; margin-right: 8px" /> <Share style="width: 1em; height: 1em; margin-right: 8px" /> </div> </template>
|
但是自定义的图标,我们却没有显示的方式,那么我们就需要一个自定义的组件,来展示自定义的 svg
图标。
对于这个自定义的组件,它需要拥有两种能力:
- 显示外部的 svg 图标(链接的方式)
- 显示项目内的 svg 图标
接下来,我们就来实现自定义组件。
显示外部的 svg 图标
css mask:
mask 属性允许使用者通过遮罩或者裁切特定区域的图片的方式来 隐藏一个元素的部分
或者 全部可见区域
。
css mask: https://juejin.cn/post/6846687594693001223
接下来,我们定义组件 SvgIcon
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| <template> <div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" :class="className" /> </template>
<script setup lang="ts"> import { defineProps, computed } from 'vue'
const props = withDefaults(defineProps<{ icon: string className?: string }>(), { className: '', icon: '' })
const isExternal = computed(() => /^(https?:|mailto:|tel:)/.test(props.icon))
const styleExternalIcon = computed(() => ({ mask: `url(${props.icon}) no-repeat 50% 50%`, '-webkit-mask': `url(${props.icon}) no-repeat 50% 50%` }))
</script> <style scoped> .svg-icon { width: 1em; height: 1em; vertical-align: -0.15em; fill: currentColor; overflow: hidden; }
.svg-external-icon { background-color: currentColor; mask-size: cover !important; display: inline-block; } </style>
|
这样,我们就可以通过 链接
的形式,来使用 svg
图标了:
1
| <svg-icon icon="https://res.lgdsunday.club/user.svg"></svg-icon>
|
展示内部图标
如果不是外部链接的话,我们就展示项目内部的 svg
图标。
通过 use 的 xlink href
属性,找到 body 下已经处理的过的 svg sprite 元素内容,它其中就包含了 symbol
元素,每个 symbol
元素上都有一个 id
,这个 id 就是和 xlink:href
与之对应的。
1 2 3 4 5 6
| <svg v-else class="svg-icon" :class="className" aria-hidden="true"> <use :xlink:href="iconName" /> </svg>
const iconName = computed(() => `#icon-${props.icon}`)
|
当我们定义好了组件之后,那么就需要在项目中,导入所有的 svg 图标了。
1 2 3 4
|
require('./svg/user.svg') require('./svg/password.svg')
|
假设我们有几百上千个 svg 图标,我们都要这样子引入吗?会不会太难受了 ~~
这里,我们可以使用 webpack 提供的 require.context 方法,来 批量
导入 svg 图标:
1 2 3 4 5 6 7 8
|
const svgRequire = require.context('./svg', false, /\.svg$/)
svgRequire.keys().forEach(svgIcon => svgRequire(svgIcon))
|
这样,就完成了所有的本地 svg
图片导入。
然后我们注册全局的 SvgIcon
组件,方便使用。
1 2 3 4 5 6 7 8
|
import type { App } from 'vue' import SvgIcon from '@/components/SvgIcon/index.vue'
export default (app: App): void => { app.component('svg-icon', SvgIcon) }
|
到这里,我们去页面上使用 <svg-icon icon="user" />
,发现没有效果,图标展示不出来。
这是因为,我们虽然在 icons/index.ts
中,通过 require.context
导入了所有的 svg 图片,但是并没有做处理,svg-icon
内部的 <use :xlink:href="#icon-user" />
找不到任何跟 #icon-user
有关的 svg 图标。
这里,我们就需要用到 svg-sprite-loader
了。
svg-sprite-loader
svg-sprite-loader 的官方解释是:一个用于创建 svg 雪碧图的 Webpack 加载器。这个加载器现在已经被 JetBrains 公司收录和维护了。
通俗的讲:svg-sprite-loader 会把你引入的 svg 塞到一个个 symbol 中,合成一个大的 svg,最后将这个大的 svg 放入 body 中。symbol 的 id 如果不特别指定,就是你的文件名。在页面上形成这样的元素,下面是导入了本地的 user.svg
和 password.svg
:
我们可以看到,每个 symbol
上,都有一个 id
属性,因为我们在 SvgIcon
中指定了 use
的 :xlink:href
,使用时,就能找到页面上对应的图标了。
接着,我们来配置 svg-sprite-loader
,打开 vue.config.js
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| const path = require('path')
function resolve(dir) { return path.join(__dirname, dir) }
module.exports = { chainWebpack(config) { 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() } }
|
这两条规则在 webpack 配置中设置了两种不同的方法来处理 SVG 文件:一种用于排除位于 src/icons 目录中的 SVG 文件,另一种用于使用 svg-sprite-loader 处理位于 src/icons 目录中的 SVG 文件。
到这里,我们就完成了 svg-sprite-loader
的配置,然后重新启动项目,就可以愉快的使用本地的 svg
了。
1 2 3 4 5
| <svg-icon icon="user"></svg-icon>
<svg-icon icon="password"></svg-icon>
|
页面上的效果是这样的
总结
经过 svg-sprite-loader
加载之后,不仅可以通过指定 id 的方式引入 icon,而且相比图片引入的方式,最大的优点就在于可以通过给 svg 标签添加 fill 属性来调整 icon 的颜色。
除此之外,还可以通过给 svg 添加 class 来调整 icon 的样式,虽然说图片引入的方式也能做到,但是如果图片指定宽高与原图的宽高不成比例,就会导致图片的失真,而 svg 不会。即使随意调整 svg 的宽高样式,它也是按照原尺寸进行缩放,达到高保真的效果。
而且通过svg-sprite-loader
的处理后,生成了精灵图,它是一种将多个图标放在一张图片中的技术,可以减少 HTTP 请求数,从而提升网站性能。