当前位置: 首页
前端开发
程序员自救转型:PHP到AI+Golang图标组件封装

程序员自救转型:PHP到AI+Golang图标组件封装

热心网友 时间:2026-07-12
转载

基于Lucide图标库,通过Vite插件实现按需加载,1737图标分5个chunk,首屏不加载,使用某图标仅加载对应chunk(约24KBgzip),总chunk数27,构建1 08s,菜单图标可配置更换。

本系列博客记录了一位 PHP 全栈工程师借助 AI 工具(如 Claude Code、Codex、DeepSeek、豆包等),从零开始学习 Golang 语言,并最终完成 ai-go-admin 开源项目(GitHub | Gitee)的完整历程。全程记录分享,不遗漏任何一个关键节点。

从 PHP 到 AI + Golang,程序员自救转型手记(二十六):icon 组件封装

上一期尝试了后台布局迁移,但效果并不理想。手动迁移过程中发现,布局相关代码已经大量使用了 icon,因此这一期先解决 icon 组件的封装问题。

Icon 组件封装

核心需求其实很简单:只需通过一个 name 属性,就能渲染出不同图标,无需手动导入。

原来的写法如下:

 复制代码

目标是写成这样:

 复制代码

一行代码即可搞定,无需逐个手动导入。这种实现方式的好处是,后台菜单的图标可以随时通过配置修改。

图标选型

让 AI 帮忙搜索了一圈高星图标库。其实挺看好 iconpark 的,但它的 GitHub 仓库已经归档了。最终选择了 lucide,原因是 star 多、设计现代、Vue3 兼容性好,图标总量不多不少,恰到好处。

图标加载

建立了一个 @src/components/icon/index.vue 图标组件,尝试直接使用 resolveComponent("图标名") 加载,结果失败了,这也是意料之中的。接下来交给 Claude Code 处理:

 复制代码@srccomponentsiconindex.vue 中的 Smile 是来自 @lucide/vue 库的图标,我希望后续使用该库的图标,免去 import { Smile } from "@lucide/vue",直接使用 resolveComponent('Smile') 之类的方式使用(即提供一个图标名即可渲染)

这种需求对 Claude Code 来说不在话下,但它额外提醒了一件事:构建产物体积会明显增加。

 复制代码改造要点 @lucide/vue 库导出了一个 icons 对象,它是一个 图标名 → 组件 的映射表(如 { Smile: FunctionalComponent, ArrowRight: FunctionalComponent, ... }),直接用它即可按名渲染,无需全局注册或逐个 import。导入 icons 会将 所有 Lucide 图标打入包中(约 3000 个),这是按名动态使用图标的必然代价。如果后续包体积成为问题,可考虑使用 Vite 的 import.meta.glob 做按需分块加载。

又让它确认一下具体会增加多少,看看是否在可接受范围:

 复制代码  ● 实测结果  ┌─────────────────────────────┬─────────┐
  │            指标             │  大小   │
  ├─────────────────────────────┼─────────┤
  │ 全部 1737 个图标源码         │ ~1.5 MB │
  ├─────────────────────────────┼─────────┤
  │ 构建后 minified              │ ~615 KB │
  ├─────────────────────────────┼─────────┤
  │ 构建后 gzip(实际网络传输)   │ ~134 KB │
  └─────────────────────────────┴─────────┘  也就是说,引入 icons 映射后,用户实际下载的增量约为 134 KB(gzip),这在现代前端项目中是可以接受的。  如果后续觉得 134 KB 还是偏大,可以考虑按需分块方案(import.meta.glob + defineAsyncComponent,只加载用到的图标),但实现复杂度会高不少。当前方案是简单和体积之间的合理折中。

很遗憾,现在就觉得大。毕竟不是所有用户都会开启 gzip,而且不知道 AI 测试下 gzip 压缩等级是多少。构建后 615 KB,如果能做到按需加载,大小会指数级下降——全量加载 615 KB,按需加载则用多少加载多少。

 复制代码现在就完成按需分块方案(import.meta.glob + defineAsyncComponent)只加载用到的图标,而且将 import * as elIcons from '@element-plus/icons-vue' 的所有图标也注册为按需加载

结果失算了。花了 10 分钟、28k tokens,还是没搞定——这个确实有点复杂:

 复制代码构建成功。1760 个 chunk,Lucide 图标不再打入主包。Element Plus 图标由于在单个文件中,不能实现按需加载。

