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

序言

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

思路

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

html 复制代码
<?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 文件

js 复制代码
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 标签

js 复制代码
function extractSvg(content) {
  const startIndex = content.indexOf('<svg')
  const endIndex = content.lastIndexOf('</svg>') + '</svg>'.length
  return content.slice(startIndex, endIndex)
}

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

移除svg标签非必要属性

js 复制代码
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 内容

js 复制代码
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 项目中直接使用的组件 。

驼峰转换

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

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

写入vue文件

vbnet 复制代码
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语句

javascript 复制代码
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语句

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

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

写入主入口文件

ini 复制代码
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中。这个文件就像是组件库的大门,所有外部对组件库的访问都通过这个文件进行。通过将这些关键信息写入其中,我们完成了组件库的整体搭建,使得所有组件能够有序地被引入和使用 。

文件写入

vbnet 复制代码
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函数,将相关的导入、导出和类型声明语句写入主入口文件。通过这个函数的调度,整个自动化生成过程得以有条不紊地完成 。

运行

scss 复制代码
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 ⭐,您的支持就是它们成长的最大动力,在此谢过各位大侠啦!

相关推荐
Mintopia12 分钟前
计算机图形学学习指南
前端·javascript·计算机图形学
Mintopia12 分钟前
three.js 中的动画(animation)
前端·javascript·three.js
AI大模型顾潇14 分钟前
[特殊字符] Prompt如何驱动大模型对本地文件实现自主变更:Cline技术深度解析
前端·人工智能·llm·微调·prompt·编程·ai大模型
苹果酱056720 分钟前
Vue3 源码解析(六):响应式原理与 reactive
java·vue.js·spring boot·mysql·课程设计
一颗不甘坠落的流星21 分钟前
【JS】计算任意字符串的像素宽度(px)
javascript·react.js·ecmascript
z_mazin23 分钟前
JavaScript 渲染内容爬取:Puppeteer 入门
开发语言·javascript·ecmascript
小小小小宇25 分钟前
React中 useEffect和useLayoutEffect源码原理
前端
AlexJee28 分钟前
在vue3中使用vue-cropper完成头像裁剪上传图片功能
前端
清晨細雨29 分钟前
uniapp微信小程序:WIFI设备配网之TCP/UDP开发AP配网
前端·物联网·小程序·uni-app
阿廖沙102430 分钟前
Rust核心概念
前端