Vue2封装svg-icon组件

4,411 阅读3分钟

前言

Hello,大家好!我是Atrox🚀,一个正在努力学习的前端攻城狮🦁。在我的上一篇文章svg基础知识入门中,我向大家介绍了有关svg的基础知识与语法,并且使用svg完成了两个简单的案例。

在实际的项目开发中,svg的使用也非常广泛,比如:项目中图标的展示;使用svg绘制坐标图、关系图等。其中比较经典与常见的使用便是使用svg来展示各种类型的图标了,因此,如何在vue项目中封装一个svg-icon组件呢?

下面这篇文章将带领大家从0到1完成一个svg-icon组件的实现,一起来学习吧!😎😎😎

创建svg-icon组件

如图所示,首先我们需要在src路径下的commponents文件夹下新建一个svgIcon文件夹用来创建svg-icon组件。

<template>
  <svg
    class="svg-icon"
    :style="{
      fontSize: size + 'px',
      width: width + 'px',
      height: height + 'px',
      color: color
    }"
  >
    <use :xlink:href="iconName"></use>
  </svg>
</template>

<script>
export default {
  name: 'svgIcon',
  props: {
    icon: {
      //svg图标名称
      type: String,
      required: true
    },
    color: {
      type: String,
      default: 'rgba(0,0,0,0.65)'
    },
    size: {
      //svg尺寸
      type: [Number, String]
    },
    width: {
      //svg宽度
      type: [Number, String]
    },
    height: {
      //svg高度
      type: [Number, String]
    }
  },
  data() {
    return {}
  },
  computed: {
    iconName() {
      return `#icon-${this.icon}`
    }
  }
}
</script>

<style scoped>
.svg-icon {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
</style>

在这里我们创建了一个svg元素,并且允许接收一些参数来修改对应的样式。

存储收集svg

如图所示,首先我们需要在src路径下新建一个名为的icons文件夹用来存放项目所需要的svg文件。然后在index.js文件中收集获取所有的svg文件,代码如下。

import Vue from 'vue'

import svgIcon from '@/components/svgIcon/index.vue' //引入项目中的svgIcon组件

Vue.component('svg-icon', svgIcon) // 全局注册svg-icon组件

/**
 * @name require.context
 * @description 一个 webpack 的 api ,通过该函数可以获取一个上下文,从而实现工程自动化(遍历文件夹的文件,从中获取指定文件,自动导入模块)。
 * @description 在前端工程中,如果一个文件夹中的模块需要频繁引用时可以使用该中方式一次性引入
 * @param dirname String 需要读取模块的文件的所在目录
 * @param useSubdirectories Boolean	是否遍历子目录
 * @param RegExp RegExp	匹配的规则(正则表达式)
 * @returns resolve	Function	接受一个参数request,request为文件夹下面匹配文件的相对路径,返回这个匹配文件相对于整个工程的相对路径
 * @returns keys	Function	返回一个数组,由匹配成功的文件所组成的数组
 * @returns id	String	执行环境的 id
 */
const req = require.context('./svg', false, /\.svg$/) // 遍历获取 svg 目录下所有的 svg 文件(不包括子目录)

const requireAll = (requireContext) => {
  requireContext.keys().map(requireContext)
}

requireAll(req)//返回svg文件夹下文件相对于整个工程的相对路径

解析svg文件

在创建了基础的svg-icon组件以及获得了项目中svg文件的路径之后,我们需要解析svg文件的内容,完成渲染。

这里我们需要借助第三方插件来完成内容的解析,推荐使用svg-sprite-loader

执行npm i svg-sprite-loader之后还需要在vue.config.js文件中进行相关的webpack配置来完成解析加载,具体代码如下。

const path = require('path')
function resolve(dir) {
  return path.join(__dirname, dir)
}
module.exports = {
  chainWebpack(config) {
    //设置svg
    config.module
      .rule('svg')
      .exclude.add(resolve('src/icons'))
      .end()
    config.module
      .rule('icons')
      .test(/\.svg$/)
      .include.add(resolve('src/icons'))
      .end()
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })
      .end()
  }
}

至此我们已经完成整个组件的加载与解析,现在就可以去使用啦。

使用svg-icon组件

首先需要在main.js引入处理好的svg文件

import './icons/index'

然后就可以在项目的任何地方使用svg-icon组件完成svg文件的渲染了。😁😁😁

<template>
  <div class="project-layout">
    <svg-icon icon="vue" width="64" height="64"></svg-icon>
  </div>
</template>

如下图所示:

如何获取svg文件

对于项目中的svg文件一般是ui同事提供,除此之外也可以去别的网站获取(例如iconfont),这里提供一个可以编辑svg的网址供大家使用---svg.wxeditor

如果你有自己的想法也可以自己编写一个svg文件,这里推荐一下svg基础知识入门这篇文章,可以帮助大家快速学习svg的相关知识。

精灵图

  1. 精灵图介绍

在项目开发中,除了使用svg作为图标外,也可以使用精灵图来实现完成需求。

精灵图可以叫雪碧图也叫css sprites,实质其实就是利用背景图和背景图的位置去显示同一张图上,不同位置的图片,进而,在引入一张图的前提下,显示不同的图片的技巧。它适合:一般小图标素材。

