解锁神奇之力:探秘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提供的CSSProperties
和computed
方法。interface SvgProps
定义了组件的属性类型,包括name
(图标名称)、prefix
(图标前缀,默认为"icon")、iconStyle
(图标样式,默认为一个包含width
和height
的对象)和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提供了一个计算属性,根据prefix
和name
属性拼接而成。
这段代码的作用是根据提供的属性渲染一个SVG图标。具体使用方式是在父组件中引用并传入相应的属性,例如:
ts
<SvgIcon name="example" :color="customColor" />
其中,name
和color
是必传属性,prefix
和iconStyle
是可选属性。
第五步:使用封装好的图标组件
写图标名称
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-loader
和file-loader
引入SVG作为图像文件
安装url-loader
和file-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:
-
使用
url-loader
和file-loader
引入SVG作为图像文件:- 安装
url-loader
和file-loader
; - 在Webpack配置中添加相关规则;
- 在Vue组件中通过
require
引入SVG作为图像文件。
- 安装
-
使用
svg-sprite-loader
将SVG图标打包为Sprite图像:- 安装
svg-sprite-loader
; - 在Webpack配置中添加相关规则;
- 在Vue组件中使用
<use>
标签引入Sprite图像。
- 安装
-
使用
vue-svg-loader
将SVG图标作为单独的组件引入:- 安装
vue-svg-loader
; - 在Webpack配置中添加相关规则;
- 在Vue组件中通过导入SVG组件来使用。
- 安装
Vite:
-
使用
@vitejs/plugin-svg
插件引入SVG:- 在Vite配置文件中添加
@vitejs/plugin-svg
插件; - 在Vue组件中通过
import
引入SVG。
- 在Vite配置文件中添加
总结起来,引入SVG的方式在Webpack和Vite中有所不同,分别需要配置不同的规则或插件。使用Webpack时,可以使用url-loader
和file-loader
、svg-sprite-loader
或vue-svg-loader
来引入SVG。而在Vite中,可以使用@vitejs/plugin-svg
插件来引入SVG