手摸手,带你优雅的使用 SvgIcon
前言:在做前端后台项目的时候经常会用到很多 icon 图标,刚开始还好,但随着项目的不断迭代,每次修改添加图标会变得很麻烦,而且总觉得不够优雅,就开始琢磨着有啥简单方便的工作流呢?
演进史
远古时期,大部分图标都是用img的方式导入,渐渐发现一个页面的请求资源中img资源占用了大部分,所以为了优化有了精灵图。所谓精灵图就是将多个图片合成一张图片,然后利用css的background-postion定位显示不同的icon。但是也有很大的痛点,维护困难,每次图片变更就需要重新修改精灵图。
font库实现页面图标,例如Font Awesome,使用起来也非常的方便,但是找起来很不方便,非常费眼睛,定制性需求也非常不友善,常常出现找不到所需要图标的情况。
**iconfont**一个阿里爸爸做的开源图库,支持上传下载以及颜色自定义设置,不仅有几百个公司的开源图标库,还有各式各样的小图标,还支持自定义创建图标库,所以不管你是一家创业公司还是对设计很有要求的公司,它都能很好的帮助你解决管理图标的痛点。你想要的基本都有~
svg矢量图,用于描述二维矢量图形的一种图形格式,可以任意定制,图像质量不下降的情况下可任意放大或缩小,超强显示效果,完全支持DOM。
svg的使用方式
1. 直接通过img标签使用svg
首先,可以在iconfont中寻找所需要的icon,并下载为svg格式
其次新建一个html文件 输入img 通过直接给src复制当前文件所在目录即可展示
<img src="../src/icons/svg/dashboard.svg" alt="">
<img src="../src/icons/svg/example.svg" alt="">
<img src="./haha.svg" alt="">
我们来看一下效果~
2. 直接使用svg标签
在html文件中直接复制svg文件
<svg width="128" height="100" xmlns="http://www.w3.org/2000/svg">
<path
d="M27.429 63.638c0-2.508-.893-4.65-2.679-6.424-1.786-1.775-3.94-2.662-6.464-2.662-2.524 0-4.679.887-6.465 2.662-1.785 1.774-2.678 3.916-2.678 6.424 0 2.508.893 4.65 2.678 6.424 1.786 1.775 3.94 2.662 6.465 2.662 2.524 0 4.678-.887 6.464-2.662 1.786-1.775 2.679-3.916 ...
3. 使用symbol-元件包裹多个svg(雪碧图原理)
这样的做法相当于将所有的小icon全部导入,然后利用symbol的语法 使用对应的svg,为下一节的封装svg组件做铺垫
雪碧图原理 symbol - 元件
1. svg标签改成symbol标签
2. 用svg把所有symbol包裹
3. 给所有symbol元素加唯一标识
4. 使用use标签,需要那个svg就直接使用
<symbol id="a"> id代表对应的svg编号 也就是唯一标识
... svg源文件A
</symbol>
<symbol id="a">
... svg源文件B
</symbol>
.....
使用方式 #a 显示对应的svg
<svg aria-hidden="true" class="svg-icon">
<use xlink:href="#a"></use>
</svg>
大家想一个问题,项目中要使用到的icon有很多,一个一个引入非常非常麻烦,有没有什么快捷的方式,直接生成svg精灵图,在开发过程中,我们只需要使用就可以了呢?
接下来我为大家介绍在vue2以及vue3中如何使用对应的svg精灵图技术
vue2项目 - svg-sprite-loader
1. 通过vue create vue-demo 快速创建vue2项目
2. 删除项目中多余的组件 保留app.vue
3. 在src下创建icons文件夹,接着在此文件夹下创建svg文件,导入所有需要使用到的svg文件(自行添加svg)
4. 安装组件svg-sprite-loader
npm install svg-sprite-loader --save-dev || yarn add svg-sprite-loader
此时我们打开浏览器控制台,已自动生成svg雪碧图
5. 在src/components下新建文件夹及文件SvgIcon/index.vue,index.vue添加如下内容:
这一步的目的就是封装全局svg组件,并进行默认配置和通讯。
<template>
<div
v-if="isExternal"
:style="styleExternalIcon"
class="svg-external-icon svg-icon"
// listeners属性,它是一个对象,里面包含了作用在这个组件上的所有监听器,你就可以配合 v-on="$listeners" 将所有的事件监听器指向这个组件的某个特定的子元素。(相当于子组件继承父组件的事件)
v-on="$listeners"
/>
// 使用svg精灵图的固定写法
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
// iconName用于接收传递的svg编号
<use :xlink:href="iconName" />
</svg>
</template>
<script>
//导入公共方法,校验传入的iconClass是否为外部链接
//匹配http或者 https
import { isExternal } from '@/utils/validate'
export default {
name: 'SvgIcon',
props: {
// 当前svg编号=>必填
iconClass: {
type: String,
required: true
},
// svg组件名词 =>选填 可以不写
className: {
type: String,
default: ''
}
},
computed: {
//匹配http或者 https
isExternal () {
return isExternal(this.iconClass)
},
iconName () {
return `#icon-${this.iconClass}`
},
svgClass () {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
},
styleExternalIcon () {
return {
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
}
}
}
}
</script>
<style scoped>
// 限制svg大小为父元素的大小
.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>
6. 需要将当前svg组件设置为全局组件
在src下新建icons文件夹,及icons文件夹下svg文件夹、index.js文件,将svg图片放入svg文件夹中,在 index.js文件中添加如下内容
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'// svg 组件
// 全局注册svg组件
Vue.component('svg-icon', SvgIcon)
// 工程化导入svg图片 require.context(directory,useSubdirectories,regExp)
// directory:表示检索的目录 useSubdirectories:表示是否检索子文件夹 regExp:匹配文件的正则表达式,一般是文件名
// 此处require.context()方法生成了一个类require方法,这个方法接收一个参数req,根据这个参数我们可以得到相应的模块信息。 比如传入svg目录下的404.svg文件名,req('./404.svg'),就会返回相对应的模块。
// 且这个类require方法有keys()方法,可以遍历获得目录下所有的子文件名。
const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)
7. 在mian.js中引入icons下的index.js
import '@/icons' // icon
8. 配置 vue.config.js(主要为打包进行设置) =>插件固定写法 可以直接cv
const path = require('path')
// 将传入的相对路径转换成绝对路径
function resolve (dir) {
return path.join(__dirname, dir)
}
module.exports = {
chainWebpack (config) {
// set svg-sprite-loader
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()
}
}
最后我们在组件中使用下吧~
<template>
<div >
<svg-icon icon-class="lock" class-name="sy-svg" class="icon"/>
<svg-icon icon-class="haha" class="icon" />
</div>
</template>
<style scoped >
.icon {
font-size: 50px;
}
</style>
我们看一下效果~ 手动撒花★,°:.☆( ̄▽ ̄)/$:.°★ 。
vue3项目 - vite-plugin-svg-icons
实现:根据 icons 文件svg图片打包到项目中,通过组件使用图标
1. 安装vue3项目
pnpm create vue vue3-svgicon
2. 安装插件
yarn add vite-plugin-svg-icons -D
# or
npm i vite-plugin-svg-icons -D
# or
pnpm install vite-plugin-svg-icons -D
3. 使用插件
// vite.config.ts
import { VantResolver } from 'unplugin-vue-components/resolvers'
+import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
+import path from 'path'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
Components({
dts: false,
resolvers: [VantResolver({ importStyle: false })]
}),
+ createSvgIconsPlugin({
+ // 指定图标文件夹,绝对路径(NODE代码)
+ iconDirs: [path.resolve(process.cwd(), 'src/icons')]
+ })
],
4. 导入插件
// mian.ts
import router from './router'
+import 'virtual:svg-icons-register'
import 'vant/lib/index.css'
5. 封装全局组件
<script setup lang="ts">
// 接收name
defineProps<{
name: string
}>()
</script>
<template>
<svg aria-hidden="true" class="cp-icon">
<!-- #icon-文件夹名称-图片名称 -->
<use :href="`#icon-${name}`" />
</svg>
</template>
<style lang="scss" scoped>
.cp-icon {
// em 是一个相对单位,相对的是自身的fint-size大小,如果自身没有,参考父元素
width: 1em;
height: 1em;
}
</style>
6. 给组件设置类型 types/components.d.ts
import SvgIcon from '@/components/CpIcon.vue'
declare module 'vue' {
interface GlobalComponents {
SvgIcon: typeof SvgIcon
}
}
7. 使用
<template>
<cp-icon name="login-eye-off" class="icon"></cp-icon>
<cp-icon name="home-haha" class="icon"></cp-icon>
</template>
<style lang="scss" scoped>
.icon {
font-size: 33px;
}
</style>
效果展示~~~