如何优雅的封装svg-icon并运用到项目中(webpack和vite)
先看一下封装好的使用方式: 这里先讲vue-cli项目的相关配置,vite项目对应的插件和配置教程在下方,需要的小伙伴可以直接往下翻
一. webpack项目配置流程
1. 安装依赖
首先需要在我们的项目中安装svg-sprite-loader这个loader,svg-sprite-loader是一个Webpack loader,用于将多个SVG文件打包成一个雪碧图(sprite)。它可以帮助优化网页性能,减少HTTP请求的数量,同时提供了一种方便的方式来管理和使用SVG图标
优点:
预加载 在项目运行时就生成所有图标,只需操作一次 dom高性能 内置缓存,仅当文件被修改时才会重新生成
接下来我们开始安装
(1).安装和配置 svg-sprite-loader:
npm i svg-sprite-loader -D
# 或
pnpm install svg-sprite-loader -D
# 或
yarn add svg-sprite-loader -D
(2).填写配置
如果是vue-cli搭建的项目,则需要在vue.config.js中添加:
const path = require('path')
// 项目的其他配置项,这里进行省略......
module.exports = defineConfig({
// 项目的其他配置项,这里进行省略......
chainWebpack: (config) => {
config.module.rule('svg').exclude.add(resolve('src/icons')).end()
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(resolve('src/icons'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]'
})
.end()
}
})
原生webpack的项目,则需要在webpack.config.js中添加对应的loader配置:
注意:url-loader 中要将 icons 文件夹排除, 不让 url-loader 处理该文件夹
// 其他配置项,这里进行省略......
module.exports = {
// 其他配置项,这里进行省略......
module: {
// 其他配置项,这里进行省略......
rules: [
// 其他loader,这里进行省略......
{
test: /\.svg$/,
loader: 'svg-sprite-loader',
include: [resolve('src/icons')],
options: {
symbolId: 'icon-[name]'
}
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
},
exclude: [resolve('src/icons')]
}
]
}
}
2. 新建组件和目录
(1).封装svg-icon组件
components 中新建组件 SvgIcon.vue:
vue3:
import { computed } from 'vue'
interface Prop {
iconClass: string
className?: string
}
const props = defineProps
/** 用于判断组件传入的iconClass是不是路径或链接, 如果是路径或者链接则使用div加mask遮罩的方式展示 */
const isExternalIcon = (path: string): boolean => {
return /^(\/)+([a-zA-Z0-9\s_\\.\-():/])+(.svg|.png|.jpg)$/g.test(path) || /^(https?:|mailto:|tel:)/.test(path)
}
const isExternalClass = computed(() => isExternalIcon(props.iconClass))
/** 拼接成use标签的href */
const iconName = computed(() => `#icon-${props.iconClass}`)
// svg标签的class
const svgClass = computed(() => {
if (props.className) {
return 'svg-icon ' + props.className
} else {
return 'svg-icon'
}
})
// 传入路径或者链接时div的mask遮罩样式
const styleExternalIcon = computed(() => {
return {
mask: `url(${props.iconClass}) no-repeat 50% 50%`,
'-webkit-mask': `url(${props.iconClass}) no-repeat 50% 50%`
}
})
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
stroke: var(--background-primary);
overflow: hidden;
display: inline;
position: relative;
}
.svg-external-icon {
background-color: currentColor;
mask-size: cover !important;
display: inline-block;
}
如果是vue2项目,可以使用下面这个组件:
export default {
name: 'svg-icon',
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String
}
},
computed: {
iconName() {
return `#icon-${this.iconClass}`
},
svgClass() {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
}
}
}
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
(2).在src下icons文件夹下新建svg目录和index.js文件
目录结构:
src/icons
- /svg # svg目录保存图标
- /index.ts # 注册全局组件
新建的index.ts:
// 引入封装好的SvgIcon组件
import SvgIcon from '@/components/SvgIcon/index.vue' // svg component
import type { App } from 'vue'
// 全局注册
export const registerSvgIcon = (app: App): void => {
app.component('svg-icon', SvgIcon)
const req = require.context('./svg', false, /\.svg$/)
// eslint-disable-next-line
const requireAll = (requireContext: any) =>
requireContext.keys().map(requireContext)
requireAll(req)
}
然后在项目的main.ts中引入:
main.ts部分代码
// 其他导入,这里省略......
import { createApp } from 'vue'
import App from './App.vue'
import { registerSvgIcon } from '@/icons'
const app = createApp(App).use(router).use(i18n).use(pinia)
registerSvgIcon(app)
app.mount('#app')
/* vue2直接Vue.use(registerSvgIcon)即可 */
3. 最后我们就可以在项目中全局使用了
icon-class传入的就是svg目录下文件的文件名,比如目录之放一个有个goBack.svg文件,我们就可以按照下面的方式使用。
也可以传入路径或者图片链接的方式,当然这种方式上面vue2对应的组件没有进行封装,感兴趣的小伙伴可以自己琢磨一下
二. vite项目配置流程
1. 安装
yarn add vite-plugin-svg-icons -D
# 或
npm i vite-plugin-svg-icons -D
# 或
pnpm install vite-plugin-svg-icons -D
2. 配置插件
在vite.config.ts 中的配置插件
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import path from 'path'
export default () => {
return {
plugins: [
createSvgIconsPlugin({
// 指定需要缓存的图标文件夹
iconDirs: [path.resolve(process.cwd(), 'src/icons')],
// 指定symbolId格式
symbolId: 'icon-[dir]-[name]',
/**
* 自定义插入位置
* @default: body-last
*/
inject?: 'body-last' | 'body-first'
/**
* custom dom id
* @default: __svg__icons__dom__
*/
customDomId: '__svg__icons__dom__',
}),
],
}
}
在 src/main.ts 内引入注册脚本
import 'virtual:svg-icons-register'
在src目录下新建icons文件夹用来存放svg图表
注意:不同与上面webpack的插件配置方式,这里不需要我们手动创建导入的js文件,在上面配置项的iconDirs里指定目录即可,比如上面配置了'src/icons'这个目录,只需要把svg图标全部存放到这个目录即可
3. 封装vue组件
创建svg-icon.vue组件:
import { computed } from 'vue'
interface Props {
name: string
prefix?: string
color?: string
width?: string
height?: string
}
const props = withDefaults(defineProps
prefix: 'icon',
color: '#333',
width: '16px',
height: '16px'
})
const symbolId = computed(() => `#${props.prefix}-${props.name}`)
和上面wabpack不同的是,目录可以更深层级的嵌套,比如icon目录下有个dir子目录,子目录之下放demo.svg文件,组件内就可以直接使用目录名-文件名的方式,比如:name="dir-demo"
icons 目录结构:
# src/icons
- icon1.svg
- icon2.svg
- icon3.svg
- dir/icon1.svg
其他组件中使用:
import SvgIcon from './components/SvgIcon.vue'
也可以注册为全局组件,不用每次都导入: 在components目录下新建一个index.ts 文件
// 引入项目中的全部全局组件
import SvgIcon from './svgIcon/index.vue'
//其他组件,这里省略...
// 组装成一个对象
const allGlobalComponents: any = {
SvgIcon,
//其他组件,这里省略...
}
// 对外暴露插件对象,在main.ts中引入后,直接自动调用install方法
export default {
install(app: any) {
// 循环注册所有的全局组件
Object.keys(allGlobalComponents).forEach((componentName) => {
app.component(componentName, allGlobalComponents[componentName])
})
},
}
main.ts中导入并use:
import { globalComponents } from '@/components'
reateApp(App).use(globalComponents).mount('#app')
4. 封装React组件
(1). /src/components目录下新建SvgIcon.jsx组件:
export default function SvgIcon({
name,
prefix = 'icon',
color = '#333',
width = '16px',
height = '16px',
...props
}) {
const symbolId = `#${prefix}-${name}`
return (
)
}
5. 获取所有 SymbolId
import ids from 'virtual:svg-icons-names'
// => ['icon-icon1','icon-icon2','icon-icon3']
6. 配置说明
参数类型默认值说明iconDirsstring[]-需要生成雪碧图的图标文件夹symbolIdstringicon-[dir]-[name]svg 的 symbolId 格式,见下方说明svgoOptionsboolean|SvgoOptionstruesvg 压缩配置,可以是对象Optionsinjectstringbody-lastsvgDom 默认插入的位置,可选body-firstcustomDomIdstring__svg__icons__dom__svgDom 插入节点的 ID
精彩文章
发表评论