序言
在组件库开发的精彩旅程中🚀,我们已经成功打造并完善了图标组件体系,赋予其强大的功能和丰富的表现力🎉。然而,随着业务版图的不断扩张🌐,手动逐个编写 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>
标签中那些我们不需要的属性。通过正则表达式,它逐个匹配并移除诸如id
、pid
、class
等非必要属性。同时,为了确保图标颜色能根据上下文灵活变化,它将<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 ⭐,您的支持就是它们成长的最大动力,在此谢过各位大侠啦!
Nova UI
组件库:github.com/gmingchen/n...- 基于 Vue3 + Element-plus 管理后台基础功能框架
- 预览:admin.gumingchen.icu
- Github:github.com/gmingchen/a...
- Gitee:gitee.com/shychen/agi...
- 基础版后端:github.com/gmingchen/j...
- 文档:admin.gumingchen.icu/doc/
- 基于 Vue3 + Element-plus + websocket 即时聊天系统
- 基于 node 开发的后端服务:github.com/gmingchen/n...