(5)添加svg支持
1 安装svg-sprite-loader
cnpm install svg-sprite-loader --save
svg-sprite-loader
是一个webpack loader
,可以将多个svg
打包成svg-sprite
。
2 配置svg-sprite-loader
我们发现vue-cli
默认情况下会使用 url-loader
对svg
进行处理,会将它放在/img
目录下,所以这时候我们引入svg-sprite-loader
会引发一些冲突。
//默认`vue-cli` 对svg做的处理,正则匹配后缀名为.svg的文件,匹配成功之后使用 url-loader 进行处理。
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
}
解决方案有两种,最简单的就是你可以将 test
的 svg
去掉,这样就不会对svg
做处理了,当然这样做是很不友善的。
- 你不能保证你所有的
svg
都是用来当做icon
的,有些真的可能只是用来当做图片资源的。 - 不能确保你使用的一些第三方类库会使用到
svg
。
所以最安全合理的做法是使用 webpack
的 exclude
和 include
,让svg-sprite-loader
只处理你指定文件夹下面的 svg
,url-loaer
只处理除此文件夹之外的所以 svg
,这样就完美解决了之前冲突的问题。
修改webpack.base.conf.js
文件关于svg
部分内容:
{
test: /\.svg$/,
loader: 'svg-sprite-loader',
include: [resolve('src/icons')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
exclude: [resolve('src/icons')],
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
}
这里,把所有需要由svg-sprite-loader
处理的svg
图标文件放到src/icons
目录下。 而url-loader
处理的svg
图标文件,将排除src/icons
目录下的svg
文件。
3 svg组件
在components
目录下,新建icon-svg
目录,该目录下新建index.vue
文件:
<template>
<!-- aria-hidden="true" 是用于屏幕阅读器的,帮助残障人士更好的访问网站。
残障人士如失明的人使用识读设备(自动读取内容并自动播放出来),
播放到带此属性的内容时会自动跳过,以免残障人士混淆!-->
<svg
:class="getClassName"
:width="width"
:height="height"
aria-hidden="true">
<use :xlink:href="getName"></use>
</svg>
</template>
<script>
export default {
name: 'icon-svg',
props: {
// 必须提供name属性
name: {
type: String,
required: true
},
className:{
type:String
},
width:{
type:String
},
height:{
type:String
}
},
computed:{
getName(){
return `#icon-${this.name}`
},
getClassName(){
return [
'icon-svg', // 已经定义好这个类的样式(style)
`icon-svg__${this.name}`,
this.className && /\S/.test(this.className)?`${this.className}`:''
]
}
}
}
</script>
<style>
.icon-svg {
width: 1em;
height: 1em;
fill: currentColor;
overflow: hidden;
}
</style>
4 读取svg文件
4.1 准备svg文件
在scr
目录下新建icons
文件夹,在此文件夹下新建svg
文件夹,复制svg
文件到此文件夹下(这里svg
文件默认是icon-*.svg
的格式,接下来能讲到)
4.2 svg读取
在icons
文件夹内新建index.js
文件:
import Vue from 'vue'
// 引入IconSvg组件,在目录/components/icon-svg下的index.vue的内容
import IconSvg from '@/components/icon-svg'
// 全局注册组件
Vue.component('IconSvg',IconSvg)
const svgFiles = require.context('./svg',true,/\.svg$/)
const iconList = svgFiles.keys().map(item =>svgFiles(item))
export default{
// 获取图标icon-(*).svg名称列表, 例如[shouye, xitong, zhedie, ...]
getNameList(){
return iconList.map(item=>item.default.id.split('-')[1])
}
}
4.3 带表达式的 require 语句
如果你的 require参数含有表达式(expressions),会创建一个上下文(context),因为在编译时(compile time)并不清楚具体是哪一个模块被导入
require("./template/" + name + ".ejs");
webpack 解析 require() 的调用,提取出来如下这些信息: Directory: ./template Regular expression: /^.*\.ejs$/
则会返回template目录下的所有后缀为.ejs模块的引用,包含子目录。
4.4 require.context
可以使用
require.context()
方法来创建自己的(模块)上下文,这个方法有 3 个参数:要搜索的文件夹目录,是否还应该搜索它的子目录,以及一个匹配文件的正则表达式。
require.context(directory, useSubdirectories = false, regExp = /^\.\//)
require.context("./test", false, /\.test\.js$/);
//(创建了)一个包含了 test 文件夹(不包含子目录)下面的、所有文件名以 `.test.js` 结尾的、能被 require 请求到的文件的上下文。
require.context("../", true, /\.stories\.js$/);
(创建了)一个包含了父级文件夹(包含子目录)下面,所有文件名以 `.stories.js` 结尾的文件的上下文。
require.context模块导出(返回)一个(require)函数,这个函数可以接收一个参数:request 函数–这里的 request 应该是指在 require() 语句中的表达式 require.context 第一个参数不能是变量,webpack在编译阶段无法定位目录 导出的方法有 3 个属性: resolve, keys, id。 resolve 是一个函数,它返回请求被解析后得到的模块 id。 keys 也是一个函数,它返回一个数组,由所有可能被上下文模块处理的请求组成。 id 是上下文模块里面所包含的模块 id. 它可能在你使用 module.hot.accept 的时候被用到
5 主文件引入
在main.js
中添加import '@/icons'
即可。 这样,就读取了icons
目录下的index.js
文件,即全局引入了IconSvg
组件。
6 测试
在main.vue
中添加<icon-svg name="admin"></icon-svg>
运行,看到已经能够正常显示图标了。
7 具体方式
-把文件名是icon-*.svg
的svg
文件,放入到/src/icons/svg
目录下 -在使用的地方(vue文件中)使用icon-svg
标签 -给该标签添加name
属性,属性值为svg
文件名中是icon-*.svg
的*
一样,就可以显示该图标了
8 还可以优化?
参考文献
https://www.zhangxinxu.com/wordpress/2014/07/introduce-svg-sprite-technology/ https://juejin.im/post/59bb864b5188257e7a427c09
https://blog.csdn.net/cauyahui/article/details/79178334