MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Vue中SVG图标的使用与优化

2022-01-236.1k 阅读

Vue 中 SVG 图标基础使用

在 Vue 项目里使用 SVG 图标,第一步是引入 SVG 文件。有多种方式可以实现这一目的,这里先介绍使用 import 关键字直接导入 SVG 文件。

直接导入 SVG 文件

假设我们有一个名为 icon.svg 的文件,存放在项目的 src/icons 目录下。在 Vue 组件中,可以这样导入:

<template>
  <div>
    <svg width="24" height="24">
      <use :xlink:href="iconUrl"></use>
    </svg>
  </div>
</template>

<script>
import icon from '@/icons/icon.svg';

export default {
  data() {
    return {
      iconUrl: `${icon}#icon`
    };
  }
};
</script>

在上述代码中,通过 import 语句将 SVG 文件引入。然后在 data 函数中定义 iconUrl,它由导入的 SVG 文件路径和 SVG 内部定义的 id(这里假设为 icon)组成。在模板中,通过 <svg> 标签结合 <use> 标签来展示 SVG 图标,xlink:href 属性指向 iconUrl

使用 vue - svg - loader

vue - svg - loader 是一个专门用于在 Vue 项目中处理 SVG 的加载器。首先要安装它:

npm install vue - svg - loader --save - dev

安装完成后,在 webpack 配置文件(通常是 webpack.base.conf.js)中进行如下配置:

const path = require('path');

module.exports = {
  module: {
    rules: [
      {
        test: /\.svg$/,
        loader: 'vue - svg - loader',
        include: [path.resolve(__dirname, '../src/icons')],
        options: {
          // 可以在这里进行更多配置
        }
      }
    ]
  }
};

配置完成后,就可以像使用普通 Vue 组件一样使用 SVG 图标。假设我们有一个 user.svg 文件,在 src/icons 目录下:

<template>
  <div>
    <UserIcon />
  </div>
</template>

<script>
import UserIcon from '@/icons/user.svg';

export default {
  components: {
    UserIcon
  }
};
</script>

这样,user.svg 就被当作一个 Vue 组件使用,使得代码更加简洁直观。

自定义 SVG 图标组件

虽然直接导入和使用 vue - svg - loader 已经能够满足基本需求,但为了更好的复用和管理 SVG 图标,我们可以自定义一个 SVG 图标组件。

创建基础 SVG 图标组件

src/components 目录下创建 SvgIcon.vue 文件:

<template>
  <svg :class="svgClass" :width="width" :height="height" aria - hidden="true">
    <use :xlink:href="`#${iconName}`"></use>
  </svg>
</template>

<script>
export default {
  name: 'SvgIcon',
  props: {
    iconName: {
      type: String,
      required: true
    },
    width: {
      type: [String, Number],
      default: '1em'
    },
    height: {
      type: [String, Number],
      default: '1em'
    },
    svgClass: {
      type: String,
      default: ''
    }
  }
};
</script>

在这个组件中,通过 props 接收 iconNamewidthheightsvgClassiconName 用于指定要显示的 SVG 图标名称,widthheight 控制图标大小,svgClass 用于添加额外的 CSS 类。<use> 标签通过 xlink:href 属性指向相应的 SVG 图标 id

全局注册 SVG 图标组件

为了在整个项目中方便使用 SvgIcon 组件,可以将其全局注册。在 main.js 文件中:

import Vue from 'vue';
import SvgIcon from '@/components/SvgIcon.vue';

Vue.component('SvgIcon', SvgIcon);

这样,在任何 Vue 组件的模板中都可以直接使用 <SvgIcon> 标签。例如:

<template>
  <div>
    <SvgIcon iconName="user" width="24" height="24" svgClass="icon - user" />
  </div>
</template>

SVG 图标优化策略

在实际项目中,随着 SVG 图标的增多,可能会出现性能问题。以下是一些优化 SVG 图标的策略。

减少 SVG 文件大小

  1. 删除无用元素:很多 SVG 文件在生成过程中会包含一些无用的元素,如编辑器的元数据、注释等。可以使用工具如 svgo 来去除这些无用信息。首先安装 svgo
npm install svgo --save - dev

然后在项目根目录下创建 svgo.config.js 文件:

module.exports = {
  plugins: [
    {
      removeDoctype: true
    },
    {
      removeXMLProcInst: true
    },
    {
      removeComments: true
    },
    {
      removeMetadata: true
    },
    {
      removeTitle: true
    },
    {
      removeDesc: true
    },
    {
      removeUselessDefs: true
    },
    {
      removeEditorsNSData: true
    },
    {
      removeEmptyAttrs: true
    },
    {
      removeHiddenElems: true
    },
    {
      removeEmptyText: true
    },
    {
      removeEmptyContainers: true
    },
    {
      removeViewBox: false
    }
  ]
};