使用精灵图可以加快网页加载速度,但在图片合并的时候,你要把多张图片有序的合理的合并成一张图片,还要留好只够的空间,防止板块内不会出现不必要的背景,如果留空间或拼合位置不合适,在布局时容易出现布局这个盒子对象时,设置背景出现拼合相邻图片,干扰图片的情况,所以使用时需要注意。

  1. 创建精灵图

下面来介绍一种快速生成精灵图的方法。

首先需要安装以下插件,用来解析生成对应的文件。

"gulp": "^4.0.2",
"gulp-postcss": "^9.0.1",
"gulp.spritesmith": "^6.11.0",
"vinyl-buffer": "^1.0.1"

这里简单介绍一下gulp

gulp是前端开发过程中一种基于流的代码构建工具,是自动化项目的构建利器;它不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成;使用它,不仅可以很愉快的编写代码,而且大大提高我们的工作效率。

gulp是基于Nodejs的自动任务运行器, 它能自动化地完成 前端代码的测试、检查、合并、压缩、格式化、浏览器自动刷新、部署文件生成,并监听文件在改动后重复指定的这些步骤。

src文件夹下创建gulpfile.js,加入以下代码。

const gulp = require('gulp')
const fs = require('fs')
const path = require('path')
const postcss = require('gulp-postcss')
const buffer = require('vinyl-buffer')
const spritemith = require('gulp.spritesmith')
const spritesPath = path.join(__dirname, 'src/assets/img')

let spritesArray = []
;(function(dir) {
  let fileList = []
  fs.readdirSync(dir).forEach((name) => {
    const spritesFile = path.join(spritesPath, name)
    const stats = fs.lstatSync(spritesFile)
    if (stats.isFile() && /png$/.test(name)) {
      fileList.push(spritesFile)
    } else if (stats.isDirectory() && fs.readdirSync(spritesArray).length) {
      const gulpTask = `sprites:${name}`
      spritesArray.push(gulpTask)
      gulp.task(gulpTask, (done) => {
        let spritesData = gulp.src(path.join(spritesFile, '*.png')).pipe(
          spritemith({
            imgName: `${name}_icon.png`, //生成雪碧图的路径
            imgPath: `/img/${name}_icon.png`, //手动指定路径,会直接出现在background的属性值中
            cssName: `${name}_icon.scss`, //less生成scss文件,方便使用
            cssTemplate: (data) => {
              return cssTemplate2(data)
            }
          })
        )
        spritesData.css.pipe(gulp.dest(path.join(__dirname, 'src/assets/css')))
        spritesData.img
          .pipe(buffer())
          .pipe(gulp.dest(path.join(__dirname, 'public/img/')))
        done()
      })
    }
  })
  if (fileList.length) {
    spritesArray.push('sprites: app')
    gulp.task('sprites: app', (done) => {
      let spritesData = gulp.src(fileList).pipe(
        spritemith({
          imgName: `app_icon.png`, //生成雪碧图的路径
          imgPath: `/img/app_icon.png`, //手动指定路径,会直接出现在background的属性值中
          cssName: `app_icon.scss`, //less生成scss文件,方便使用
          cssTemplate: (data) => {
            return cssTemplate2(data)
          }
        })
      )
      spritesData.css.pipe(gulp.dest(path.join(__dirname, 'src/assets/css')))
      spritesData.img
        .pipe(buffer())
        .pipe(gulp.dest(path.join(__dirname, 'public/img/')))
      done()
    })
  }
})(spritesPath)

var cssTemplate2 = function(data) {
  let arr = []

  let url = data.spritesheet.image
  data.sprites.forEach(function(sprite) {
    arr.push(
      `.sprite-${sprite.name}{
                display:inline-block;
                vertical-align:middle;
                width:${sprite.px.width};
                height:${sprite.px.height};
                background:url("${url}") no-repeat;
                background-position:${sprite.px.offset_x} ${sprite.px.offset_y}
            }`
    )
  })
  return arr.join('\n')
}

gulp.task('css', function() {
  var processors = []
  return gulp
    .src('./src/*.css')
    .pipe(postcss(processors))
    .pipe(gulp.dest('./dest'))
})

gulp.task('default', gulp.series(spritesArray))
  1. 生成精灵图

之后我们需要在package.js中的scripts中新增一条命令"start": "gulp default"

此时在终端中输入npm run start后会自动读取src\assets\img下的图片文件

然后在src\assets\css\app_icon.scss目录下生成对应的scss文件

以及在public\img\app_icon.png目录下生成精灵图文件。

  1. 使用精灵图

精灵图使用起来也非常简单

首先需要在main.js中引入生成好的app_icon.scss的文件

import '@/assets/css/index.css'

之后就可以在项目中的任何地方采用class类名的方式来使用了。

<div class="sprite-logo"></div>

总结

这篇文章介绍了如何在项目中封装一个svg-icon组件,以及精灵图的生成使用。总的来说还是推荐使用svg文件作为项目的图标来使用,因为svg的本质是代码,加载速度、渲染速度都比图片快,而且体积小不失真都是它的优点,具体使用还是要以项目实际开发情况为准🦁。