这一轮下来,得到两个结论:

  1. Element Plus 图标无法按需加载,那就按自己之前的方式,动态注册全部组件即可——官方文档有现成方案,几行代码的事,后续手搓。
  2. 1760 个 chunk 肯定不行。让 CC 配置分包规则减少 chunk 数量:能否配置打包的分包规则,减少 chunk 数量,这太多了
 复制代码● 理解,1737 个 chunk 部署时确实太多。最务实的方案:Lucide 图标合并为单个 lazy chunk,不在主包中,用到才加载。# ......● 这样 Rollup 将所有 Lucide 图标合并为一个 lazy chunk,配合 HTTP/2 一次性下载 128 KB,比 1737 个小文件逐个请求高效得多。

单个 chunk 128 KB,看起来在加载任意图标时又回到了全量加载。实际上想法是按图标前缀分组 chunk,但这种方案也很复杂且不完美。卡瓶颈了,只能再和 AI 多沟通,让它想想其他办法。切换到 plan mode:

 复制代码我需要真正的按需加载,且 chunk 不要那么多,是否还有其他办法?
 复制代码Lea vening… (7m 5s · ↓ 27.2k tokens)首次使用任意 Lucide 图标时一次性下载 128 KB (gzip),之后所有图标即时可用。这比首屏就加载 615 KB 要好得多,也比部署 1760 个文件实际可用得多。

看得出来 AI 自我感觉良好,觉得现在的方案非常好。但现实是,首屏一般就会用到 Lucide 图标,所以这 128 KB 会在首屏完成加载——懒加载功能就显得很多余了。

这时候可能有小伙伴会说:换图标库。有些图标库从第三方 CDN 按需加载,但那不是我们想要的方式。

整理现状和所有方案

确定遇到瓶颈了,让 AI 总结一下当前问题和可行方案:

 复制代码● Import 只是一个图标名,不是工具函数。所以 @lucide/vue 没有内置按需加载机制。
     
  ---
  核心矛盾在于 Vite 8 / Rolldown 的代码分割机制:
     
  - 每个 import() → 1 个独立 chunk(这是代码分割的基础,无法改变)
  - import.meta.glob → 为每个匹配文件生成一个 import() → 1737 个 chunk
  - manualChunks 在 Rolldown 中无法可靠合并这些 chunk(我们已验证过)
  - 单次 import() 且内部静态引用多个模块 → 1 个 chunk(barrel 方案的核心原理)  所以能达成"真正按需 + chunk 数量可控"的路径只有:  ┌──────────────────────┬──────────────────────────────────────────────────────────────┬──────────────────────────┬──────────────────────────────┐
  │         方案         │                             原理                             │         chunk 数         │           维护负担           │
  ├──────────────────────┼──────────────────────────────────────────────────────────────┼──────────────────────────┼──────────────────────────────┤
  │ A. Barrel 生成脚本    │ 脚本生成 5 个静态 re-export 文件,各一个 import()              │ 5                        │ 加一个生成脚本 + postinstall │
  ├──────────────────────┼──────────────────────────────────────────────────────────────┼──────────────────────────┼──────────────────────────────┤
  │ B. Vite 插件虚拟模块  │ 同 A,但不落盘,插件 load 钩子动态生成 barrel 内容              │ 5                        │ 加一个轻量 Vite 插件         │
  ├──────────────────────┼──────────────────────────────────────────────────────────────┼──────────────────────────┼──────────────────────────────┤
  │ C. 接受单 chunk       │ import('@lucide/vue')                                        │ 1                        │ 零                           │
  ├──────────────────────┼──────────────────────────────────────────────────────────────┼──────────────────────────┼──────────────────────────────┤
  │ D. 手动 import       │ 放弃按名动态使用,哪里用哪里 import { X } from '@lucide/vue'    │ 0(tree-shaking 到主包) │ 零                           │
  └──────────────────────┴──────────────────────────────────────────────────────────────┴──────────────────────────┴──────────────────────────────┘  B(Vite 插件) 最优雅——不需要落盘文件、不污染代码库、始终与 @lucide/vue 版本同步,只需一个 ~30 行的 Vite 插件。  你倾向哪个? Cogitated for 38s

