在项目中优雅的使用svg-icon

530 阅读3分钟

背景

在项目中我们通常会使用很多icon图标,一般有使用pngsvg或者css来实现图标,他们各自有自己的优缺点

  • png:受控于颜色、大小,一个icon的颜色或者大小改变了,则需要重新制作,在高清屏下,图片放大会失真,且每个图片都是一个文件,占用请求资源(当然这点可以利用雪碧图来解决,但是当合成为一张大图片的时候,可能会出现加载比较慢)
  • css:主要是书写比较复杂,为实现一个图标有时候可能会书写十几行的css代码,更甚者几十行都有
  • svg:它不受控于颜色、大小,可以任意改变颜色、大小,且不失真,但是缺点是使用起来比较麻烦 本文就针对于svg的使用做优化,本文实现Icon组件库是基于vue2.xwebpack4.x构建的。

准备工作

安装vuewebpack或者直接使用vue-cli3.x来创建一个vue的项目,为了方便,我就使用vue-cli来创建一个vue项目

vue create my-app
cd my-app
npm run dev

这样我们先创建了一个项目,然后需要对项目做以下调整:

  • 1、创建存放svg的目录,在src 下面新建一个asstes/icon的目录,西面存放所有需要用到的svg,
  • 2、在项目根路径下新建vue的配置文件vue.config.js
  • 3、在components目录下新建SvgIcon.vue文件,它是一个SvgIcon组件,存储我们需要用到的svg
  • 4、在src目录下新建utils文件夹,在其下面新建index.js,这里是项目中常用的工具函数
// 只列出主要目录结构
my-app
    │  .eslintrc
    │  .gitignore
    │  babel.config.js
    │  package-lock.json
    │  package.jsonREADME.md
    │  vue.config.js  // 1、新增配置
    ├─public
    │      favicon.ico
    │      index.html
    └─src
        │  App.vue
        │  main.js
        ├─assets
        │  └─icon	// 2、存放svg-icon
        │          add.svg
        │          search.svg
        │          user.svg
        ├─components
        │      SvgIcon.vue // 3、svg-icon组件
        ├─utils
        │      index.js  // 4、工具方法
        └─views
                About.vue
                Home.vue

开始处理

  • 1、首先配置webpack的rules,因为vue-cli有默认的webpack配置,我们需要修改默认的配置,vue.config.js是vue-cli提供的一个可选配置文件,同时也会介绍webpack的三种配置方式,如下:
// vue.config.js
const path = require('path')
const resolve = dir => path.join(__dirname, dir)
module.exports = { 
    lintOnSave: process.env.NODE_ENV !== 'development',
    // 第一种:对象模式
    // configureWebpack: {
    //     resolve: {
    //         alias: {
    //             comps: resolve('/src/components')
    //         }
    //     }
    // },
    // 第二种:函数模式
    // configureWebpack: (config) => {
    //     config.resolve.alias.comps = resolve('/src/components')
    //     if (process.env.NODE_ENV === 'development') {
    //         console.log('dev---')
    //         config.name = '开发环境'
    //     } else {
    //         config.name = '正式环境'
    //     }
    // },
    // 第三种:因为vue-cli默认安装了chainwebpack第三方插件,可以使用chainwebpack配置,它可以实现链式调用
    chainWebpack(config) {
    	// 给 components 起一个别名,若需要使用 components 下的组件可以使用 import xxx from '/comps' 就可以引入 components 下面的组件
        config.resolve.alias.set('comps', resolve('/src/components'))
        // 因为vue-cli默认配置处理了svg,所以此处,需要找到svg对应的规则,让默认的svg规则排除对 src/assets/icon 这个目录下svg的处理
        config.module.rule('svg')
            .exclude.add(resolve('src/assets/icon'))
        // 对 src/assets/icon 这个目录下的svg使用我们自己写iocns规则
        config.module.rule('icons')
            .test(/\.svg$/)
            .include.add(resolve('src/assets/icon')).end()
            .use('svg-sprite-loader')
            .loader('svg-sprite-loader')
            .options({
                symbolId: 'icon-[name]' // #icon-user.svg
            })
    }
}
  • 2、SvgIcon.vue 组件放置我们所有svg内容 (通常使用从iconfont复制的svg内容)如下:
// SvgIcon.vue
<template>
    <svg :class="svgClass" v-on="$listeners"> 
        <use :xlink:href="fullIconName"></use>
    </svg>
</template>

<script>
export default {
    name: 'SvgIcon',
    props: {
        iconName: {
            type: String,
            require: true
        },
        className: {
            type: String,
            require: true
        }
    },
    computed: {
        fullIconName () {
            return `#icon-${this.iconName}`
        },
        svgClass () {
            return this.className ? `svg-icon ${this.className}` : 'svg-icon'
        }
    }
}
</script>

<style lang="postcss">
.svg-icon {
    width: 1em;
    height: 1em;
    vertical-align: middle;
    fill: currentColor;
    overflow: hidden;
}
</style>
  • 3、现在有了svg-icon组件,想要使用的话,我们在引入这个组件,但是,如果多个页面要使用的话,每个页面都要引入这个组件,这样的话很麻烦,所以只要把这个组件注册为全局组件就不用每个文件都引入了,于是,在utils/index.js中编写注册全局组件的代码,如下:
// main.js
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon.vue'
// 1、自动加载icon/*.svg文件
// 获取一个指定上下文的requie函数
const req = require.context('../assets/icon', false, /\.svg$/)
req.keys().map(req)
// 2、祖册全局的Icon组件
Vue.component('svg-icon', SvgIcon)
  • 4、在main.js中引入公共方法
import Vue from "vue";
import App from "./App.vue";
import "@/utils" // 引入项目中的公共方法

Vue.config.productionTip = false;

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount("#app");

  • 5、使用svg-icon组件,我们就再App.vue中试一下
// App.vue
<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">
        <!-- 之前使用svg的方式
        <svg>
          <use xlink:href="#icon-user"></use>
        </svg> 
        -->
        <!-- 现在使用svg的方式 -->
        <svg-icon icon-name='user'></svg-icon>
        Home
      </router-link> |
      <router-link to="/about">About</router-link>
    </div>
    <router-view />
  </div>
</template>