之后,可以在 package.jsonscripts 中添加命令:

{
  "scripts": {
    "svgo": "svgo - f src/icons"
  }
}

这样,运行 npm run svgo 就可以优化 src/icons 目录下的所有 SVG 文件。

  1. 简化路径:复杂的 SVG 路径可能会导致文件大小增加。可以使用工具将复杂路径简化。例如,PathOptimizer 工具可以对 SVG 路径进行优化,减少路径中的控制点数量,从而降低文件大小,同时不影响视觉效果。

合并 SVG 文件

将多个 SVG 图标合并成一个文件,可以减少 HTTP 请求数量,提高页面加载性能。

  1. 使用 Symbol Sprites:Symbol Sprites 是一种将多个 SVG 图标合并到一个 SVG 文件中的技术。首先,创建一个 symbol.svg 文件,将所有 SVG 图标的 <symbol> 标签放入其中。例如:
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
  <symbol id="user" viewBox="0 0 24 24">
    <!-- 用户图标路径定义 -->
  </symbol>
  <symbol id="home" viewBox="0 0 24 24">
    <!-- 家图标路径定义 -->
  </symbol>
</svg>

然后,在 Vue 组件中使用这些合并后的图标:

<template>
  <div>
    <svg width="24" height="24">
      <use :xlink:href="`symbol.svg#user`"></use>
    </svg>
    <svg width="24" height="24">
      <use :xlink:href="`symbol.svg#home`"></use>
    </svg>
  </div>
</template>
  1. Webpack 插件实现合并:可以使用 svg - spritemap - webpack - plugin 插件来自动合并 SVG 文件。首先安装插件:
npm install svg - spritemap - webpack - plugin --save - dev

然后在 webpack 配置文件(如 webpack.base.conf.js)中添加如下配置:

const path = require('path');
const SvgSpritemapPlugin = require('svg - spritemap - webpack - plugin');

module.exports = {
  module: {
    rules: [
      // 其他规则...
    ]
  },
  plugins: [
    new SvgSpritemapPlugin({
      src: path.resolve(__dirname, '../src/icons/*.svg'),
      target: path.join(__dirname, '../static/icons/symbol.svg'),
      publicPath: '/static/icons/',
      generate: {
        title: false
      }
    })
  ]
};

配置完成后,运行 webpack 构建,会在指定目录生成合并后的 symbol.svg 文件。在 Vue 组件中可以像上述使用 Symbol Sprites 的方式一样使用合并后的图标。

优化 SVG 渲染性能

  1. 减少 DOM 操作:频繁操作 SVG 元素的 DOM 会影响性能。尽量在初始化时设置好 SVG 的属性,避免在运行时频繁修改。例如,如果需要改变 SVG 图标的颜色,最好通过 CSS 来实现,而不是直接操作 SVG 内部元素的 fill 属性。
.icon - user {
  fill: red;
}
  1. 使用 CSS Transitions 和 Animations:当需要对 SVG 图标进行动画效果时,优先使用 CSS 的过渡(transitions)和动画(animations)。例如,实现一个 SVG 图标在悬停时的淡入效果:
.icon - user {
  opacity: 0.8;
  transition: opacity 0.3s ease - in - out;
}

.icon - user:hover {
  opacity: 1;
}
  1. 硬件加速:对于一些复杂的 SVG 动画,可以通过 transform: translateZ(0)transform: translateX(0) 等属性来启用硬件加速,提升动画的流畅度。例如:
.icon - animated {
  transform: translateZ(0);
  animation: rotate 5s infinite linear;
}

@keyframes rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

响应式 SVG 图标

在现代网页设计中,响应式设计至关重要。SVG 图标天然具有可缩放性,但为了更好地适应不同屏幕尺寸和设备,还需要一些额外的处理。

使用百分比单位

在定义 SVG 图标大小时,使用百分比单位可以使其根据父元素的大小进行自适应调整。在自定义的 SvgIcon 组件中,可以修改 widthheight 的默认值为百分比:

<template>
  <svg :class="svgClass" :width="width" :height="height" aria - hidden="true">
    <use :xlink:href="`#${iconName}`"></use>
  </svg>
</template>

<script>
export default {
  name: 'SvgIcon',
  props: {
    iconName: {
      type: String,
      required: true
    },
    width: {
      type: [String, Number],
      default: '100%'
    },
    height: {
      type: [String, Number],
      default: '100%'
    },
    svgClass: {
      type: String,
      default: ''
    }
  }
};
</script>

这样,当父元素大小改变时,SVG 图标会自动按比例缩放。

