自制个人技能管理工具:从技能盘点到数据可视化实战
最近我花了两周时间把一个拖了很久的想法落地了项目起名就叫skills。它不是什么颠覆性平台也不是一个在线课表而是一个完全给自己用的个人技能管理工具。我做它的原因很朴素作为一个什么都在学、什么都想沾一点的开发者我发现自己根本没有一张“清楚的技术能力地图”。简历上写的技能是模糊的脑子里的知识是碎片化的每隔一阵子都要花很长时间回忆最近到底学了什么、哪些已经生疏、下一步该补什么。真正让我下决心动手的是一次面试前的自我梳理我打开一个空白文档准备列技能清单结果写了两行就卡住了。这个场景我相信很多人经历过。市面上的学习工具要么是课程播放器要么是打卡软件能把你学过的内容、熟练度、计划、复盘放在一起并且用数据可视化的方式让你一眼看到差距的工具几乎没有。所以我没有再等现成方案而是自己动手做了一个。这个项目的核心就是四个字技能盘点。它解决的是“我到底会什么、不会什么、接下来学什么”这个问题适合正在积累技术栈的开发新人、想转行的学习者以及任何被“知识焦虑”困扰但不知道从哪开始的人。下面我把这个项目的设计和实现过程完整拆开包括我怎么定需求、怎么设计数据表、怎么写后端、怎么画图表以及踩过哪些坑。1. 为什么我会亲手写一个“skills”系统1.1 需求源头技能太多盘点不过来我给自己做过很多次年度学习计划但基本没坚持过。原因是计划里的“学会 Vue”“精通 Java”这种条目本身就没有可衡量性。三个月后回头看我根本不知道自己是“会”还是“不太会”更不知道投入的时间是否对得起产出。我想象中的技能管理应该是像游戏角色面板那样每个技能有等级、有经验条、有树状依赖学到什么程度清清楚楚。现实是大部分人的技能都被塞在一堆文件夹、书签、收藏夹和笔记里没有结构。后来我换了个思路不用追求一劳永逸的庞大技能树先做一个能快速录入、评估、查看差距的最小系统。我给自己定下第一个目标——每次打开这个系统我能在三十秒内回答三个问题我现在有哪些技能每个技能处于什么水平我最近应该在哪个方向上投入时间1.2 现有方案到底缺什么市面上不是没有类似产品。有些在线学习平台有技能图谱但那是平台定义的不是针对个人的。有些简历工具有技能标签但只面向求职没有成长视角。还有些笔记软件支持标签和关系图但维护成本太高记着记着就变成了蹩脚的数据库。我真正需要的是一个足够简单的数据库技能主表、评估记录、学习投入记录再配上几个直观的图表。这个需求听起来很简单但通用工具为了兼容所有人往往把流程做复杂了反而让我不想打开。另外我还想要一个“不看广告、不搞社交、没有课程推销”的纯粹工具。自己写的好处就是所有功能都按自己的习惯来坏处是得自己维护。但作为一个开发者这不正好是练手的机会吗我把它看作一个长期演进的个人项目而不是一定要求多完善。1.3 我确认的核心需求清单动工前我列了一个优先级列表原则是“先做能做出来的再做想做的”。技能管理支持新增、编辑、删除技能每个技能属于一个分类分类如前端、后端、数据库、工程效能等。等级评估给每个技能设定一个当前等级和目标等级等级定义要具体不能含糊。学习记录记录每次投入的时间、内容和心得让“学过”有迹可循。数据可视化用雷达图展示多维能力对比用热力图展示近期投入用条形图展示当前和目标差距。数据导入导出支持 JSON 备份避免平台锁定。至于提醒、多人协作、简历生成我放到后面版本再做。第一个版本做得足够简单才能让我坚持使用下去。2. 设计一套不过度的数据模型2.1 技能分类从“技能树”到“技能森林”最初我参考了很多技能树图谱比如把某个语言细分为异步编程、内存管理、多线程等树可以很深。但真正用起来会发现超过三层的树维护成本极高。如果你今天学了个小众工具你很难给它定位到准确的二级分类下结果就是到处找地方放最后干脆不录。我最终采用了扁平分类加标签的方案技能只属于一个分类但可以打多个标签。分类数量控制在十五个以内例如前端、后端、数据库、运维、算法、力、项目管理、软技能等。标签则自由得多比如“面试高频”“近期在用”“兴趣方向”。这样设计的好处是录入技能时的认知负担很小。新增一条技能记录只需要填名称、选分类、添加标签整个过程不到十秒。同时查询和筛选也很方便按分类看按标签看或者直接搜关键词。树结构虽然看起来性感但对个人盘点来说森林比树更合适每棵小树代表一个领域技能之间是松散关联而不是严格的父子依赖。2.2 等级评估把自己当作一个“角色”技能等级是主观的所以必须定义清楚否则自己和自己都容易产生分歧。我参考了很多游戏和评估体系的描述最终定了一个六级量表等级名称行为特征0未接触看过名字不知道具体内容1了解知道基本概念能说清是什么但没做过项目2入门做过简单的练习或小项目能照着文档写3熟练独立完成中等规模任务能解决常见问题4精通能深入原理能优化方案能指导别人5专家能定义最佳实践能推动团队/社区认知对每一项技能我要求自己写一句“达到该等级的证据”比如达到 Level 3 的 Vue证据是“独立开发过企业内部中后台系统并通过代码评审”。这句话很重要因为人很容易高估自己只有把证据写下来等级才经得起回顾。半年后你再来看如果证据站不住那就是真的退步了。除了当前等级和目标等级我还加了一个字段“最近使用频率”选择值包括每天、每周、每月、偶尔、几乎不用。这个字段用于筛选“生疏技能”也用于决定优先级高频使用但等级不高说明有实战机会应该继续深入低频但目标等级高说明缺少场景需要刻意安排。2.3 学习投入与状态机技能表只是静态快照真正让系统“活”起来的是学习记录。我专门建了一张表每次学习不超过一条记录字段包括技能 ID、日期、时长分钟、学习内容简述、状态类型。这里的状态类型我设计成一个简单的枚举理论学习、动手实践、复盘总结、没学但复习。四种状态基本能覆盖日常学习行为。为什么需要这个状态因为“学两个小时”和“练两个小时”的含金量完全不同。复盘的时候我可以用图表看自己有多少时间真正花在敲代码和调试上而不是只看视频。我最初还想给技能加一个“掌握度百分比”后来发现百分比和等级是重复的。于是把百分比改成进度条的动态值它由当前等级除以目标等级计算得出。比如当前等级 2目标等级 4进度就是 50%。这样就不用手动维护两个数值也避免了主观上的自欺欺人。3. 从零实现后端、前端与数据可视化3.1 技术栈选择和初始化个人项目最忌讳过度架构。我没有用 Kubernetes也没拆微服务更没上消息队列。最终选型是后端用 Node.js 和 Express数据库用 SQLite前端用 Vue 3 和 Element Plus图表用 ECharts。选这些主要是图省事而且生态成熟。Node.js 写起来轻快SQLite 不需要单独部署适合个人项目。Vue 3 的组合式 API 写起来顺手Element Plus 组件库能快速把表单和表格搭建起来。ECharts 则是目前最灵活的开源图表库雷达图、热力图、柱状图都有成熟示例。初始化项目时我分成了两个目录server和web。server只负责 APIweb负责页面展示。开发时用 Vite 做代理把/api的请求转发到后端。这里有个小坑如果你用的是 WindowsSQLite 的 native 模块可能需要重新编译建议直接用better-sqlite3它是同步 API性能足够而且安装简单基本不存在编译问题。3.2 后端API最小闭环我设计的第一版 API 非常简单只有五组GET /api/skills获取技能列表支持分类、标签、关键词筛选POST /api/skills新增技能PUT /api/skills/:id更新技能DELETE /api/skills/:id删除技能POST /api/records新增学习记录GET /api/stats/radar获取雷达图数据后端代码结构分成routes、controllers、services三层。虽然只有几组接口但分层能让自己后续扩展时不用重写。下面是一个简化版的新增技能接口示例// routes/skills.js const express require(express); const router express.Router(); const skillService require(../services/skillService); router.post(/, async (req, res) { try { const { name, category, tags, level, targetLevel, evidence, frequency } req.body; if (!name || !category) { return res.status(400).json({ error: name 和 category 不能为空 }); } const skill await skillService.createSkill({ name, category, tags, level, targetLevel, evidence, frequency }); res.status(201).json(skill); } catch (err) { res.status(500).json({ error: err.message }); } }); module.exports router;这段代码朴实无华但很实用。我在写的时候特别注意了字段校验因为个人项目很容易忽略入参合法性结果就是前端报错时难以排查。tags字段我记得存成 JSON 字符串在数据库里是TEXT类型读取时再解析这样避免建关联表带来的复杂度。数据库表结构我用 SQL 初始化的方式没有引入 ORM。核心表两条CREATE TABLE IF NOT EXISTS skills ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, category TEXT NOT NULL, tags TEXT DEFAULT [], level INTEGER DEFAULT 1, target_level INTEGER DEFAULT 3, evidence TEXT DEFAULT , frequency TEXT DEFAULT 偶尔, created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS learning_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, skill_id INTEGER NOT NULL, date TEXT NOT NULL, duration INTEGER DEFAULT 0, content TEXT DEFAULT , type TEXT DEFAULT 理论学习, created_at TEXT DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (skill_id) REFERENCES skills(id) );用 ORM 能节省一些代码但个人项目用 SQL 直接操作反而更可控。尤其是当你需要写统计查询比如按月份分组算总时长时一段 SQL 比 ORM 链式调用清晰得多。3.3 前端页面和交互细节前端我做了三个页面技能总览、技能详情、数据仪表盘。技能总览是一个表格默认按分类排序每行显示技能名、分类、标签、当前等级、目标等级、进度条以及最后学习时间。每行右边有一个“记一笔”按钮点击会弹出对话框录入学习时长和内容。这个交互是使用频率最高的所以一定要减少操作步骤从点击按钮到保存成功我控制在五次点击以内。技能详情页展示单个技能的所有信息包括历史学习记录以及一个小的趋势图。趋势图是按月统计的学习时长柱状图可以直观看到这个技能是否被冷落。详情页还有一个编辑区域可以修改等级、目标和证据。每次修改后我会提醒自己更新证据否则等级容易失真。写前端的时候有几个值得注意的体验细节。第一技能名称输入框要做重复校验后端也要查重否则技能列表里会出现两个“Docker”统计就会乱。第二分类建议从预设下拉框里选同时允许输入新分类但新增的分类要提示用户确认防止手滑造出一堆同义分类。第三进度条颜色可以按照剩余差距来区分差距小于等于 1 显示绿色等于 2 显示橙色大于 2 显示红色。这样一眼就能看出哪些技能是短板。3.4 数据可视化雷达图与热力图仪表盘是整个项目最让我上瘾的部分。雷达图用来对比分类维度的能力值而不是单个技能。因为单个技能比较太难且情绪化但“后端能力”“前端能力”“云原生能力”这些维度对比起来很有启发。我写了一个统计函数取某个分类下所有技能当前等级的平均值作为该维度的得分然后输出到雷达图。目标等级也做一份同图叠加差距一目了然。举个例子我把技能分成六个维度前端、后端、数据库、DevOps、算法、软技能。雷达图上当前等级围成的多边形跟目标等级围成的多边形之间会产生缺口哪边缺口大哪边就是最近的发力点。这个视觉冲击比任何表格都强。原本我总觉得自己算法还行一上图发现算法维度只有 1.5 分而目标 3.5 分直接就排进了下个月的优先级首位。热力图我用来展示最近九十天的学习活跃度。横轴是星期纵轴是第几周色块深浅表示当天的学习总时长。这个设计参考了 GitHub 的贡献图但用 ECharts 里的 calendar 坐标系实现。每天学习不足三十分钟的格子是浅灰色超过一个小时是深蓝色。热力图非常诚实一旦连续两周都是浅灰色看着就慌于是会逼着自己至少保持一点学习节奏。4. 实操中的问题与避坑盘点4.1 不要把技能树做成无限深的树我最初一版设计过parent_id想做技能之间的依赖关系比如“JavaScript”依赖“HTML/CSS”“Promise”依赖“异步编程”。结果录技能的时候我发现自己要先想清楚这个技能挂在哪个父节点下成本太高录了几条就烦了。后来我删掉了父节点字段只保留分类和标签。依赖关系不是不重要而是不适合作为日常输入模型。如果你想表达“需要先会 X 才能学 Y”可以在技能详情里加一个“前置技能”文本字段输入技能名即可系统不强校验。这既能保留参考信息又不影响录入效率。4.2 自评等级别搞得太复杂我一开始设计过十级的量表后来又想过用百分比但实际体验是等级多了人在评估时反而更纠结。你很难说清楚自己是“7级”还是“8级”但“熟练Level 3”和“精通Level 4”之间就比较好判断。所以最终固定六级并且强制填写行为特征作为证据。实操中我发现很多人的技能等级评定不是“学得不够”而是“定义不清楚”。所以这里有个小经验每次调整等级时一定要同步调整证据描述。如果证据已经支撑不了这个等级就把等级降下来这反而让人更踏实地看到自己真实水平。4.3 数据备份和同步要提前想个人项目最容易忽视的是数据安全。我用 SQLite 数据库默认就是单文件备份起来很简单。我的方案是每天定时把data/skills.db复制一份到备份目录同时每周导出一份 JSON 文件存到云盘。这里的坑是SQLite 的 WAL 模式下直接复制文件可能漏掉尚未落盘的数据。我在备份前会执行一次sqlite3.db.exec(PRAGMA wal_checkpoint(FULL))确保把 WAL 日志合并到主文件里。你要是也打算用 SQLite这一点值得留意。4.4 冷启动后的坚持问题工具做得再好不坚持使用也白搭。我前两周的使用频率很高第三周开始下降原因是没有形成回看数据的习惯。后来我给仪表盘加了一个“上周学习总时长”的数字放在首页最顶部让自己每次打开先看到这个数字。这看起来逼格不高但极其有效。当你的目标是“每周不低于三小时”看到一个 2.5 小时就会不由自主想补一点。现在我已经连续两个月每天都打开一次不是为了打卡而是因为想看数据变化。5. 这只是开始skills 还能怎么延伸5.1 和简历生成打通技能数据积累到一定程度最自然的落地场景就是简历。我现在已经在想能不能把技能列表按“当前等级大于等于 3”筛选自动生成一段“个人技能”描述。再加一个“项目经历”模块把学习记录里面 type 为“动手实践”的记录汇总成项目描述。这样简历不再是临阵磨枪而是平时数据的副产品。虽然自动化写的句子有点死板但至少能作为初稿。5.2 加入目标拆解和提醒另一个明显的改进是目标管理。现在每个技能只有目标等级但没有下一步行动。我计划给每个技能增加一个“下一步行动”字段并支持设置计划日期。系统每天启动时把今天要做的技能学习任务列出来。这其实是把项目管理系统里的 OKR 思路移植过来目标定等级拆解到具体技能再落到每一个学习动作上。5.3 多端同步与分享目前我只在电脑上使用但很多时候在地铁上想记一笔没有移动端就不方便。我的计划是加一个 Telegram Bot 或者极简的移动端 PWA实现快速录入。另外我还想做一个分享页可以把雷达图和技能列表以只读的方式分享给朋友或同学接受他们的建议。这样既不会公开自己太弱又能获得外部反馈。最后再分享一个我这段实操里体会最深的事个人技能管理系统不是一个“做完了就结束”的项目它更像一个长期的数字花园。你要不断调整它的结构让它适配自己的真实习惯而不是反过来让生活去适应工具。工具本身的价值不在代码多炫、界面多漂亮而在于你每一次打开它都能看到自己在哪个方向是空转哪个方向在加速。对我来说skills 已经从一个练手项目变成了日常学习的基础设施。如果你也有类似的信息焦虑不妨试着从一张技能清单开始不用写代码先用表格记录一个月你会发现自己真正需要深入的领域可能跟你想象的不太一样。