当前位置: 首页 > news >正文

【Nova UI】十、打造组件库第一个组件-图标组件(下):从.svg 到 SVG Vue 组件的高效蜕变✨

序言

在组件库开发的精彩旅程中🚀,我们已经成功打造并完善了图标组件体系,赋予其强大的功能和丰富的表现力🎉。然而,随着业务版图的不断扩张🌐,手动逐个编写 SVG Vue 组件的传统方式,逐渐暴露出效率低下的短板。这种重复性的工作不仅耗费大量宝贵的时间⏳与精力💪,还容易在机械的操作中引入人为错误🚫。今天,让我们一同踏上探索自动化生成的奇妙之路🧭,聚焦于如何借助神奇的代码力量,通过.svg 文件自动生成 SVG Vue 组件,开启简化操作的崭新时代🌟。这一转变将如同为组件库开发装上了涡轮增压引擎,推动开发效率实现质的飞跃,让我们的开发工作更加高效、流畅 。

思路

为深入理解自动生成的实现逻辑,我们从阿里图库下载了一个典型的.svg 文件。其内部结构如下:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#2c2c2c" d="M474 152m8 0l60 0q8 0 8 8l0 704q0 8-8 8l-60 0q-8 0-8-8l0-704q0-8 8-8Z"  /><path fill="#2c2c2c" d="M168 474m8 0l672 0q8 0 8 8l0 60q0 8-8 8l-672 0q-8 0-8-8l0-60q0-8 8-8Z"  />
</svg>

剖析这个文件可知,我们所需的关键信息集中在<svg>节点及其子节点内容。为达成从.svg 文件到 SVG Vue 组件的转换,首要任务是读取该文件📄,精准提取所需数据📊,巧妙移除<svg>节点的非必要属性,最终依据处理后的数据生成对应的 Vue 文件📝。

读取 .svg 文件

const readFile = () => {const result = {}const files = fs.readdirSync(assetsRoot)files.forEach(file => {const path = `${assetsRoot}/${file}`let content = fs.readFileSync(path, 'utf8')content = format(content)const name = file.replace('.svg', '')result[name] = content})return result
}

此函数犹在指定目录assetsRoot中,读取所有.svg 文件。对每个文件,它打开并读取内容,进行格式化处理,然后提取文件名(去除.svg 后缀)作为键,将处理后的内容作为值,存入结果对象。

提取 svg 标签

function extractSvg(content) {const startIndex = content.indexOf('<svg')const endIndex = content.lastIndexOf('</svg>') + '</svg>'.lengthreturn content.slice(startIndex, endIndex)
}

该函数在整个文件内容中定位<svg>标签的起始和结束位置,然后将这部分内容完整地裁剪出来。这一步确保我们只保留了与 SVG 图形直接相关的部分,为后续的处理提供了纯净的数据基础 。

移除svg标签非必要属性

function removeAttribute(content) {const removeAttrs = ['id', 'pid', 'class', 'width', 'height', 'version', 'fill']removeAttrs.forEach(attr => {const reg = new RegExp(` ${attr}="[^"]*"`, "g")content = content.replace(reg, '')})content = content.replace('<svg', '<svg fill="currentColor"')return content
}

这部分代码专门清理<svg>标签中那些我们不需要的属性。通过正则表达式,它逐个匹配并移除诸如idpidclass等非必要属性。同时,为了确保图标颜色能根据上下文灵活变化,它将<svg>标签的fill属性替换为fill="currentColor"。经过这一番清理和设置,<svg>标签变得简洁且符合我们的需求 。

将 提取的 svg 内容 转为 vue 内容

function toComponentContent(name, content) {const result = `
<template>${content}
</template><script setup>defineOptions({name: 'N${name}Svg',})
</script>
`return result
}

这个函数将提取并处理好的 SVG 内容,巧妙地包装成 Vue 组件的形式。它创建了一个包含<template><script setup>的 Vue 组件模板,将 SVG 内容放入<template>中,并在<script setup>中定义了组件的名称。经过这一步,原本的 SVG 数据摇身一变,成为了可以在 Vue 项目中直接使用的组件 。

