Appearance
带你用vue写后台系列(封装Svg组件SvgIcon-挖坑并填埋)
1、组件目录搭建
接下来我们学习一下如何封装一个独立的组件,并且在这个过程之中我们会挖一些新人常见的报错坑并进行填埋
在src文件夹下新建一个components目录,这里我们用来存放公共的组件
JS
+ Svg组件存放的地址 components=> SvgIcon=> index.vue
+ Svg存放的地址
`D:\LTB\code\NexusV2\src\assets\icons\svg`
这里我已经在对应的文件夹下面放了一些svg图片,当然,也可以自己去iconfont里面下载,下载以后我们就可以自己进行使用
2、思路:
1、封装一个Svgicon组件,全局注册,我们使用的时候,只需要使用上面的名字就可以实现文件夹下icon的使用
2、可以对于icon的颜色和大小进行设置
3、搭建Svgicon组件的页面和传递的参数
4、引入和使用Svgicon组件
3、搭建Svgicon组件
接下来我们就正式搭建Svgicon组件
我们先来一个简单一些的,就是直接使用v-html的方式直接把svgIcon的内容给引入进来
js
<template>
<span v-html="svgContent"></span>
</template>
<script>
export default {
name: 'SvgIcon',
props: {
icon: {
type: String,
required: true
}
},
computed: {
svgContent() {
return require(`@/assets/icons/svg/xxx.svg`); // 或者直接使用字符串xxx就是 this.icon
}
}
};
</script>
<style scoped>
/* 可选:为 SVG 添加样式 */
span svg {
width: 24px;
height: 24px;
fill: currentColor;
}
</style>
先在局部的组件内部使用一下看看
- 引入和使用
接下来我们就引入和使用这个404试试
这里组件使用我们直接上代码
js
<template>
<h3>组件列表</h3>
<div>
<SvgIcon icon="404"></SvgIcon>
</div>
</template>
<script>
import {SvgIcon} from '@/components/SvgIcon';
export default{
name: 'components',
components: {},
mounted(){},
data() {},
methods:{},
}
</script>
<style scoped></style>
4、挖坑填埋
坑 vue2 只有一个template根
这边报错,差点忘了vue2 只有一个 template 根的原则 所以外头套个div
保存以后进行查看
坑 不写组件名字
报错: Unknown custom element: <SvgIcon>
- did you register the component correctly? For recursive components, make sure to provide the "name" option.`
这是因为我们引入组件必须在组件之中写组件名字
JS
进行这么更改
components: {}, =》 components: {SvgIcon},
在这个过程之中其实学习报错也是非常有必要的,上面不想看的也可以直接贴下面的代码,主要上面针对新人学习的
坑 不写data返回
报错的含义就是,你没写data返回 这里我们的data不能写成空的,所以这里我们直接写个返回必须
JS
data() {
return {
};
},
报错解决
坑 未传递参数
这里是因为我们并没有给组件传递应该有的名字
更改一下
JS
<SvgIcon icon="404"></SvgIcon>
坑 组件写法问题
这里可以看到我们渲染成功了,但是只是一个路径是怎么回事呢?
回头看一下我们的svgIcon组件内部,我们使用名字是直接使用的,是不是我们传递参数地址错误了呢?直接改成地址参数试试
JS
SvgIcon
return require(xxx); // 或者直接使用字符串:this.icon
改成下面这个部分
return require(`@/assets/icons/svg/404.svg`);
预览一下我们可以看出来,根本不是我们外部传递参数的问题,而是我们本身内部就有问题
也就是说我们不能使用v-html,这里我们改成svg的方式,这里svg的使用具体可以查看这篇svg文章,之前看的感觉蛮不错的!也可以看看Element的组件这部分源码!
接下来我们就直接使用一下看看,看如何做一个自己的SvgIcon`组件
5、SvgIcon组件插件svg-sprite-loader
在 Vue2项目中,我们一般会使用使用 svg-sprite-loader` 来处理 SVG 图标并生成 SVG 雪碧图,它可以帮助我们将多个 SVG 图标打包成一个雪碧图,在 Vue 组件中以符号的方式引用,减少 HTTP 请求和优化性能。
JS
在组件之中直接写一下我们的代码,这时候没出来是缺少了svg的渲染组件
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="100" height="100">
<use xlink:href="https://res.lgdsunday.club/user.svg" />
</svg>
这个时候就需要引入一下我们的svg组件(针对webopack的)
JS
"svg-sprite-loader": "5.1.1",
安装插件
JS
npm install svg-sprite-loader@5.1.1
配置 webpack
在 vue.config.js` 文件中进行配置
主要是针对svg进行配置
JS
// SVG部分的规则
chainWebpack(config){
// 设置 svg-sprite-loader
config.module
.rule('svg')
.exclude.add(resolve('src/assets/icons'))
.end()
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(resolve('src/assets/icons'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]'
})
.end()
}
完整部分就是如下:
JS
const { defineConfig } = require('@vue/cli-service');
const path = require('path')
function resolve(dir) {
return path.join(__dirname, dir)
}
module.exports = defineConfig({
transpileDependencies: true,
// 是否开启eslint保存检测,有效值:ture | false | 'error'
lintOnSave: false,
// SVG部分的规则
chainWebpack(config){
// 设置 svg-sprite-loader
config.module
.rule('svg')
.exclude.add(resolve('src/assets/icons'))
.end()
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(resolve('src/assets/icons'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]'
})
.end()
}
})
全局注册所有组件
当我们的组件十分多的时候,这个时候我们几百上千个总不可能一个一个去注册吧,这个时候我们就会去自动全几乎注册某个文件夹下面的组件
在在assets/icons/index.js中全局注册组件,完成组件的注册,无需自己一个一个手动引入
JS
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'// svg component // register globally
Vue.component('svg-icon', SvgIcon)
const req = require.context('./svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys().map(requireContext)
requireAll(req)
对components/SvgIon中组件进行封装 封装一些我们常用的组件参数,通过iconClass 参数来控制组件显示哪一个
JS
<template>
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
<use :xlink:href="iconName" />
</svg>
</template>
<script>
export default {
name: 'SvgIcon',
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String,
default: ''
}
},
computed: {
isExternal() {
return /^(https?:|mailto:|tel:)/.test(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-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>
引入
这里记得要在我们的main.js之中进行引入啊,不引入如何使用呢
JS
import './assets/icons' // icon //引入icon
使用svg-icon组件
这里我们之前进行了全局注册,所以使用起来异常的简单
js
<svg-icon class-name="aisc" icon-class="aisc"/>
最后来看一下我们最终的效果:
ok! 这就顺利完成了svg-icon组件的封装!
6、展示项目下面所有的icons
接下来我们在pages下面写一个icons,这个部分用于展示我们的所有项目里面的icon
项目结构如下:
- svg-icons.js
其实就是导入,匹配,然后导出所有的
js
const req = require.context('@/assets/icons/svg', false, /\.svg$/)
const requireAll = requireContext => requireContext.keys()
const re = /\.\/(.*)\.svg/
const svgIcons = requireAll(req).map(i => {
return i.match(re)[1]
})
export default svgIcons
js
<template>
<div class="icons-container">
<div v-for="item of svgIcons" :key="item" class="icon-li">
<div class="icon-item left">
<svg-icon :icon-class="item" class-name="disabled" />
<div class="span">{{ item }}</div>
</div>
<div class="useicon right">
{{ generateIconCode(item) }}
</div>
</div>
</div>
</template>
<script>
import svgIcons from './svg-icons'
export default {
name: 'Icons',
data() {
return {
svgIcons,
}
},
methods: {
generateIconCode(symbol) {
return `<svg-icon icon-class="${symbol}" />`
},
}
}
</script>
<style lang="css">
.icons-container {
margin: 10px 20px 0;
overflow: hidden;
display: flex;
flex-flow:wrap;
justify-content: flex-start;
}
.icons-container .icon-li{
flex: 1;
margin: 20px;
padding: 20px;
text-align: center;
width: 400px;
height: 100px;
font-size: 30px;
color: #24292e;
cursor: pointer;
border: 1px solid #fff;
padding: 10px;
border-radius: 10px;
display: flex;
background: cadetblue;
}
.icons-container .left{
flex: 0.4;
background: #fff;
border-radius: 10px;
padding-top: 10px;
}
.icons-container .span{
font-size: 13px;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 0 10px;
}
.icons-container .icon-item .disabled {
pointer-events: none;
}
.icons-container .useicon{
flex: 0.6;
color: #fff;
font-size: 14px;
text-align: left;
padding: 10px;
font-weight: bold;
/*background: rgba(0,0,0,0.5);
background: rgba(41,91,199,0.5);*/
font-size: 13px;
}
</style>
最后我们预览一下 成功!