带你用vue2写后台系列(封装Svg组件SvgIcon-挖坑并填埋)

1、组件目录搭建

接下来我们学习一下如何封装一个独立的组件,并且在这个过程之中我们会挖一些新人常见的报错坑并进行填埋

在src文件夹下新建一个components目录,这里我们用来存放公共的组件

+ Svg组件存放的地址 components=> SvgIcon=> index.vue
+ Svg存放的地址

`D:\LTB\code\NexusV2\src\assets\icons\svg`
  • 1.
  • 2.
  • 3.
  • 4.

这里我已经在对应的文件夹下面放了一些svg图片,当然,也可以自己去iconfont里面下载,下载以后我们就可以自己进行使用

2、思路:

1、封装一个Svgicon组件,全局注册,我们使用的时候,只需要使用上面的名字就可以实现文件夹下icon的使用

2、可以对于icon的颜色和大小进行设置

3、搭建Svgicon组件的页面和传递的参数

4、引入和使用Svgicon组件

3、搭建Svgicon组件

接下来我们就正式搭建Svgicon组件

我们先来一个简单一些的,就是直接使用v-html的方式直接把svgIcon的内容给引入进来

<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>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.

先在局部的组件内部使用一下看看

  • 引入和使用带你用vue2写后台系列(封装Svg组件SvgIcon-挖坑并填埋)_ico

接下来我们就引入和使用这个404试试

这里组件使用我们直接上代码

<template>
	<h3 id="h0">组件列表</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>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

4、挖坑填埋

坑 vue2 只有一个template根

这边报错,差点忘了vue2 只有一个 template 根的原则 所以外头套个div带你用vue2写后台系列(封装Svg组件SvgIcon-挖坑并填埋)_ico_02

保存以后进行查看带你用vue2写后台系列(封装Svg组件SvgIcon-挖坑并填埋)_封装_03

坑 不写组件名字

报错: Unknown custom element: <SvgIcon> - did you register the component correctly? For recursive components, make sure to provide the “name” option.`带你用vue2写后台系列(封装Svg组件SvgIcon-挖坑并填埋)_SVG_04

这是因为我们引入组件必须在组件之中写组件名字

进行这么更改components: {},  =》 components: {SvgIcon},
  • 1.
  • 2.

在这个过程之中其实学习报错也是非常有必要的,上面不想看的也可以直接贴下面的代码,主要上面针对新人学习的

坑 不写data返回

带你用vue2写后台系列(封装Svg组件SvgIcon-挖坑并填埋)_SVG_05

报错的含义就是,你没写data返回 这里我们的data不能写成空的,所以这里我们直接写个返回必须

data() {
    return {

    };
},
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

报错解决

坑 未传递参数

带你用vue2写后台系列(封装Svg组件SvgIcon-挖坑并填埋)_ico_06

这里是因为我们并没有给组件传递应该有的名字

更改一下

<SvgIcon icon="404"></SvgIcon>
  • 1.
坑 组件写法问题

这里可以看到我们渲染成功了,但是只是一个路径是怎么回事呢?

回头看一下我们的svgIcon组件内部,我们使用名字是直接使用的,是不是我们传递参数地址错误了呢?直接改成地址参数试试

SvgIconreturn require(xxx); // 或者直接使用字符串:this.icon

改成下面这个部分
return require(`@/assets/icons/svg/404.svg`);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

预览一下我们可以看出来,根本不是我们外部传递参数的问题,而是我们本身内部就有问题

也就是说我们不能使用v-html,这里我们改成svg的方式,这里svg的使用具体可以查看这篇svg文章,之前看的感觉蛮不错的!也可以看看Element的组件这部分源码!

接下来我们就直接使用一下看看,看如何做一个自己的SvgIcon`组件

5、SvgIcon组件插件svg-sprite-loader

在 Vue2项目中,我们一般会使用使用 svg-sprite-loader` 来处理 SVG 图标并生成 SVG 雪碧图,它可以帮助我们将多个 SVG 图标打包成一个雪碧图,在 Vue 组件中以符号的方式引用,减少 HTTP 请求和优化性能。

在组件之中直接写一下我们的代码,这时候没出来是缺少了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>
  • 1.
  • 2.
  • 3.
  • 4.

这个时候就需要引入一下我们的svg组件(针对webopack的)

"svg-sprite-loader": "5.1.1",
  • 1.
安装插件
npm install svg-sprite-loader@5.1.1
  • 1.
配置 webpack

在 vue.config.js` 文件中进行配置

主要是针对svg进行配置

// 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()
  }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

完整部分就是如下:

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()
  }
})
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
全局注册所有组件

当我们的组件十分多的时候,这个时候我们几百上千个总不可能一个一个去注册吧,这个时候我们就会去自动全几乎注册某个文件夹下面的组件

在assets/icons/index.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)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

components/SvgIon中组件进行封装 封装一些我们常用的组件参数,通过iconClass 参数来控制组件显示哪一个

<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>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
引入

这里记得要在我们的main.js之中进行引入啊,不引入如何使用呢

import './assets/icons' // icon //引入icon
  • 1.
使用svg-icon组件

这里我们之前进行了全局注册,所以使用起来异常的简单

<svg-icon class-name="aisc" icon-class="aisc"/>
  • 1.

最后来看一下我们最终的效果:带你用vue2写后台系列(封装Svg组件SvgIcon-挖坑并填埋)_封装_07

ok! 这就顺利完成了svg-icon组件的封装!

6、展示项目下面所有的icons

接下来我们在pages下面写一个icons,这个部分用于展示我们的所有项目里面的icon带你用vue2写后台系列(封装Svg组件SvgIcon-挖坑并填埋)_封装_08

项目结构如下:

  • svg-icons.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
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
<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>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.

最后我们预览一下
成功!