驼峰转换

function toGreatHump(value) {return value.split('-').map(item => item.replace(item.charAt(0), item.charAt(0).toUpperCase())).join('')
}

该函数对字符串进行驼峰命名法的转换。它将以-分隔的字符串,转换为每个单词首字母大写的驼峰形式。例如,icon-close会被转换为IconClose。这种转换使得组件名称在符合 Vue 命名规范的同时,也更具可读性和一致性 。

写入vue文件

function writeComponentFile(key, content) {const greatHumpName = toGreatHump(key)const data = toComponentContent(greatHumpName, content)const path = `${componentsRoot}/${key}.vue`fs.writeFile(path, data, error => error && console.error(error))
}

这部分代码将转换好的 Vue 组件内容写入到指定路径的.vue 文件中。它先将文件名转换为驼峰形式,然后结合之前生成的 Vue 组件内容,将数据写入到componentsRoot目录下对应的.vue 文件。如果在写入过程中出现错误,它会在控制台输出错误信息,方便我们及时排查和解决问题 。

转成import、export语句

function toImport(keys) {return keys.map(key => `import ${toGreatHump(key)} from './components/${key}.vue'`).join('\n')
}
function toExport(keys) {return `export {${keys.map(key => toGreatHump(key)).join(',\n  ')}
}`
}

这两个函数负责生成导入和导出语句。toImport函数遍历所有的组件文件名,生成对应的import语句,这些语句将每个组件从其对应的.vue 文件中引入。toExport函数则将所有组件名以合适的格式组合成export语句,方便在其他地方统一引入这些组件。通过这两个函数,我们构建了一个清晰的组件导入导出体系,使得组件在项目中的使用更加便捷 。

转为 TS type语句

function toType(keys) {return `export const svgs = [\n${keys.map(key => `  '${toGreatHump(key)}'`).join(',\n')},\n] as const`
}

此函数将所有组件名整理成一个类型声明语句。这个语句定义了一个包含所有组件名的常量数组svgs,并且使用as const确保其类型为只读常量数组。在 TypeScript 项目中,这个声明有助于在使用这些组件时进行类型检查,提高代码的安全性和稳定性 。

写入主入口文件

function writeMainFile(keys) {const importData = toImport(keys)const exportData = toExport(keys)const typeData = toType(keys)const data = `${importData}\n\n${exportData}\n\n${typeData}\n`const path = `${mainRoot}/index.ts`fs.writeFile(path, data, error => error && console.error(error))
}

这个函数是整个自动化流程的 “收尾大师”🎨,它将前面生成的导入语句、导出语句和类型声明语句整合在一起,写入到主入口文件index.ts中。这个文件就像是组件库的大门,所有外部对组件库的访问都通过这个文件进行。通过将这些关键信息写入其中,我们完成了组件库的整体搭建,使得所有组件能够有序地被引入和使用 。

文件写入

const writeFile = json => {for (const key in json) {if (Object.prototype.hasOwnProperty.call(json, key)) {writeComponentFile(key, json[key])}}const keys = Object.keys(json)writeMainFile(keys)
}

这个函数是整个自动化流程的 “指挥官”👨‍✈️,它统筹协调各个部分的工作。它遍历包含所有 SVG 文件内容的对象,对每个文件内容调用writeComponentFile函数,将其写入对应的.vue 文件。然后,收集所有的文件名,调用writeMainFile函数,将相关的导入、导出和类型声明语句写入主入口文件。通过这个函数的调度,整个自动化生成过程得以有条不紊地完成 。

运行

function run() {const json = readFile()writeFile(json)
}run()

只要运行这个run方法,就会在packages/svgs/components下生成对应的 Vue 文件,在packages/svgs/生成index.ts主入口文件。

