svg-sprite-loader
使用 svg-sprite-loader 实现 svg 图标按需加载
参考文档:
使用 svg-sprite-loader 在 Vue3 项目中实现图标按需加载
直接使用svg的问题
参考文档: https://www.cnblogs.com/Leophen/p/13201907.html
一般用法在 Vue 中直接使用 svg,但既然已经是用 Vue来组件化开发项目了,那么在组件中穿插着一大段的 svg 也显得过于杂乱;
这里可以通过 svg 的 use 标签,将 svg 的一大段绘制代码封装在 symbol 中,然后通过 use 调用。
例如,将所有绘制 svg 的代码放到 svg-icon.vue 文件中,所有图标的绘制代码使用 symbol 标签分隔开并单独命名,然后将这个文件当做组件导出,在主页面中引入此组件,接着在需要使用 svg 图标的地方通过 use 标签将其引入, svg-icon.vue 代码示例如下:
<template>
<svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink"
style={{position:'absolute',width:0,height:0}}>
<defs>
<symbol viewBox="0 0 26 31" id="location">
<path xmlns="http://www.w3.org/2000/svg"
d="M512.571517 65.907059c-204.736964 0-370.715183 165.979242-370.715183 370.724393 0 94.440929 35.320437 180.625824 93.462648 246.083651 1.572822 2.690272 3.50994 5.225001 5.817496 7.531534l240.297878 251.597225c1.279133 1.864464 2.736321 3.64297 4.393054 5.298679 2.111081 2.111081 4.418636 3.90596 6.856152 5.402033 14.458293 10.06524 34.491559 8.658194 47.393403-4.242627 3.26537-3.263323 5.78782-6.987135 7.582699-10.960633L783.610536 690.24766c1.867534-1.866511 3.489474-3.88447 4.876054-6.010901 58.951647-65.640999 94.819552-152.431691 94.819552-247.604284C883.305119 231.886301 717.325877 65.907059 512.571517 65.907059zM512.390391 588.611865c-82.734306 0-149.814074-67.087954-149.814074-149.842727 0-82.753749 67.079768-149.833517 149.814074-149.833517 82.772168 0 149.851936 67.079768 149.851936 149.833517C662.242328 521.523911 595.161536 588.611865 512.390391 588.611865z"
fill="#d81e06" />
</symbol>
</defs>
</svg>
</template>
这里将整个 vue 组件导出一个大的 svg,此 svg 中包含了许多小图标,类似于精灵图,每个图标使用 symbol 分隔,并单独命名以方便调用。
在 index.vue 中使用示例:
...
<svg class="location-icon">
<use xlink:href="#location"></use>
</svg>
...
不过还有个问题,如果当一个页面需要用到的 svg 图标很多,势必就造成 svg-icon.vue 这个文件非常大,当另一个页面只需要用到其中一个 svg 图标时,就需要把包含几百个图标的 svg 组件加载进去,明显不太友好; 最好是能够实现按需加载,当前页面需要哪些图标就加载哪些。
通过 vue-svg-icon
插件实现按需加载
安装
npm install vue-svg-icon --save-dev
在项目的 main.js 入口引入 vue-svg-icon 以便全局调用
import Icon from 'vue-svg-icon/Icon.vue';
Vue.component('icon', Icon);
在组件中按需加载需要的图标
例如 pen.svg 放到了 /src/svg 目录中,在 vue 组件按需加载:
<template>
<icon name="pen" scale="1"></icon>
</template>
这里可以通过修改 scale 属性值或在使用的页面中修改 svg 样式来调整图标大小
安装
npm install svg-sprite-loader --save-dev
svg图片导入项目
示例: email.svg
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 4H20C21.1 4 22 4.9 22 6V18C22 19.1 21.1 20 20 20H4C2.9 20 2 19.1 2 18V6C2 4.9 2.9 4 4 4Z" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
<path d="M22 6L12 13L2 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
这里将 svg 图标中对应的图标颜色值改为字符串 currentColor
,方便使用时控制图标实时颜色 将图标放在特定文件夹下,这里以 @/assets/svg
中导入的 svg 为例。
配置 vue.config.js
const path = require("path");
module.exports = {
// 配置使用stylus全局变量
chainWebpack: config => {
const svgRule = config.module.rule("svg");
svgRule.uses.clear();
svgRule
.use("svg-sprite-loader")
.loader("svg-sprite-loader")
.options({
symbolId: "icon-[name]"
})
.end();
}
};
我的vue.config.js
const { defineConfig } = require('@vue/cli-service')
const CompressionPlugin = require('compression-webpack-plugin');
const { resolve } = require('path');
module.exports = defineConfig({
devServer: {
allowedHosts: ['wl.wl_dev']
},
lintOnSave: false,
transpileDependencies: [
'quasar'
],
pluginOptions: {
quasar: {
importStrategy: 'kebab',
rtlSupport: false
}
},
productionSourceMap: false,
chainWebpack(config) {
delete config.plugins['prefetch'];
if(process.env.NODE_ENV !== 'development') {
config.plugin('compressionPlugin').use(
new CompressionPlugin({
test: /\.(js|css|html)$/,
threshold: 10240,
})
)
}
config.module.rules.delete("svg")
const svgRule = config.module.rule("svg-sprite-loader");
svgRule.test(/\.svg$/).include.add(resolve('src/assets/svg')).end()
svgRule.use("svg-sprite-loader")
.loader("svg-sprite-loader")
.options({
symbolId: "icon-[name]"
})
.end();
}
})
新建 SvgIcon.vue 文件
新建 SvgIcon.vue 文件,这里可传入 name 属性控制图标类型,传入 size 属性控制图标大小,传入 color 属性控制图标颜色。
views/app/components/SvgIcon.vue
<template>
<svg class="svg-icon" :style="{
width: props.size + 'px',
height: props.size + 'px',
color: props.color
}" @mousedown="clickIcon">
<use :xlink:href="`#icon-${props.name}`" :fill="props.color" />
</svg>
</template>
<script>
import { defineComponent } from "vue";
export default defineComponent({
name: "SvgIcon",
props: {
name: {
type: String,
required: true,
default: "email"
},
size: {
type: Number,
default: 32
},
color: {
type: String,
default: "#000"
}
},
setup(props) {
return {
props
};
}
});
</script>
将SvgIcon.vue
添加为插件
src/plugins
目录下新建 svgIcon.js
import SvgIcon from "@/views/app/components/SvgIcon.vue";
const componentPlugin = {
install: function(vue, options) {
if (
options &&
options.imports &&
Array.isArray(options.imports) &&
options.imports.length > 0
) {
const { imports } = options;
imports.forEach((name) => {
require(`@/assets/svg/${name}.svg`);
});
} else {
const ctx = require.context("@/assets/svg", false, /\.svg$/);
ctx.keys().forEach(path => {
const temp = path.match(/\.\/([A-Za-z0-9\-_]+)\.svg$/);
if (!temp) return;
const name = temp[1];
require(`@/assets/svg/${name}.svg`);
});
}
vue.component(SvgIcon.name, SvgIcon);
}
};
export default componentPlugin;
main.js 引入插件
在 main.js(或 main.ts)中引入上面的 plugin 文件
import plugin from "./plugin";
createApp(App)
.use(plugin, {
imports: []
})
我的main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import { Quasar } from 'quasar'
import quasarOpt from '@/plugins/quasar'
import Axios from './plugins/axios'
import * as Expand from './utils/expand'
// import 'default-passive-events'
import svgIconPlugin from './plugins/svgIcon'
try{
store.commit('Auth/loadJwt')
} catch(err) {
console.log(err)
}
const app =createApp(App)
app.config.globalProperties.Axios = Axios
app.use(Quasar, quasarOpt)
.use(store)
.use(router)
.use(svgIconPlugin)
.mount('#app')
图标组件的使用
<SvgIcon name="email" :size="24" color="green" />
报错解决
启动报错
参考文档: https://segmentfault.com/q/1010000037660061
报错信息:
Compiled with problems:X
ERROR in ./src/assets/desktop/bg-dark.svg
Module build failed (from ./node_modules/svg-sprite-loader/lib/loader.js):
ExtractPluginMissingException: svg-sprite-loader exception. svg-sprite-loader in extract mode requires the corresponding plugin
at Object.loader (/data/Project/GitLab/WL_Ngrok_V2/src/frontend/admin/node_modules/svg-sprite-loader/lib/loader.js:47:13)
ERROR in ./src/assets/desktop/bg.svg
Module build failed (from ./node_modules/svg-sprite-loader/lib/loader.js):
ExtractPluginMissingException: svg-sprite-loader exception. svg-sprite-loader in extract mode requires the corresponding plugin
at Object.loader (/data/Project/GitLab/WL_Ngrok_V2/src/frontend/admin/node_modules/svg-sprite-loader/lib/loader.js:47:13)
ERROR
Cannot read properties of undefined (reading 'get')
during rendering of asset asset/resource|/data/Project/GitLab/WL_Ngrok_V2/src/frontend/admin/node_modules/svg-sprite-loader/lib/loader.js??clonedRuleSet-6.use[0]!/data/Project/GitLab/WL_Ngrok_V2/src/frontend/admin/src/assets/desktop/bg-dark.svg
原因: webpack配置问题。
解决方法: 修改vug.config.js
const { defineConfig } = require('@vue/cli-service')
const CompressionPlugin = require('compression-webpack-plugin');
const { resolve } = require('path');
const path = require("path");
module.exports = defineConfig({
devServer: {
allowedHosts: ['wl.wl_dev']
},
lintOnSave: false,
transpileDependencies: [
'quasar'
],
pluginOptions: {
quasar: {
importStrategy: 'kebab',
rtlSupport: false
}
},
productionSourceMap: false,
chainWebpack(config) {
delete config.plugins['prefetch'];
if(process.env.NODE_ENV !== 'development') {
config.plugin('compressionPlugin').use(
new CompressionPlugin({
test: /\.(js|css|html)$/,
threshold: 10240,
})
)
}
const svgRule = config.module.rule("svg");
svgRule.uses.clear();
svgRule.test(/\.svg$/).include.add(resolve('src/assets/svg')).end()
.use("svg-sprite-loader")
.loader("svg-sprite-loader")
.options({
symbolId: "icon-[name]"
})
.end();
}
})
添加: svgRule.test(/\.svg$/).include.add(resolve('src/assets/svg')).end()
使用SvgIcon
不显示图片
参考文档: https://blog.csdn.net/weixin_45732235/article/details/123895025
将:
const svgRule = config.module.rule("svg");
svgRule.uses.clear();
svgRule.test(/\.svg$/).include.add(resolve('src/assets/svg')).end()
.use("svg-sprite-loader")
.loader("svg-sprite-loader")
.options({
symbolId: "icon-[name]"
})
.end();
改为:
config.module.rules.delete("svg")
const svgRule = config.module.rule("svg-sprite-loader");
svgRule.test(/\.svg$/).include.add(resolve('src/assets/svg')).end()
svgRule.use("svg-sprite-loader")
.loader("svg-sprite-loader")
.options({
symbolId: "icon-[name]"
})
.end();