后续还要做图标选择器组件,该组件需要所有 Lucide icon 的 name 并将图标渲染出来供用户选择,到头来还是全量加载。所以目前看来方案 B 是最佳选择。让 AI 实现:

 复制代码最终方案:Vite 插件 + 虚拟模块
     
  不需要生成脚本、不需要 postinstall、不需要落盘文件。全部逻辑在 vite.config.ts 中一个 ~40 行的自定义插件 lucideIconBatches()。  原理
 
  菜单配置写 "icon": 
    "Smile"                                                                                                                                                "Smile" />
         ↓
  iconLoader.ts → getBatchName('Smile') → 'q-s'
         ↓
  import('virtual:lucide-icons/q-s')   ← 动态 import,产生独立 chunk
         ↓
  Vite 插件 load 钩子 → 读取 node_modules,返回 315 个图标 re-export
         ↓
  Rolldown 将 315 个静态 re-export 打包为 1 个 chunk(95 KB / 24 KB gzip)  构建产物  ┌───────────────────────┬──────────────┬────────┬────────────────────┐
  │         Chunk         │  min / gzip  │ 图标数 │      触发条件      │
  ├───────────────────────┼──────────────┼────────┼────────────────────┤
  │ a-c-*.js              │ 140 / 36 KB  │ 514    │ 首字母 a–c         │
  ├───────────────────────┼──────────────┼────────┼────────────────────┤
  │ d-l-*.js              │ 124 / 33 KB  │ 424    │ 首字母 d–l         │
  ├───────────────────────┼──────────────┼────────┼────────────────────┤
  │ m-p-*.js              │ 73 / 20 KB   │ 262    │ 首字母 m–p         │
  ├───────────────────────┼──────────────┼────────┼────────────────────┤
  │ q-s-*.js              │ 95 / 24 KB   │ 315    │ 首字母 q–s         │
  ├───────────────────────┼──────────────┼────────┼────────────────────┤
  │ t-z-*.js              │ 62 / 18 KB   │ 223    │ 首字母 t–z         │
  ├───────────────────────┼──────────────┼────────┼────────────────────┤
  │ createLucideIcon-*.js │ 1.3 / 0.8 KB │ —      │ 公共依赖(仅一次) │
  └───────────────────────┴──────────────┴────────┴────────────────────┘  - 首屏:0 KB Lucide 代码
  - 使用 Smile:仅加载 q-s chunk(24 KB gzip)
  - 总 chunk 数:27(之前 1760)
  - 构建时间:1.08s  菜单配置用法  {
    "menus": [
      { "title": "仪表盘", "icon": "LayoutDashboard" },
      { "title": "用户管理", "icon": "Users" },
      { "title": "设置", "icon": "Settings" }
    ]
  }  改配置即可换图标,无需改任何代码。 Sautéed for 1m 47s

测试

对比数据准备

目前还没对最终方案的代码做正式 review,先测试一下是否达成需求。为了和未实现按需加载之前做对比,用 git stash 把当前工作区的全部改动暂存起来。

未加载任何图标前,浏览器禁用缓存,总是用同一 Vue 页面测试:

开发环境下:47 个请求,传输 4.7 MB,4.7 MB 资源。构建产物 assets 内共 17 个项目,1.55 MB、14 个请求,传输 1.6 MB,1.6 MB 资源。上 100 KB 的文件有三个:1078 KB、111 KB、370 KB。

方案数据

开发环境下:475 个请求,传输 5.4 MB,5.3 MB 资源。一个图标一个请求?先不测了,让 AI 改:将同一 Chunk 的 icon 放到一个文件可以吗?目前单 chunk 的请求数达到了 475 个!

 复制代码● 完美。每个 chunk = 1 个 HTTP 请求。
     
  a-c  140KB  0 个 import()  只有 1 个静态 import(共享 createLucideIcon)
  d-l  124KB  0 个 import()
  m-p   73KB  0 个 import()
  q-s   95KB  0 个 import()
  t-z   62KB  0 个 import()  根因与修复  export { default as X } from '...'(re-export)→ Rolldown 将其视为透传绑定,不内联模块,导致每个图标一个子请求。  import X from '...' + export const Name = X(显式绑定)→ Rolldown 视为本地依赖,必须内联到同一个 chunk 文件。  修改的就是 Vite 插件 load 函数中生成的代码格式,从一个 export from 变为 import + export const。其余代码(iconLoader.ts、Icon 组件)无需改动。 Crunched for 1m 24s