你以为到这里就够了吗?还不行!每次运行的时候都要切换到packages/svgs目录下,这显然不是很便捷。所以,我们可以在根目录下的package.json文件中scripts属性下增加一行 "svg:build": "pnpm -C packages/svgs run init" 。这样,每次只要在packages\svgs\assets目录下放上.svg 文件,并在命令行运行npm run svg:build,就会自动生成所需的组件和主入口文件。是不是超级方便呢?这一优化让我们的开发流程更加流畅,大大提高了工作效率,为组件库的持续发展和完善提供了有力支持 。

🦀🦀感谢看官看到这里,如果觉得文章不错的话🙌,点个关注不迷路⭐。
诚邀您加入我的微信技术交流群🎉,群里都是志同道合的开发者👨‍💻,大家能一起交流分享摸鱼🐟。期待与您在群里相见🚀,咱们携手在开发路上共同进步✨ !
👉点我

感谢各位大侠一路相伴,实在感激! 不瞒您说,在下还有几个开源项目 📦,它们就像精心培育的幼苗 🌱,急需您的浇灌。要是您瞧着还不错,麻烦动动手指,给它们点亮几颗 Star ⭐,您的支持就是它们成长的最大动力,在此谢过各位大侠啦!

  • Nova UI组件库:https://github.com/gmingchen/nova-ui
  • 基于 Vue3 + Element-plus 管理后台基础功能框架
  • 预览:https://admin.gumingchen.icu
    • Github:https://github.com/gmingchen/agile-admin
    • Gitee:https://gitee.com/shychen/agile-admin
    • 基础版后端:https://github.com/gmingchen/java-spring-boot-admin
    • 文档:http://admin.gumingchen.icu/doc/
  • 基于 Vue3 + Element-plus + websocket 即时聊天系统
    • 预览:https://chatterbox.gumingchen.icu/
    • Github:https://github.com/gmingchen/chatterbox
    • Gitee:https://gitee.com/shychen/chatterbox
  • 基于 node 开发的后端服务:https://github.com/gmingchen/node-server
http://www.xdnf.cn/news/146791.html

相关文章:

  • gerbera文件转PCB文件-Altium Designer
  • GitHub 趋势日报 (2025年04月24日)
  • 赛灵思 XCKU115-2FLVB2104I Xilinx Kintex UltraScale FPGA
  • Parasoft C++Test软件单元测试_对函数打桩的详细介绍
  • AKM旭化成微电子全新推出能量收集IC“AP4413系列”
  • 自然语言处理+知识图谱:智能导诊的“大脑”是如何工作的?
  • C++中的vector和list的区别与适用场景
  • LLM Graph Rag(基于LLM的事件图谱RAG)
  • 一种用于加密代理流量检测的轻量级深度学习方法
  • jdk-8u202-linux-x64.tar.gz官方下载地址
  • mysql基础——数据表查询(全面解析)
  • 技术书籍推荐(003)
  • A2A与MCP之间的简单理解
  • 【Echarts】使用echarts绘制多个不同类型的中国地图
  • Redis 集群切片全解析:四种常见技术的原理、优劣与应用
  • Github 2025-04-25 Java开源项目日报 Top8
  • Java实现HTML转PDF(deepSeekAi->html->pdf)
  • 通过模仿学习实现机器人灵巧操作:综述(下)
  • Azure Data Factory ETL设计与调度最佳实践
  • Android ActivityManagerService(AMS)深度解析
  • PostgreSQL使用LIKE右模糊没有走索引分析验证
  • 【高频考点精讲】ES6 String的新增方法,处理字符串更方便了
  • CentOS系统防火墙服务介绍
  • 【大模型理解消化的搅碎机】基于6000种商品CSV表格的知识图谱构建
  • 计组1.2.4——计算机系统的层次结构
  • Allegro23.1新功能之OrcadX平台使用操作指导
  • (三) Trae 调试C++ 基本概念
  • 【虚拟机安装Ubuntu 24 LTS】 MobaXterm 连接Access denied错误-安装进度卡“正在传输文件”-固定内网ip
  • 反爬虫机制中的验证码识别:类型、技术难点与应对策略
  • 艾德文·卡特姆:将画布变成屏幕,开启CGI时代