前言
在前端项目中,使用自定义的icon图标时,需要引用很长的相对地址,添加和修改图标会变得很麻烦。
<img src="./long/path/to/your/svg/icon.svg" />
有没有更简单的icon图标引入方式呢?像使用组件库中的ICON组件一样。
<a-icon type="fast-backward" />
答案是肯定的,我们可以通过SVG Sprite简化自定义图标的使用方式。
SVG Sprite
如果你没听过SVG Sprite,也许听过雪碧图(CSS Sprite),如下图所示。雪碧图是为了减少网络请求次数,将许多小图标整合到一张图片上,然后通过CSS定位技术显示特定位置的图标。雪碧图在使用上存在一些弊端,目前已经很少使用了。
类似的SVG Sprite是通过<symbol>和<use>实现的。<symbol>元素可以把svg图标定义成一个图形模板对象,<use>元素通过xlink:href属性引用symbol id展示图形。下面代码定义了三个<symbol>图形模板,此时图形并不会展示到页面上,通过<use>元素引用symbol id后才可展示图形。
//定义图形
<svg width="0" height="0">
<symbol id="shape1">
<circle cx="40" cy="40" r="24" style="stroke:#006600; fill:#00cc00"/>
</symbol>
<symbol id ="shape2">
<rect x="10" y="10" height="100" width="100" style="stroke:#006600; fill: #00cc00"/>
</symbol>
<symbol id="shape3">
<polygon points="10,0 60,0 35,50" style="stroke:#660000; fill:#cc3333;"/>
</symbol>
</svg>
//引用图形
<svg width="500" height="200">
<use xlink:href="#shape1" x="0" y="25" />
<use xlink:href="#shape1" x="60" y="25" />
<use xlink:href="#shape2" x="150" y="0" />
<use xlink:href="#shape3" x="280" y="10" />
</svg>
通过上面示例代码可以看出:
<use>元素可以跨<svg>元素引用<symbol>。<use>可以重复引用<symbol>。
如果将项目中的svg图标用<symbol>元素定义成图形模板,并将其组合成一个大的<svg>加载到页面中,如下图所示。那么我们可以在页面的任何位置,只需要一行代码就可以引用这个图标了。
<svg class="size"><use xlink:href="#icon-tubiao" /></svg>
进一步,将<use>对<symbol>元素的引用封装到组件中,通过动态的传递svg文件名来展示不同的svg图标,从而实现简洁的引用自定义图标。
<svg-icon name="tubiao" />
如何将svg图标组合一起呢?svg-sprite-loader可以帮我们实现。
svg-sprite-loader使用
svg-sprite-loader会把项目中引入的svg图标塞到一个个<symbol>中,合成一个大的svg,最后将这个大的svg插入到<body>元素中。
下面基于vue2.0介绍svg-sprite-loader配置。
1. 安装svg-sprite-loader
npm i svg-sprite-loader -D
2. 配置vue.config.js
本示例中是将项目中的svg图标统一放到在src/icons/svg目录中,可根据实际需要调整。配置svg-sprite-loader前,首先清除webpack默认loader(如url-loader,file-loader等)对src/icons/svg目录中svg图标处理,然后添加svg-sprite-loader对src/icons/svg目录下的svg图标的处理。指定symbolId为文件名加上“icon-”前缀。
const path = require("path");
function resolve(dir) {
// __dirname项目根目录的绝对路径
return path.join(__dirname, dir);
}
module.exports = {
chainWebpack: (config) => {
//默认规则不处理src/icons目录下的svg文件
config.module.rule("svg").exclude.add(resolve("src/icons/svg")).end();
//针对icons/svg目录下的文件添加svg-sprite-loader规则
config.module
.rule("svgicon")
.test(/\.svg$/)
.include.add(resolve("src/icons/svg"))
.end()
.use("svg-sprite-loader")
.loader("svg-sprite-loader")
.options({
symbolId: "icon-[name]",
});
},
}
3. 封装SvgIcon组件
将<use>对<symbol>元素的引用封装到组件中,通过动态的传递svg文件名来展示不同的svg图标。
<template>
<svg :class="svgClass" aria-hidden="true">
<use :xlink:href="iconName" />
</svg>
</template>
<script>
export default {
name: "SvgIcon",
props: {
name: {
type: String,
required: true,
},
className: {
type: String,
},
},
computed: {
iconName() {
//构造symbolId
return `#icon-${this.name}`;
},
svgClass() {
if (this.className) {
return "svg-icon " + this.className;
} else {
return "svg-icon";
}
},
},
};
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>
4. 全局注册SvgIcon组件,并加载所有图标。
- 在icons目录下,分别创建svg目录和index.js文件。
- 在svg目录中存放项目中使用到的所有svg图标。
- 在index.js中,将SvgIcon组件,注册为全局组件,之后就可以全局使用了。
- 在index.js中,将svg目录中的图标一次性导入,之后就不需要单独引入了。
import Vue from "vue";
import SvgIcon from "../components/svgIcon/Index"; //svg组件
//全局注册组件
Vue.component("svg-icon", SvgIcon);
// 定义一个加载目录的函数
const requireAll = (r) => r.keys().map(r);
// 批量导入svg目录下的svg文件
requireAll(require.context("./svg", false, /\.svg$/));
5. 在入口文件中导入icons/index.js 文件
在main.js入口文件导入icons/index.js文件,使其在首次导入时运行一次。
import "./icons/index.js"
6. 使用
现在只需传入文件名即可引入svg图标。
//name:svg图标的文件名
//class-name:svg图标的样式类名
<svg-icon name="download" class-name="icon" />
<svg-icon name="supervise" class-name="icon" />
<svg-icon name="view_off" class-name="icon" />
下图是使用SvgIcon组件后的页面DOM结构,可以看到<body>元素中有一个由多个<symbol>元素组成的<svg>,通过<use>元素将图标展现在了页面中。
总结
本文首先介绍了SVG Sprite原理,然后详细介绍了svg-sprite-loader的安装、配置及SvgIcon组件的封装与使用。希望本文对你起到帮助。