美文网首页web前端
在vue项目中使用 SVG Sprites

在vue项目中使用 SVG Sprites

作者: 雨翼195 | 来源:发表于2020-11-27 12:28 被阅读0次

    前言

    网页图标展示方式大概可以分为以下几类

    前端工程化 - 工程配置 vue-cli3+

    在了解 SVG Sprites 技术之后,引出本文的主角:svg-sprite-loader 这是关键

    1、雏形

    vue-cli 内置的webpack配置会将svg文件使用 file-loader 进行加载,这不符合我们当前的需求

    • 解决方案:固定目录下的svg文件使用 svg-srpite-loader 进行加载,其它则继续使用 file-loader,目前网上搜索到的解决方案基本如此
    // vue.config.js
    const path = require('path');
    function Resolve(dir) {
      return path.join(__dirname, dir);
    }
    module.exports = {
      chainWebpack: config => {
        config.module.rule('svg').exclude.add(Resolve('src/assets/icons')); //让file-loader排除掉这个目录
        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()
          //使用svgo-loader 进行优化,去除svg本身的填充色
          .use('svgo-loader').loader('svgo-loader').options({
            plugins: [
              {removeAttrs: {attrs: 'path:fill'}},
              { removeXMLNS: true }
            ]
          })
      }
    }
    
    // main.vue
    <template>
      <svg>
        <use :xlink:href="'#'+iconId"></use>
      </svg>
    </template>
    <script>
      import MySvg '@/assets/icons/logo.svg';  // src/assets/icons/logo.svg
      export default {
        data() {
          return {
            iconId: MySvg.default.id
          }
        }
      }
    </script>
    

    这样做有一个问题,如果图标是页面专属,其它页面不会用到,把svg都放在同一个目录下,没有跟随组件目录,那这样就非常不好管理。

    2、优化

    vue-cli的官方文档中看到这么一段话:

    2020-11-27_114659.jpg

    不再局限于 src/assets/icons 目录下,而是判断文件名以 .icon.svg 结尾:

    // vue.config.js
    const path = require('path');
    function Resolve(dir) {
      return path.join(__dirname, dir);
    }
    module.exports = {
      chainWebpack: config => {
        //svg sprites
        const svgRule = config.module.rule('svg')
        svgRule.uses.clear()
        svgRule.oneOf('icon').test(/\.icon\.svg$/)
          .use('svg-sprite-loader')
          .loader('svg-sprite-loader')
          .options({
            symbolId: 'icon_[hash:base64:5]',
          }).end()
          .use('svgo-loader').loader('svgo-loader').options({
            plugins: [
              {removeAttrs: {attrs: 'path:fill'}},
              { removeXMLNS: true }
            ]
          }).end()
    
        //普通svg图片
        svgRule.oneOf('img').test(/\.svg$/)
          .use('file-loader')
          .loader('file-loader').options({
            name: 'img/[name].[hash:8].[ext]'
          })
      }
    }
    

    3、和 importrequire 说再见吧

    经过上面的优化后,在svg文件引入的方式上还是有些不尽人意,有没有像 img 标签那样舒服的使用方式呢?当然有!首先看看在基于vue-cli脚手架的项目中是怎么实现:
    处理静态资源

    2020-11-27_120854.jpg
    当然,这需要vue-loader处理,详细文档
    修改vue-loader后的最终配置:
    const path = require('path');
    function Resolve(dir) {
      return path.join(__dirname, dir);
    }
    module.exports = {
      chainWebpack: config => {
        //svg sprites
        const svgRule = config.module.rule('svg')
        svgRule.uses.clear()
        svgRule.oneOf('icon').test(/\.icon\.svg$/)
          .use('svg-sprite-loader')
          .loader('svg-sprite-loader')
          .options({
            symbolId: 'icon_[hash:base64:5]',
          }).end()
          .use('svgo-loader').loader('svgo-loader').options({
            plugins: [
              {removeAttrs: {attrs: 'path:fill'}},
              { removeXMLNS: true }
            ]
          }).end()
    
        //普通svg图片
        svgRule.oneOf('img').test(/\.svg$/)
          .use('file-loader')
          .loader('file-loader').options({
            name: 'img/[name].[hash:8].[ext]'
          })
    
        //vue-loader
        config.module
        .rule('vue')
        .use('vue-loader')
          .loader('vue-loader')
          .tap(options => {
            // 修改它的选项...
            options.transformAssetUrls = {
              video: ['src', 'poster'],
              source: 'src',
              img: 'src',
              image: ['xlink:href', 'href'],
              use: ['xlink:href', 'href'],
              'vc-svg-icon': 'src'  //这一项是关键,下面的组件封装有用到
            }
            return options
          })
      }
    }
    

    上面的 options.transformAssetUrls['vc-svg-icon'] = 'src',让vue-loader识别vc-svg-icon组件的src属性,并对其解析为一个 模块依赖

    4、组件封装

    <template>
      <svg class="vc-svg-icon" aria-hidden="true" :icon-href="href">
        <use :xlink:href="href"></use>
      </svg>
    </template>
    
    <script>
      export default {
        name: 'SvgIcon',
        props: {
          src: module
        },
        computed: {
          href() {
            return `#${this.src.default.id}`
          }
        },
        mounted() {
          //初始化时查找是否存在该 symbol
          const container = document.querySelector('#__SVG_SPRITE_NODE__')
          if (!container.querySelector(this.href)) {
            container.insertAdjacentHTML('beforeend', this.src.default.content)
          }
        },
        destroyed() {
          //组件销毁后查找是否存在无用的 symbol,没有被vc-svg-icon组件引用则移除对应的symbol
          const hasIcon = document.querySelector(`[icon-href="${this.href}"]`)
          const symbol = document.querySelector('#__SVG_SPRITE_NODE__').querySelector(this.href)
          if (!hasIcon && symbol) {
            symbol.remove()
          }
        }
      }
    </script>
    <style>
      .vc-svg-icon {
        width: 1em;
        height: 1em;
        vertical-align: middle;
        fill: currentColor;
        overflow: hidden;
      }
    </style>
    

    mounteddestroyed 生命周期里的处理是为了“用完即销毁”,可自行斟酌,非必要,因为symbol标签的父级svg#__SVG_SPRITE_NODE__是隐藏的(display:none),大量存在无用的symbol标签也不会造成性能问题

    5、使用

    //*.vue
    <template>
    <div>
      <!-- 一般使用 -->
       <vc-svg-icon src="./***/***.icon.svg"></vc-svg-icon>
      <!-- src 值在 data 中 -->
      <vc-svg-icon :src="myIcon"></vc-svg-icon>
    </div>
    </template>
    <script>
      export default {
        data() {
          return {
            myIcon: require('./***/***.icon.svg')
          }
        }
      }
    </script>
    

    相关文章

      网友评论

        本文标题:在vue项目中使用 SVG Sprites

        本文链接:https://www.haomeiwen.com/subject/hqtxwktx.html