Vue3: 探秘webpack与Vite引入SVG的独特技巧

解锁神奇之力:探秘webpack与Vite引入SVG的独特技巧

注意:不想看过程的伙伴,可以直达文章最后,有总结(赠人玫瑰,手留余香,懂的就留个余香,不懂的就感谢观看,谢谢回顾)

引言

最近在封装一个多文件上传组件,需求是按照不同类型的文件上传后,回显对应类型的封面图,如下:

而这些封面图都是存储在本地项目中,如下:/assets/svg 文件夹

本想通过最常用的图片引人方式如下:

html 复制代码
<img src="@/assets/svg/0.icon_file-icon-word.svg" alt="转存失败,建议直接上传图片文件">

结果却是:

用浏览器的调试工具才发现,解析出来的却是[object]

但在以前Vue2版本的时候,可以直接正常显示,为什么Vue3后却不能正常显示了呢?

这是因为Vue3中的模板编译器对于SVG文件的处理方式与Vue2有所不同。

探秘

Vite构建工具下

当我们在Vite工具下使用require('@/assets/xx.svg'),这种引入方式结果会如何?

代码:

ts 复制代码
const word = require('@/assets/svg/0.icon_file-icon-other.svg');

运行后:

那么为何报require is not defined ?

在Vue3的Vite项目中,因为Vite使用ES模块语法,所以无法直接使用CommonJS的require语法。require在Vite中会返回undefined

既然不支持使用require,那我们改成它支持的动态导入语法import()总行了吧?

尝试

那么我就有了下面的尝试:

第一步:将svg全部导入到vue3对象中去,如下:

第二步:查看效果,效果就是,不是我想象的那样😭,如下:

代码是这样写的:

渲染后结果是这样的:

第三步:分析原因:

既然显示错误,那么我们看看,执行这个代码, import word from '@/assets/svg/0.icon_file-icon-word.svg'; 之后,是个什么东西,打印一下

从图中可以看出来,word通过import引入svg的方式,得到的是一个render对象,那是因为在Vue3中,通过import导入的SVG文件默认被当作Vue组件处理;

那么有什么办法可以让其正常加载svg呢?

解决方案

1. 安装依赖插件

npm i vite-plugin-svg-icons

2. 配置 vite.config.js(或vite.config.ts)

第一步:引入

import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';

第二步:制定缓存图标文件夹
ts 复制代码
plugins: [
    createSvgIconsPlugin({
        // 指定要缓存的图标文件夹
        iconDirs: [path.resolve(process.cwd(), 'src/assets/svg')],
        // 执行icon name的格式
        symbolId: 'icon-[dir]-[name]',
    }),
],
第三步:在 main.js(或main.ts) 入口注册脚本

import "virtual:svg-icons-register";

注意: vite-plugin-svg-icons 插件注册,不引入会导致svg图标, 无法正常显示

第四步:封装 SvgIcon 组件
ts 复制代码
<template>
	<svg aria-hidden="true" :style="iconStyle">
		<use :href="symbolId" :fill="color" />
	</svg>
</template>

<script setup lang="ts">
import { CSSProperties, computed } from 'vue';

interface SvgProps {
	name: string; // 图标的名称(Svg 文件名) ==> 必传
	prefix?: string; // 图标的前缀 ==> 非必传(默认为"icon")
	iconStyle?: CSSProperties; // 图标的样式 ==> 非必传
	color: string;
}

// 接收父组件参数并设置默认值
const props = withDefaults(defineProps<SvgProps>(), {
	prefix: 'icon',
	iconStyle: () => ({ width: '30px', height: '30px' }),
	color: '#333',
});

const symbolId = computed(() => `#${props.prefix}-${props.name}`);
</script>

用于渲染SVG图标。

模板部分:

  • <svg>元素表示SVG图像容器,aria-hidden="true"的属性用于辅助技术(如屏幕阅读器)隐藏该图标。
  • :style="iconStyle"绑定了一个动态样式对象,用于设置图标的样式。
  • <use>元素用于在SVG图像中引用符号(Symbol),:href绑定了一个动态的符号ID,:fill绑定了一个动态的颜色值。