viewBox 属性的应用

viewBox 属性定义了 SVG 图形的可视区域。合理设置 viewBox 可以确保 SVG 图标在缩放时保持正确的比例和外观。例如,对于一个正方形的 SVG 图标,viewBox 可以设置为 0 0 24 24(假设图标大小为 24x24):

<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <!-- 图标路径定义 -->
</svg>

当 SVG 图标被缩放时,viewBox 内的图形会按比例缩放,不会出现变形。

媒体查询与 SVG 图标切换

在某些情况下,可能需要根据屏幕尺寸切换不同的 SVG 图标。可以结合媒体查询和 Vue 的条件渲染来实现。例如,在小屏幕上显示简单的图标,在大屏幕上显示详细的图标:

<template>
  <div>
    <SvgIcon v - if="isSmallScreen" iconName="user - simple" />
    <SvgIcon v - else iconName="user - detailed" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      isSmallScreen: window.innerWidth < 768
    };
  },
  mounted() {
    window.addEventListener('resize', () => {
      this.isSmallScreen = window.innerWidth < 768;
    });
  }
};
</script>

在上述代码中,通过 data 函数定义 isSmallScreen 来判断当前屏幕是否为小屏幕。在 mounted 钩子函数中,监听窗口的 resize 事件,动态更新 isSmallScreen 的值,从而实现根据屏幕尺寸切换 SVG 图标。

SVG 图标与主题切换

在一些项目中,可能需要支持主题切换,而 SVG 图标也需要根据主题的变化改变颜色、样式等。

通过 CSS 变量实现主题切换

  1. 定义 CSS 变量:在项目的 CSS 文件中定义与 SVG 图标相关的 CSS 变量。例如:
:root {
  --icon - color: #333;
}

.theme - dark {
  --icon - color: #fff;
}
  1. 应用 CSS 变量到 SVG 图标:在 SVG 图标组件的样式中使用这些 CSS 变量。例如,在 SvgIcon.vue 组件的 <style> 标签中:
svg {
  fill: var(--icon - color);
}

这样,当切换主题时,只需要改变 :root 或特定主题类(如 .theme - dark)下的 CSS 变量值,SVG 图标颜色就会相应改变。在 Vue 组件中,可以通过添加或移除主题类来实现主题切换:

<template>
  <div :class="`theme - ${theme}`">
    <SvgIcon iconName="user" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      theme: 'light'
    };
  },
  methods: {
    toggleTheme() {
      this.theme = this.theme === 'light'? 'dark' : 'light';
    }
  }
};
</script>

根据主题切换 SVG 图标文件

除了通过 CSS 改变 SVG 图标样式,还可以根据主题切换不同的 SVG 文件。例如,在 src/icons 目录下创建 lightdark 两个子目录,分别存放不同主题的 SVG 图标。

<template>
  <div>
    <SvgIcon :iconName="getIconName('user')" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      theme: 'light'
    };
  },
  methods: {
    getIconName(name) {
      return `${this.theme}/${name}`;
    },
    toggleTheme() {
      this.theme = this.theme === 'light'? 'dark' : 'light';
    }
  }
};
</script>

SvgIcon 组件中,根据当前主题动态获取对应的 SVG 图标名称,从而实现根据主题切换不同的 SVG 图标文件。

解决 SVG 图标兼容性问题

虽然 SVG 已经得到了广泛支持,但在一些旧版本浏览器中可能还存在兼容性问题。

浏览器前缀

在使用 CSS 对 SVG 图标进行样式设置时,某些属性可能需要添加浏览器前缀以确保在不同浏览器中正常显示。例如,对于 transform 属性:

svg {
  -webkit - transform: rotate(45deg);
  -moz - transform: rotate(45deg);
  -ms - transform: rotate(45deg);
  -o - transform: rotate(45deg);
  transform: rotate(45deg);
}

提供备用方案

对于一些不支持 SVG 的极旧浏览器,可以提供备用方案,如使用 PNG 图片替代 SVG 图标。可以通过 Modernizr 库来检测浏览器是否支持 SVG,然后根据检测结果进行处理。首先安装 Modernizr:

npm install modernizr --save - dev

然后在 main.js 文件中引入并使用:

import Vue from 'vue';
import Modernizr from'modernizr';

if (!Modernizr.svg) {
  // 使用 PNG 图片替代 SVG 图标的逻辑
  // 例如,可以修改 SvgIcon 组件的渲染逻辑,使其在不支持 SVG 时显示 PNG 图片
}

SvgIcon 组件中,可以根据检测结果进行条件渲染:

