本节是渐进式vue3的组件库通关秘籍的第五节 -- 基于svg-sprite的图标组件。 更多可以看这里:vue3组件库开发学习专栏
0、前言
前端实现图标通常有以下几种技术:
- 使用图标字体:将图标设计成字体文件,然后通过CSS的字体属性来使用。常用的图标字体包括 Font Awesome、Material Icons等。
- SVG 图标:使用矢量图形文件格式 SVG(可伸缩矢量图形)作为图标,通过
<svg>标签嵌入到HTML代码中。 - CSS Sprite:将所有图标合并成一张大图片,然后通过CSS来控制显示区域和位置,实现图标的展示。
- Iconfont:通过字体图标库(如阿里巴巴矢量图标库)提供的在线字体图标资源来使用图标。
- Base64 编码:将图标图片转换成Base64编码的字符串,然后通过CSS的background属性来使用。
本节介绍的是基于svg的图标使用方案。
1、支持SVG文件作为图标
图标组件需要实现以下功能:
-
图标组件接受svg文件,使用方式如下:
import 'path/to/message.svg' <Icon name='icon-message' /> -
能够自定义宽高,样式,填充颜色。
总的来说组件的属性如下:
| 属性名称 | 类型 | 描述 | 默认值 |
|---|---|---|---|
| name | string | svg图标文件名称 | null |
| style | CSSProperties | 图标的样式 | null |
| width | number | 宽度 | 16 |
| height | number | 高度 | 16 |
| fill | string | 填充颜色 | #ffffff |
在使用的时候,需要将引入的SVG文件需要使用webpack将原始的svg文件转换成svg-sprit,类似于下面的格式:
<svg>
<symbol id='icon-test'></symbol>
<symbol id='icon-home'></symbol>
<svg>
然后我们在只需要在svg标签内使用use,引用上面编译出来的某一个 symbol 的 id 即可。
<svg>
<use xlink:href='icon-test'/>
<svg>
我们将上面的逻辑封装成组件如下:
新建 components/icon/icon.tsx :
import { defineComponent } from 'vue';
import { props } from './props';
import withInstall from '../../utils/withInstall';
import './style/index.less';
const Icon = defineComponent({
props,
setup(props) {
return () => {
return (
<svg
class="svg-icon"
style={{ width: props.width, height: props.height, ...props.style }}
aria-hidden="true"
>
<use xlinkHref={`#${props.name}`} />
</svg>
);
};
},
});
export default withInstall(Icon);
新建 components/Icon/props.ts,定义组件的属性:
import type { CSSProperties, PropType } from 'vue';
export const props = {
style: {
type: Object as PropType<CSSProperties>,
default: null,
},
height: {
type: Number,
default: 16,
},
width: {
type: Number,
default: 16,
},
fill: {
type: String,
default: '#ffffff',
},
name: {
type: String,
default: 'home',
},
};
export type IconProps = typeof props;
新建 components/icon/index.ts 导出组件:
import icon from './icon';
export default icon;
新建 components/icon/style/index.less ,定义icon的默认样式:
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
组件的所有逻辑就如上所示,接下来完成组件的预览和使用。
2、预览和使用
由于组件的使用,需要和svg文件搭配,并且需要将svg文件解析封装成svg-spirte,所以需要以下两个loader
- svg-sprite-loader : 将svg封装成sprite
- svgo-loader: 解析svg文件并优化文件大小
安装依赖:
npm i -D svg-sprite-loader svgo-loader
修改 config/webpack.dev.config.js
const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin');
const { merge } = require('webpack-merge');
const baseConfig = require('./webpack.base.config');
const distfilename = 'fakeui';
const resolveDir = dir => path.join(__dirname, `../${dir}`);
const dev = merge(baseConfig, {
...
module: {
rules: [
{
test: /.svg$/,
use: [
{
loader: 'svg-sprite-loader',
options: {
symbolId: 'icon-[name]',
include: [path.join(__dirname, 'preview/statics/icons')],
},
},
'svgo-loader',
],
},
],
},
...
});
module.exports = [dev];
其中 svg-sprite-loader的include表示要解析哪些文件夹下的svg文件。
接下来在app.tsx 中引入看一下效果:
import { defineComponent } from 'vue';
import Icon from '../components/icon';
import './statics/icons/test.svg';
export default defineComponent({
setup() {
const render = () => {
return (
<>
<Icon name="icon-test" width={200} height={200} />
</>
);
};
return render;
},
});
静态文件大家可以随意找一个svg文件放到 preview/statics/icons 文件夹下。
最终效果如下:
3、内置图标
对于组件库的内置图标,多数的组件库都选择将内置图标单独出来一个库使用,方便增删图标,独立打包,独立发布,易于管理。
对于内置图标,也是通过本节的Icon组件,引入svg文件完成的,所以,对于Icon组件,需要支持传入自身组件的实例,以方便内置图标的引入,也就是说图标的使用方式可以有以下三种:
- 通过 name 引入 svg 文件
- 通过 component 引入可复用的图标组件
- 直接使用可复用的图标组件
import IconTest from 'path/to/icon'
<Icon name="icon-test" width={200} height={200} />
<Icon component={IconTest} />
<IconTest />
所以 Icon 组件需要再引入一个 component 属性。
修改 components/Icon/props.ts:声明一个类型为纯函数组件的属性
import type { CSSProperties, PropType } from 'vue';
export const props = {
...
// 传入组件,声明为纯函数组件
component: {
type: Function,
default: null,
},
};
export type IconProps = typeof props;
修改 preview/app.tsx:
import { defineComponent } from 'vue';
import Icon from '../components/icon';
import './statics/icons/test.svg';
const TestIcon = function () {
return <Icon name="icon-test" width={200} height={200} />;
};
export default defineComponent({
setup() {
const render = () => {
return (
<>
<Icon component={TestIcon} />
<TestIcon />
</>
);
};
return render;
},
});
这里声明了一个纯函数组件,将其作为属性传入 Icon 组件即可,组件能够被正确的渲染。
所以可以通过Icon组件开发一个内置图标库,然后使用的时候再组件库内引入即可。
4、总结
本节实现了一个基于 svg-sprite 的图标组件,能够直接使用svg文件作为图标
使用svg图标主要有以下好处:
- SVG 图标是矢量图形,无论放大缩小,都可以保持图标的清晰度和锐利度。
- SVG 图标可以轻松实现多色图标,适用于需要多种颜色的图标场景。
- 每个 SVG 图标都是一个独立的文件,需要单独加载,可能会导致 HTTP 请求过多,但可以通过雪碧图或者 SVG Sprite 等技术进行优化
参考文献:
本节代码分支:
欢迎点赞、欢迎star、欢迎讨论、一起学习。
下一节:Loading组件