结果还是 475 个请求。开关 Vite 开发服务多次,确定数据无误。考虑到可能是开发环境的问题,但还是要改——大批量文件加载会让调试和日志变得很麻烦。提醒 AI 当前是开发环境。

 复制代码● 最终方案总结  开发模式 (pnpm dev):
    import('@lucide/vue') → 1 个请求,加载全部图标
    避免虚拟模块 475 个子请求  生产模式 (pnpm build):
    import('virtual:lucide-icons/a-c') → 1 chunk,1 请求,140KB / 38KB gzip
    import('virtual:lucide-icons/d-l') → 1 chunk,1 请求,127KB / 35KB gzip
    import('virtual:lucide-icons/m-p') → 1 chunk,1 请求,75KB / 21KB gzip
    import('virtual:lucide-icons/q-s') → 1 chunk,1 请求,97KB / 26KB gzip
    import('virtual:lucide-icons/t-z') → 1 chunk,1 请求,63KB / 19KB gzip
    createLucideIcon (共享)      → 1 chunk,1 请求,1.3KB  首屏 HTML:0 KB Lucide 代码
  使用 "Smile" />:触发 q-s chunk (26KB gzip) + createLucideIcon (0.8KB)  import.meta.env.DEV 是编译时常量,Vite 在构建时将无效分支完全删除,零运行时开销。

方案数据再次统计

开发环境下:51 个请求,传输 5.5 MB,5.5 MB 资源。构建产物 assets 内共 24 个项目,2.17 MB、17 个请求,传输 1.9 MB,1.9 MB 资源。上 100 KB 的文件多了一个 144 KB。

请求数合理了,请求大小也合理了,但又发现了一个大问题:图标根本没显示,空白。继续调整,幸好只是个图标大小写转换的小问题。修复后再测试:

逐一使用 a、d、m、q、t 开头的图标,确认以下文件是按需加载的,文件大小分布也比较合理——AI 还是行的:144 KB、127 KB、75.2 KB、97.3 KB、63.5 KB。

好了,宣布成功。后面再慢慢 review 吧,Icon 组件的核心难点已经完美解决了。

来源:https://juejin.cn/post/7659884568909053995

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
React中使用CSS变量实现组件样式动态隔离的方法

React中使用CSS变量实现组件样式动态隔离的方法

在React中,利用`data-id`属性和CSS属性选择器限定变量作用域,实现组件样式隔离,避免`:root`全局污染。运行时通过`ref`和`setProperty`仅更新当前组件根元素的CSS变量,确保变量仅在该组件DOM树内生效。

时间:2026-07-12 07:01
Canvas绘制带箭头的贝塞尔路径端点指向计算

Canvas绘制带箭头的贝塞尔路径端点指向计算

Canvas绘制带箭头贝塞尔曲线需计算终点切线方向,三次曲线方向为终点减第二个控制点。将向量单位化后旋转±30°,按固定长度延伸出箭头两顶点,再用路径API绘制实心三角形。注意箭头紧接曲线绘制,避免路径污染。

时间:2026-07-12 07:00
如何从零开始为Web Worker编写模块化脚本的完整指南

如何从零开始为Web Worker编写模块化脚本的完整指南

WebWorker模块化脚本利用ES模块与newURL实现零配置可复用结构;也可用Browserify或web-worker库兼容旧环境。需避免DOM操作、使用相对路径、仅通过postMessage通信,由构建工具自动打包。此方式提升代码复用性,避免全局污染,提高开发效率。

时间:2026-07-12 07:00
CSS网格布局中grid-auto-flow自动流向属性详解及实现方法

CSS网格布局中grid-auto-flow自动流向属性详解及实现方法

grid-auto-flow控制未手动定位元素的填充方向,非自动对齐开关,需配合grid-template等定义轨道生效。dense模式回填空隙但破坏DOM顺序与视觉一致性。实际切换流向应直接重写整套网格定义,默认按行填充,理解这些避免布局混乱。

时间:2026-07-12 07:00
Vue.js处理大量异步请求的高效调度方案

Vue.js处理大量异步请求的高效调度方案

处理Vue js中大量异步请求的调度方案包括:并行聚合(Promise all)处理独立请求,顺序链式(async await)处理依赖请求,可控并发(分批控制)平衡效率与负载,请求队列实现强管控如令牌续期和防重复提交。

时间:2026-07-12 06:59
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 热门数据榜