日常使用svg作为图标使用已经很常见了,以下为几种使用方式 .
file-loader
解析资源文件最原始的无非就是直接导入使用了 , webpack使用了file-loader
对所有静态资源进行路径解析,包括图片、字体、文件等.
简单的配置对.svg
的解析.
module.exports={ module:{ rules:[{ test:/\.(svg)(\?.*)?$/, use:[{ loader:'file-loader', options:{ name:'img/[name].[hash:8].[ext]',},},],},],},};
filer-loader
可以解析import/require()
文件路径,并把文件输出到构建目录中.
然后在组件文件中使用导入的资源
<template><div><img:src="icon404"/><!--<object:data="icon404"></object><iframe:src="icon404"></iframe>--></div></template><script lang="ts">import{ Component, Vue}from"vue-property-decorator";// import icon404 from "@/assets/icons/404.svg";const icon404=require("@/assets/images/icon-404.svg");interfaceIState{ description: string;} @Component({ name:"work-bench",})exportdefaultclassextends VueimplementsIState{ description="展示当前项目拥有的组件,数据皆为模拟测试数据.";// 实例中定义引用 icon404= icon404;}</script>
通过img \ object \ iframe
作为资源载体加载资源 . 这样引入的方式对于我们常用修改颜色、字号等产生很大不便. 组件中资源过多就存在大量的import
.
可以通过打印查看icon404
的值是什么? 是对引用的当前.svg资源的路径地址.
svg-sprite-loader
声明式调用为什么说是声明式调用 , 因为它将我们需要的svg资源作为一个模版变量,进而进行指向引用即可 .
首先我们按照npm包文档指引进行配置.
# 或 yarn npm install svg-sprite-loader-D
webpack
简单配置变更了loader 为svg-sprite-loader
module.exports={ module:{ rules:[{ test:/\.(svg)(\?.*)?$/, use:[{ loader:'svg-sprite-loader', options:{ symbolId:"icon-[name]",},},],},],},};
现在在看组件内的.svg
引入 . 已经加载不出来了,这里要注意的是require
导入,
// ... 访问导入的值this.icon404= icon404.default;
use
呈现通过打印查看导入的内容icon404
<symbol xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 400 400" id="icon-icon-404">// ... svg 内容</sysmbol>
提供了id
作为引用键值 . 再使用img \ object
不行了, 了解过svg基础知识的同学知道,symbol
用来定义一个图形模版,然后使用use
呈现 .
svg-sprite-loader
会将加载的svg定义成symbol
汇聚到一个<svg>
元素中添加到body中.
这样的话 , 我们局部定义svg引用对应的symbol , 通过use
标签引用资源.
导入的icon404
是一个SpriteSymbol<id,viewBox,content>
实例, 通过id
进行访问 .
<template><div><svg><use:xlink:href="`#${icon404.id}`"/></svg></div></template>
当我们知道了svg的名称之后,加上webpack的配置symbolId: "icon-[name]"
, 就不需要使用icon404
对象了,只需要导入
<template><div><svg><use:xlink:href="#icon-icon-404"/></svg></div></template><script lang="ts">import{ Component, Vue}from"vue-property-decorator";// import icon404 from "@/assets/icons/404.svg";// const icon404 = require("@/assets/images/icon-404.svg");// 直接导入资源import"@/assets/icons/404.svg";interfaceIState{ description: string;} @Component({ name:"work-bench",})exportdefaultclassextends VueimplementsIState{ description="展示当前项目拥有的组件,数据皆为模拟测试数据.";// 此处已不再需要定义实例中的变量// icon404 = icon404.default;}</script>
就可以愉快的使用啦. 这样还存在一个问题就是,每个需要使用的组件都需要导入 . 而且这明显可以作为一个模版抽离出来进行组件化使用.
按照上面的思路,我们可以定一个index.ts
. 导入所有的svg图片,然后导出 ,
// 引入一遍所有的svg图标import'@/assets/images/icon-404.svg'import'@/assets/images/icon-404.svg'import'@/assets/images/icon-404.svg'import'@/assets/images/icon-404.svg'// ... 更多
记得要在主入口文件main.ts
引入.
// icon-svgimport"@/components/svgIcon/index.ts";
这样在所有vue组件中使用,不需要每次都导入了svg了.
svg-icon
公共组件当我们在多个组件中使用某一代码段时,我们就想把它抽离成组件.定一个svgIcon.vue
. 这样的
<template><svg:class="svgClass" v-on="$listeners"><use:xlink:href="svgName"/></svg></template><script lang="ts">import{ Component, Vue, Prop}from"vue-property-decorator"; @Component({ name:"svg-icon",})exportdefaultclassextends Vue{ @Prop()/* 图标名称 */ readonly iconName!: string; @Prop({default:""})/* 图标类名 */ readonly iconClass?: string;// 我们配置的loader 选项中,定义了 icon-[name]getsvgName(){return`#icon-${this.iconName}`;}getsvgClass(){return`svg-icon${this.iconClass}`;}}</script><style lang="less" scoped>.svg-icon{ width:1em; height:1em; fill: currentColor; overflow: hidden;}</style>
包含了svg名称 , 内部处理引入的sysmbolId
. 使用的人只需要知道svg名称是什么就行了. 不关注webpack配置. 还可以加上自定义class名.以及原生事件等等.
注册为全局组件,在index.ts
调整,
import Vuefrom"vue";import SvgIconfrom"./index.vue";// 引入一遍所有的svg图标import'@/assets/images/icon-404.svg'import'@/assets/images/icon-404.svg'import'@/assets/images/icon-404.svg'import'@/assets/images/icon-404.svg'// ... 更多// SVG 图标 Vue.component("svg-icon", SvgIcon);
然后就可以在所所有的组件中愉快的使用公共组件svg-icon
了,不需要每次都导入.
怎么使用呢, 还是上面的组件.
<template><div><!--<svg><use:xlink:href="#icon-icon-404"/></svg>--><!-- 正常使用一个vue组件--><svg-icon iconName="icon-404"></svg-icon></div></template><script lang="ts">import{ Component, Vue}from"vue-property-decorator";// 导入不需要// import icon404 from "@/assets/icons/404.svg";// const icon404 = require("@/assets/images/icon-404.svg");interfaceIState{ description: string;} @Component({ name:"work-bench",})exportdefaultclassextends VueimplementsIState{ description="展示当前项目拥有的组件,数据皆为模拟测试数据.";// 不需要// icon404 = icon404.default;}</script>
是不是很完美 . 还需要优化的点就是加载svg资源的,我们只不过是换了个地方import
// 引入一遍所有的svg图标import'@/assets/images/icon-404.svg'// 成百上千个 ....
想办法批量加载完成 . 幸好webpack提供了一个API 用于做批量加载的事情.传送门
require.context
引入指定文件夹下的所有文件require.context(dir,useSubDir,regExp,mode)
mode=sync
返回一个require函数 ,可以接受一个request参数 ;
require函数三个静态属性resolve,keys,id
. 通过编译属性kyes
执行每一个加载资源的请求.
// 指定目录加载所有的.svg资源const req= require.context("@/assets/images",false,/\.svg$/);// 编译req属性的keys , 对每一个资源路径执行加载函数 req.keys().forEach(req)
然后我们的index.ts
文件就修改为
import Vuefrom"vue";import SvgIconfrom"./index.vue";// import "@/assets/images/icon-404.svg";// SVG 图标 Vue.component("svg-icon", SvgIcon);const req=require.context("@/assets/images",false,/\.svg$/); req.keys().forEach(req)
看起来就简洁多了, 你只需要往这个目录添加你想要使用的svg资源就行了, 然后在项目中使用它.
vue.config.js
配置上面简单的自定义webpack配置时,如何配置webpack. 通常我们都使用了vue-cli
脚手架搭建vue项目.
那就要在vue.config.js
中调整loader配置了.
// vue.config.js// eslint-disable-next-lineconst path=require("path"); module.exports={chainWebpack:(config)=>{// svg-sprite-loader config.module.rules.delete("svg"); config.module.rule("svg-sprite-loader").test(/\.svg$/).include.add(path.join(__dirname,"./src/assets/icons")).end().use("svg-sprite-loader").loader("svg-sprite-loader").options({ symbolId:"icon-[name]"});}}
首先移除了.svg
的默认loader 规则配置. 开始添加针对.svg
文件的loader规则配置.
关于vue.config.js
配置详解,还会有另一篇文章来说明 . 解析chainWebpack
是如何运转的.
svgo-loader
优化svgsvgo-loader
依赖安装svgo
, 用来优化svg资源 , 清理掉多余、不必要的信息,比如:元数据信息、批注信息、隐藏的元素等.
还有用来操作svg,svgo
可以转换svgSVG-as-XML
SVG-as-JS
AST 树从而手动新增、更新、删减元素 .
{ content:[{ doctype:'',},{ comment:'',},{ elem:'svg', local:'svg', attrs:{// ...}, content:[{// ... 嵌套子元素}]}]}
需要更详细操作svg的可以自行去查看这个库, 没怎么具体使用过. 如果有这方面的需求,肯定还有有更细致的文章.
了解过之后,再来看svgo-loader
npminstall svgo-loader --save-dev
webpack
配置简单的配置,需要明确的是它不是file-loader \ svg-sprite-loader
的替代. 作用不一样.
起一个中间优化的作用.配置中添加svgo-loader
即可.
module.exports={ module:{ rules:[{ test:/\.(svg)(\?.*)?$/, use:[{ loader:'file-loader', options:{ name:'img/[name].[hash:8].[ext]',},},{ loader:"svgo-loader"}],},],},};
vue.config.js
配置在之前配置的svg-sprite-loader
追加loader配置即可.
// vue.config.js// eslint-disable-next-lineconst path=require("path"); module.exports={chainWebpack:(config)=>{// svg-sprite-loader config.module.rules.delete("svg"); config.module.rule("svg-sprite-loader").test(/\.svg$/).include.add(path.join(__dirname,"./src/assets/icons")).end().use("svg-sprite-loader").loader("svg-sprite-loader").options({ symbolId:"icon-[name]"}).end().use("svgo-loader").loader("svgo-loader");}}