svg-sprite

前端icon的演进史

  • img图片

使用图片浪费资源

  • sprite雪碧图

虽然只加载一张图片,但是后期修改添加icon时,容易对前边已有的icon坐标造成误操作,维护困难

  • Font库 Font Awsome

找起来很费劲,不能满足所有你需要的图标。适用于没有UI追求的初创公司。

  • iconfont

种类/数量多,有几百个公司的开源库,还支持自定义创建图表库,能满足所有需求。

iconfont使用方法

    • unicode

优点:兼容性好 支持IE6+浏览器,font-size、color直接控制大小颜色
缺点:语义性差,因为是字体所以无法控制多色图标颜色(遇多色图标自动去色)

1. 复制项目下生成的font-face(或直接用远程文件)
@font-face {
    font-family: 'iconfont';
    /* project id 1379818 */
    src: url('https://at.alicdn.com/t/font_1379818_eptqk5354g7.eot');
    src: url('https://at.alicdn.com/t/font_1379818_eptqk5354g7.eot?#iefix') format('embedded-opentype'), 
    url('https://at.alicdn.com/t/font_1379818_eptqk5354g7.woff2') format('woff2'), 
    url('https://at.alicdn.com/t/font_1379818_eptqk5354g7.woff') format('woff'), 
    url('https://at.alicdn.com/t/font_1379818_eptqk5354g7.ttf') format('truetype'), 
    url('https://at.alicdn.com/t/font_1379818_eptqk5354g7.svg#iconfont') format('svg');
}

2. 定义要使用的iconfont样式
.iconfont {
    font-family: "iconfont"!important;
    font-size: 16px;
    font-style: normal;
    -webkit-font-smoothing: antialiased;
    -webkit-text-stroke-width: 0.2px;
    -moz-osx-font-smoothing: grayscale;
}

3. html中使用字体编码
<i class="iconfont">&#xe745;</i>
    • font-class

优点:语义性强,图标类别一目了然。兼容性良好支持IE8+,及现代浏览器

缺点:还是字体无法控制多色图标颜色

1. 复制项目下生成的css文件并引入
<link rel="stylesheet" href="https://at.alicdn.com/t/font_1379818_ikubw1ig6m.css">

2. 按类名使用 //iconfotn:固定类名
<i class="iconfont icon-icon11"></i>
    • symbol

优点:可以像字体那样控制font-size、color直接控制大小颜色。支持多色图标 更有色彩。矢量 不失真减少http请求

缺点:兼容性差 支持IE9+,及现代浏览器。浏览器渲染svg的性能一般,不如png。

1. 引用项目下载下来的iconfont.js文件,或者远程js //at.alicdn.com/t/font_1379818_v3d94rjzo4.js
<script src="iconfont.js"></script>

2. 加入通用css样式
.icon {
    width: 1em;
    height: 1em;
    vertical-align: -0.15em;
    fill: currentColor;
    overflow: hidden;
}

3. html中使用
<svg class="icon" aria-hidden="true">
    <use xlink:href="#icon-icon-test"></use>
</svg>

问题:现在所有的svg-sprite都是通过iconfont 的iconfont.js生成的。他是一段用js来生成的svg代码,所有图标icon很不直观,完全不知道哪个图标名对应哪个图标

  • 不能按需加载:所有图标同时生成在一个iconfont.js文件中。
  • 自定义性差:通常导出的svg包含大量的无用信息(编辑器信息、注释。。。)。
  • 添加不友善:如有自定义的图标,需要先上传到iconfont库中,和其他图标整合后,重新下载,很繁琐。

svg-sprite

svg好比舞台,symbol是舞台上一个一个组装的原件,而symbol需要使用use才生效,所以symbol+use ==> svg-sprite

<svg>
    <symbol>
        <!-- 第1个图标路径形状之类代码 -->
    </symbol>
    <symbol>
        <!-- 第2个图标路径形状之类代码 -->
    </symbol>
    <symbol>
        <!-- 第3个图标路径形状之类代码 -->
    </symbol>
</svg>

使用webpack loader的svg-sprite-loader可以将多个svg打包成svg-sprite.

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最安全的是使用webpack的exclude和include,让svg-sprite-loader只处理你指定文件夹下的svg,url-loader处理除此之外的svg
// 设置svg图片-图标处理方式  vue.config.js
config.module
  .rule('svg')
  .exclude.add(resolve('src/icons')) // 除了src/icons下的svg都是用url-loader来处理
  .end()
config.module
  .rule('icons')
  .test(/\.svg$/)
  .include.add(resolve('src/icons')) // svg-sprite-loader只处理src/icons下的svg
  .end()
  .use('svg-sprite-loader')
  .loader('svg-sprite-loader')
  .options({
    symbolId: 'icon-[name]'
  })
  .end()

// 有了这个区分,就要求我们规范的管理SVG图标,将其统一放置在src/icons目录下
自动导入生成SVG Sprite require.context,在与@/icons/svg同级目录下的index.js编写
// 使用如下代码导入项目中所有的icon
const requireAll = requireContext => requireContext.keys().map(requireContext)
//在svg文件夹下,不检索其子目录,寻找后缀名为.svg的能被require所有资源图标
const req = require.context('./svg', false, /\.svg$/)  // (1-源svg文件夹,2-是否检索下级目录,3-匹配资源正则表达式)
requireAll(req)

使用

  • 写<svg-icon>组件
  • <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 this.isExternal(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%`
          }
        }
      },
      methods: {
        // 判断是否是外部图标
        isExternal(path) {
          return /^(https?:|mailto:|tel:)/.test(path)
        }
      }
    }
    </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>
    
  • 页面使用

  • <svg-icon icon-class="eye" class-name="eyeStyle" />
    

results matching ""

    No results matching ""