脚本部分(使用<script setup>语法):

  • import { CSSProperties, computed } from 'vue'引入了Vue 3提供的CSSPropertiescomputed方法。
  • interface SvgProps定义了组件的属性类型,包括name(图标名称)、prefix(图标前缀,默认为"icon")、iconStyle(图标样式,默认为一个包含widthheight的对象)和color(图标颜色)。
  • const props = withDefaults(defineProps<SvgProps>(), {...})中使用了defineProps定义了组件的属性,withDefaults用于为属性设置默认值。
  • const symbolId = computed(() => # <math xmlns="http://www.w3.org/1998/Math/MathML"> p r o p s . p r e f i x − {props.prefix}- </math>props.prefix−{props.name})为符号ID提供了一个计算属性,根据prefixname属性拼接而成。

这段代码的作用是根据提供的属性渲染一个SVG图标。具体使用方式是在父组件中引用并传入相应的属性,例如:

ts 复制代码
<SvgIcon name="example" :color="customColor" />

其中,namecolor是必传属性,prefixiconStyle是可选属性。

第五步:使用封装好的图标组件

写图标名称

ts 复制代码
// console.log(word);
const word = '0.icon_file-icon-word';
const ppt = '0.icon_file-icon-ppt';
const pdf = '0.icon_file-icon-pdf';
const img = '0.icon_file-icon-img';
const other = '0.icon_file-icon-other';
const xls = '0.icon_file-icon-xls';

注意要与assets/svg的图标名称对应,用于索引

引入组件:import SvgIcon from '@/components/SvgIcon.vue';

使用组件:<svg-icon :name="subItem.icon" :icon-style="{ width: '50px' }"></svg-icon>

效果:

webpack构建工具下

在Vue中使用Webpack引入SVG图标,有多种引入方式。以下是几种常见的方式:

1. 使用url-loaderfile-loader引入SVG作为图像文件

安装url-loaderfile-loader

shell 复制代码
npm install --save-dev url-loader file-loader

在Webpack配置中添加规则:

ts 复制代码
module.exports = {
  // 其他配置项...
  module: {
    rules: [
      // 其他规则...
      {
        test: /.(png|jpe?g|gif|svg)$/i,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,
              fallback: 'file-loader',
              name: 'images/[name].[hash:8].[ext]'
            }
          }
        ]
      }
    ]
  }
};

在Vue组件中引入SVG:

html 复制代码
<template>
  <div>
    <img :src="require('@/assets/icons/my-icon.svg')" alt="Icon">
  </div>
</template>

2. 使用svg-sprite-loader将SVG图标打包为Sprite图像

安装svg-sprite-loader

css 复制代码
npm install --save-dev svg-sprite-loader

在Webpack配置中添加规则:

ts 复制代码
module.exports = {
  // 其他配置项...
  module: {
    rules: [
      // 其他规则...
      {
        test: /.svg$/,
        use: [
          {
            loader: 'svg-sprite-loader',
            options: {}
          }
        ]
      }
    ]
  }
};

在Vue组件中使用SVG Sprite图像:

html 复制代码
<template>
  <div>
    <svg class="icon">
      <use :xlink:href="'#my-icon'"></use>
    </svg>
  </div>
</template>

3. 使用vue-svg-loader将SVG图标作为单独的组件引入

安装vue-svg-loader

ts 复制代码
npm install --save-dev vue-svg-loader

在Webpack配置中添加规则:

ts 复制代码
module.exports = {
  // 其他配置项...
  module: {
    rules: [
      // 其他规则...
      {
        test: /.svg$/,
        loader: 'vue-svg-loader'
      }
    ]
  }
};

在Vue组件中引入SVG组件:

html 复制代码
<template>
  <div>
    <SVGIcon />
  </div>
</template>

<script>
import SVGIcon from './icon.svg';

export default {
  components: {
    SVGIcon
  }
}
</script>

这些是在Vue中使用Webpack引入SVG图标的多种方式。根据你的需求和项目配置,选择适合的方式即可

大总结

在使用Webpack和Vite这两个构建工具引入SVG图标时,有一些不同的注意事项。以下是对它们的引入方式的总结:

Webpack:

  1. 使用url-loaderfile-loader引入SVG作为图像文件:

    • 安装url-loaderfile-loader
    • 在Webpack配置中添加相关规则;
    • 在Vue组件中通过require引入SVG作为图像文件。
  2. 使用svg-sprite-loader将SVG图标打包为Sprite图像:

    • 安装svg-sprite-loader
    • 在Webpack配置中添加相关规则;
    • 在Vue组件中使用<use>标签引入Sprite图像。
  3. 使用vue-svg-loader将SVG图标作为单独的组件引入:

    • 安装vue-svg-loader
    • 在Webpack配置中添加相关规则;
    • 在Vue组件中通过导入SVG组件来使用。

Vite:

  1. 使用@vitejs/plugin-svg插件引入SVG:

    • 在Vite配置文件中添加@vitejs/plugin-svg插件;
    • 在Vue组件中通过import引入SVG。

总结起来,引入SVG的方式在Webpack和Vite中有所不同,分别需要配置不同的规则或插件。使用Webpack时,可以使用url-loaderfile-loadersvg-sprite-loadervue-svg-loader来引入SVG。而在Vite中,可以使用@vitejs/plugin-svg插件来引入SVG

相关推荐
GIS程序媛—椰子12 分钟前
【Vue 全家桶】7、Vue UI组件库(更新中)
前端·vue.js
DogEgg_00118 分钟前
前端八股文(一)HTML 持续更新中。。。
前端·html
ZL不懂前端21 分钟前
Content Security Policy (CSP)
前端·javascript·面试
乐闻x24 分钟前
ESLint 使用教程(一):从零配置 ESLint
javascript·eslint
木舟100925 分钟前
ffmpeg重复回听音频流,时长叠加问题
前端
王大锤439135 分钟前
golang通用后台管理系统07(后台与若依前端对接)
开发语言·前端·golang
我血条子呢1 小时前
[Vue]防止路由重复跳转
前端·javascript·vue.js
黎金安1 小时前
前端第二次作业
前端·css·css3
啦啦右一1 小时前
前端 | MYTED单篇TED词汇学习功能优化
前端·学习