文章
记录
文化
加载
保存
删除
上传
删除
预览
<<>>
段落缩进
#[]()
target=_blank的链接

阿鲁第32号表情
![我是描述[80% x ]]()
宽度80%高度未设的图片
-(red: 文字)-
红色的文字
[html][/html]
直接插入HTML
[youtube][描述](https://)[/youtube]
youtube/bilibili视频
--标题-- 内容 -- --
field元素
阿鲁
小黄脸
<<>>几个月前我刚接触vue3时就研究过,一直搞忘了分享。 --起因-- <<>>我的vue项目一般都使用动态导入的svg-sprite,意思是:既要是个雪碧图,用`<use>` 标签复用;而且页面的svg应该是动态按需加载的。我觉得这样性能比较好,在vue2中#[实现比较简单](https://github.com/yunyuyuan/cloudflare-blog/blob/master/components/svg-icon.vue#L28),但是vue3+vite下我并未找到合适的解决方案,只有雪碧图没有动态导入。于是乎我就想自己做一个。 -- -- ### 思路 **就两点** * 首先要做一个#[vite plugin](https://cn.vitejs.dev/guide/api-plugin.html),作为一个loader加载`.svg`文件,读取svg文件的内容,类似`raw-loader`。 * 然后需要一个component,它去动态加载svg文件,并把svg文件的内容拼接到雪碧图里。 ### 代码 `vite.config.ts`中这样写: ```ts import {defineConfig, Plugin} from 'vite' import vue from '@vitejs/plugin-vue' import fs from "fs"; import {dataToEsm} from "rollup-pluginutils"; const rawSvgPlugin:Plugin = { name: 'raw-svg-file-loader', transform(svg: string, filepath: string) { // 判断后缀是否为svg if (filepath.slice(-4) !== '.svg') return null; const content = fs.readFileSync(filepath).toString() return { // 直接返回svg文件的原始内容 code: dataToEsm(content) } }, } export default defineConfig({ plugins: [vue(), rawSvgPlugin], }) ``` `IconSvg.vue`文件: ```ts <template> <svg aria-hidden="true"> <use :href="getName"></use> </svg> </template> <script lang="ts"> import {defineComponent} from "vue"; const svgParser = new DOMParser(); export default defineComponent({ name: "IconSvg", props: { name: { type: String, default: '' } }, data (){ return { getName: '' } }, watch: { // 监听 name 变化 '$props.name': { // 首次执行 immediate: true, async handler (){ // 拼接svg文件名 const getId = `icon-${this.name}` const name = `#${getId}` // 动态加载 const res = await import(`../svg/${this.name}.svg`); // 雪碧图的DOM容器 let container = document.querySelector('#_SVG_SPRITE_CONTAINER_'); if (!container || !container.querySelector(name)) { if (!container) { // 如果还未创建容器,就创建一个。(此处也可以直接写在index.html里) container = document.createElement('div'); container.id = '_SVG_SPRITE_CONTAINER_' container.setAttribute('xmlns', 'http://www.w3.org/2000/svg') container.setAttribute('style', 'position: absolute; width: 0; height: 0;overflow: hidden') document.body.insertBefore(container, document.body.children[0]); } if (!container.querySelector(name)) { // 如果容器内没有该svg,则解析并制作该svg的雪碧图 const svgElement = svgParser.parseFromString(res.default, "image/svg+xml").querySelector('svg'); if (svgElement) { //删除影响样式的属性 for (const key of ['width', 'height', 'x', 'y']) { svgElement.removeAttribute(key) } svgElement.id = getId // 插入到容器里 container.appendChild(svgElement as SVGSVGElement) } } } this.getName = name; } } }, }) </script> ``` 在`main.ts`里只需要全局注册IconSvg组件就行了: ```ts import { createApp } from 'vue' import App from './App.vue' import IconSvg from "./assets/svg/IconSvg.vue"; createApp(App).component('svg-icon', IconSvg).mount('#app') ``` 这样使用: ```html <!-- 对应home.svg --> <svg-icon name="home"/> ``` ### 小结 这样做问题是解决了,可以动态导入svg并生成雪碧图,但是方式有点不优雅,有点投机取巧的感觉
输入密码
取消
确认