<template>
  <div>
    <img v - if="!isSvgSupported" :src="getPngUrl(iconName)" alt="icon" />
    <svg v - else :class="svgClass" :width="width" :height="height" aria - hidden="true">
      <use :xlink:href="`#${iconName}`"></use>
    </svg>
  </div>
</template>

<script>
export default {
  name: 'SvgIcon',
  props: {
    iconName: {
      type: String,
      required: true
    },
    width: {
      type: [String, Number],
      default: '1em'
    },
    height: {
      type: [String, Number],
      default: '1em'
    },
    svgClass: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      isSvgSupported: typeof SVGRect!== 'undefined'
    };
  },
  methods: {
    getPngUrl(name) {
      return `/static/png/${name}.png`;
    }
  }
};
</script>

通过上述方式,可以在一定程度上解决 SVG 图标在不同浏览器中的兼容性问题。

与第三方图标库结合使用

在实际项目中,除了自定义 SVG 图标,还经常会使用第三方图标库。

使用 Font Awesome 与 SVG

Font Awesome 是一个广泛使用的图标库,它既提供字体图标,也提供 SVG 图标。要使用 Font Awesome 的 SVG 图标,首先需要安装相关包:

npm install @fortawesome/fontawesome - free --save

然后在 Vue 组件中引入并使用:

<template>
  <div>
    <i class="fab fa - github"></i>
  </div>
</template>

<script>
import '@fortawesome/fontawesome - free/css/all.min.css';

export default {
  // 组件逻辑...
};
</script>

在上述代码中,通过引入 Font Awesome 的 CSS 文件,并使用相应的类名(如 fab fa - github 表示 GitHub 图标)来显示 SVG 图标。如果需要自定义样式,可以在项目的 CSS 文件中覆盖 Font Awesome 的默认样式。

使用 Material Icons 与 SVG

Material Icons 是 Google 推出的一套图标库,同样支持 SVG 格式。安装方式如下:

npm install @material - icons/svg --save

在 Vue 组件中使用:

<template>
  <div>
    <span class="material - icons">home</span>
  </div>
</template>

<script>
import '@material - icons/svg/material - icons.css';

export default {
  // 组件逻辑...
};
</script>

通过引入 Material Icons 的 CSS 文件,并使用相应的类名(如 material - icons home 表示家图标)来展示 SVG 图标。与 Font Awesome 类似,也可以根据项目需求自定义样式。

与第三方图标库结合使用,可以快速获取丰富的图标资源,同时结合前面介绍的优化、响应式等方法,能够打造出高性能、美观且兼容的前端界面。

在 Vue 3 中的 SVG 图标使用变化

随着 Vue 3 的发布,一些 API 和使用方式发生了变化,SVG 图标使用也有相应的不同。

组合式 API 下的 SVG 图标组件

在 Vue 3 中使用组合式 API 来创建 SVG 图标组件。首先创建 SvgIcon.vue 文件:

<template>
  <svg :class="svgClass" :width="width" :height="height" aria - hidden="true">
    <use :xlink:href="`#${iconName}`"></use>
  </svg>
</template>

<script setup>
import { defineProps } from 'vue';

const props = defineProps({
  iconName: {
    type: String,
    required: true
  },
  width: {
    type: [String, Number],
    default: '1em'
  },
  height: {
    type: [String, Number],
    default: '1em'
  },
  svgClass: {
    type: String,
    default: ''
  }
});
</script>

在上述代码中,使用 defineProps 来定义组件的属性,相比 Vue 2 的 props 选项,这种方式更加简洁直观。

Vue 3 中的响应式原理变化对 SVG 图标的影响

Vue 3 采用了基于 Proxy 的响应式系统。在处理 SVG 图标响应式相关逻辑时,例如根据屏幕尺寸切换图标,虽然基本思路不变,但在数据响应式更新上有一些差异。以之前屏幕尺寸切换图标的例子来说:

<template>
  <div>
    <SvgIcon v - if="isSmallScreen" iconName="user - simple" />
    <SvgIcon v - else iconName="user - detailed" />
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';

const isSmallScreen = ref(window.innerWidth < 768);

onMounted(() => {
  window.addEventListener('resize', () => {
    isSmallScreen.value = window.innerWidth < 768;
  });
});
</script>

这里使用 ref 来创建响应式数据 isSmallScreen,并在 onMounted 钩子函数中监听窗口 resize 事件,通过修改 isSmallScreen.value 来触发视图更新,从而实现响应式切换 SVG 图标。

了解 Vue 3 中的这些变化,能够更好地在新的 Vue 版本中使用和优化 SVG 图标。无论是基础使用、优化策略,还是与其他功能的结合,都需要根据 Vue 3 的特性进行相应调整,以充分发挥其优势,提升项目的开发效率和用户体验。