---
title: "啵啵音乐迎来全新升级！ - Angryecho"
description: "版权声明：本人文章仅在掘金平台发布，请勿抄袭搬运，转载请注明作者及原文链接 🦉 阅读提示：网页版带有主题和代码高亮，阅读体验更佳 🍉 废话不多说，先来一波截图纯享。"
canonical_url: "https://www.angryecho.com/c/qian_duan_she_qu/p/P2TBByen31i36te313Alc"
meta:
  "og:description": "版权声明：本人文章仅在掘金平台发布，请勿抄袭搬运，转载请注明作者及原文链接 🦉 阅读提示：网页版带有主题和代码高亮，阅读体验更佳 🍉 废话不多说，先来一波截图纯享。"
  "og:image": "https://www.angryecho.com/_og/d/c_Post%2Ctitle_~5ZCQ5qe9572R%2Cdescription_~5ZCQ5qe95LiA5LiL77yM5bCx5LiK5ZCQ5qe9572R44CC%2CpostId_P2TBByen31i36te313Alc%2Crevision_~MjAyNi0xMS0wMVQwMjo1NTo1Ny40MDAxNzda%2Cp_Ii9jL3FpYW5fZHVhbl9zaGVfcXUvcC9QMlRCQnllbjMxaTM2dGUzMTNBbGMi%2Cs_iNhiZhyQiRShv6tb.png"
  "og:image:height": "630"
  "og:image:type": image/png
  "og:image:width": "1200"
  "og:title": 啵啵音乐迎来全新升级！
---

> Index of every page on this site: [llms.txt](https://www.angryecho.com/llms.txt)

# **啵啵音乐迎来全新升级！**

> 版权声明：本人文章仅在掘金平台发布，请勿抄袭搬运，转载请注明作者及原文链接 🦉
>
> 阅读提示：网页版带有主题和代码高亮，阅读体验更佳 🍉

废话不多说，先来一波截图纯享。

| 标题 |  | |
| --- | --- | --- |
| 2ec237a422ede164c18a09972186c686.jpg | 093617a5b0a68fe8fa1e3f9de78f9836.jpg | 45db506c797969687944a841717bc1cd.jpg |
| d8d681fb94a4e53ef23fd35692fc3bf5.jpg | b63918db8014e34f973b6bf7cc196698.jpg | 653f01a0edca58f34b644493231acea9.jpg |

## 一、动因

两年前写上篇文章的时候，啵啵音乐刚过 1.0。之后一路迭代，今年五月更到 2.7.0，然后就停更了。

不是不想动，是不敢动。

这个项目是我边学 flutter 边写的，第一版什么成色，我心里有数 —— 能跑，但内里全是欠条。用得越久，欠条越多。随手列几条给大家品品：

**1.缓存的歌整首驻留内存，播放进度靠 300ms 轮询刷**

**2.歌词时间轴的单位解析错了，滚动节奏永远对不上，比歌声慢十倍**

**3.启动的时候，一边把待播放列表清空，一边又去读它，主打一个精神分裂**

**4.SQL 里拼表名，删除逻辑宽匹配，一不小心就误删歌曲**

**5.event\_bus 满天飞，UI 直接读数据库，同一个状态三处各存一份**

**6.任何状态一变，notifyListeners 全量通知，整棵组件树陪跑**

这就是 2.x 的现状。功能都能用，我自己也一直在用，但每次想加个新功能，都得先在欠条堆里刨半天。

我也认真考虑过渐进重构，方案都写到 P5 了。写到一半发现不对劲：地基是歪的，装修救不了。旧代码没有任何测试保护，改一处崩三处，重构的成本不比重写低，收益还不如重写。

而且这次和两年前不一样了 —— 两年前我是 flutter 新手，现在好歹算个熟手；另外，AI 参与开发的方式也完全成熟了（这个后面细说）。

于是在某个深夜，我把 master 冻结存档，切了个新分支，然后把 lib/ 下的代码基本删光了。

对，删光了。

## 二、决策

删代码一时爽，但有几个决策得先想清楚，不然就是自杀式重写。

**1.留什么？**

只留一样东西：B 站音源的 SDK。这是整个项目最值钱的资产 —— WBI 签名、风控票据、playurl 解析，全是当初一点点逆向出来的心血，重造不起。接入前做了番改造：剥掉和 UI 的耦合、错误类型化，再补了一轮特征测试（签名黄金值、解析快照），保证改造前后行为分毫不差。

其余全部推倒。旧代码的去处是 git 历史和文档，不做长期双轨。

**2.状态管理用什么？**

这里要先给两年前吹过的牛磕一个：上篇文章结尾我说，"下次开发我必然用上 getx"。

这次食言了，用的是 Riverpod 3。

原因也想清楚了：getx 是把状态、路由、依赖注入塞进一个袋子的全家桶，爽是爽，但黑魔法太多。而 2.x 用 provider + event\_bus 的组合把我坑惨了，我这次要的是"编译期就报错"和"精确刷新" —— Riverpod 的 codegen 正好对症，某个歌单变了就失效那一个 provider，不再全量刷新整棵树。event\_bus 直接禁用，跨模块通信只准走 provider 依赖图。

**3.版本号怎么定？**

旧版已经发到 2.7.0，重写版从 3.0.0 起步，分支就叫 v3，对得起"第三代"这个名字。

**4.旧数据迁不迁？**

纠结过，迁移方案都写好了。最后还是砍掉：新旧数据库的表结构完全不同，硬迁移很容易把旧坑带进新实现。2.7.0 留档在 master，新旧互不干扰。

## 三、设计

这次我学乖了，动手前先写了一份重写计划，把功能清单、数据模型、状态域、播放内核一条条定死，其中光功能语义就有九条决策。举个 🌰：

**1.曲目和歌单成员是两个概念**：一首歌全局只有一个身份，可以出现在 N 个列表里。"喜欢"、歌词、下载标记都是歌本身的属性，天然全局同步，不需要任何同步代码。你在我喜欢里点了心形，歌单里同一首歌自动是红的——不是做了同步，是它本来就只有一份。

**2.播放队列独立于歌单**：插队、追加、允许重复、可调序，跨启动保留。

**3.本地歌曲的身份用内容指纹**：拿"文件大小 + 时长 + 归一化的标题歌手"生成指纹当主键，重扫一百遍，你的喜欢和歌词也不会丢。

数据库就四张表，SQL 全参数化，Repository 是唯一允许碰 SQL 的层。UI 想读库？没门，走 provider。

播放内核是重头戏，2.x 那套"整首进内存 + 300ms 轮询"必须死。新的方案是一个自定义的代理音频源：

**1.Range 请求直取 CDN，边下边播，顺序流边播边落盘，播完自动转正为缓存**

**2.缓存按歌曲身份做 key，LRU 淘汰，上限 2GB**

**3.主链挂了自动降级备用直链，15 秒没动静看门狗直接介入**

**4.断点续播：歌曲、进度、播放模式、队列全部落盘，冷启动恢复**

调这块的时候踩了个很阴的坑：播放一直没声音，日志也不报错。排查半天发现是看门狗的 Timer 声明了 late 但没初始化，空指针异常被上层的 catch 吞掉了，流程悄悄断了，落盘一个 0 字节的流。从那以后我对 "静默的 catch" PTSD 了。

系统媒体这块换了路子。audio\_service 用下来，媒体通知的样式定制被模板天花板死死挡住，想要的样子根本画不出来，最后干脆整个换掉，自研了一个轻量媒体中心：Kotlin 和 Swift 各写一层薄桥，通知自己画，锁屏、控制中心、耳机线控全接。想画成什么样就画成什么样，再也不用跟别人的抽象搏斗。

音源从单一 B 站扩到了五个：B 站、酷我、网易、QQ、千千，搜索页一键切换。歌词做成三级检索链：站内直连 → 开放歌词库 → 备选源，自动匹配不到就手动搜，还支持时间校准。

本地扫描也重做了：后台 isolate 扫描，进度走流式事件，页面上一边扫一边出结果。UI 上则定了一整套设计系统——颜色全部走 token，全项目禁止硬编码色值；背景是"极光玻璃"风，三颗光斑慢速漂移加呼吸脉动，用缓存图层加位移实现，合成器零重绘；空状态统一换成平面矢量插画。

## 四、聊聊这一周

实际的施工从删库那天算起，到打出正式包定版，一共七天，108 个 commit。

前面的方案设计和 UI 原型打磨不算在内——那部分花的时间比施工长。慢设计，快施工，这笔账是算得过来的。

这周的开发体验和两年前写 1.x 时完全是两个世界，最大的变量是 AI。

两年前的模式是：我边学边写，卡住了找 AI 救火，AI 给的代码经常跑不起来，因为它推荐的库基本都断更了。这次反过来：我先把架构决策写成文档，给 AI 立好规矩——跨模块只准走 provider、SQL 全参数化、颜色只准走 token、静态检查零 issue 加测试全绿才准交付——然后让 AI 按图施工，我做方案决策、代码评审和真机验收。

规矩之外还有记忆：每个模块踩过的坑、修过的 bug、模块之间的关系，都沉淀成文档，下次开工先读记忆再动手。等于给项目配了一个不会离职、还过目不忘的同事。

当然，AI 也不是银弹。播放内核这种高度依赖真机行为的部分，AI 写的代码必须一条条用真机验。比如 B 站视频失效返回 -404，这种只有线上才知道的事，文档里不会写，AI 也编不出来，只有真机能说话。

测试这次是当交付门槛用的：五源各配了假实现，播放引擎有替身，切歌策略和状态机时序都有矩阵测试，全部加起来一百七十多例。2.x 是零测试，所以它才敢腐化。

最后必须吐槽一下构建环境，这七天里最脏的活其实是它：

**1.工具链整体升级，132 个依赖 major 升级，AGP 9、Gradle 9.3.1、JDK 17 一步到位**

**2.AGP 9 砍了 Jetifier，但一堆插件的 Kotlin 版本跟不上，连锁报错；某个知名分享插件的新版本在 AGP 9 下编译丢类，只能锁死在旧版本窗口**

**3.NDK 想钉旧版？直接被拒，必须 28.2**

**4.本机 JAVA\_HOME 指着 JDK 8，只能在构建脚本里再钉一遍 17**

**5.首次构建各种下载超时，满屏报错看着像全完了，其实重试就好**

这些坑没有一个是业务逻辑，但每一个都能让你卡一晚上。好在这次有 AI 陪着排错，两年前那种"环境问题耗三天"的剧情没有重演。

## 五、结语

一周时间，从删库到 3.0.0：五个音源、三级歌词链、自研的播放内核和系统媒体桥、一百七十多例测试。两年前"免费听歌、无广告"的初心没变，变的只是里子。

重写最大的感受是：欠的债总是要还的，早还早超生。删代码的那天，是真的爽。

想下载体验的朋友请访问 GitHub Releases： [啵啵音乐](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FRedstone-1%2Fbobomusic%2Freleases "https://github.com/Redstone-1/bobomusic/releases")。

想看源码的朋友请访问 github 仓库： [bobomusic](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2FRedstone-1%2Fbobomusic "https://github.com/Redstone-1/bobomusic")。喜欢的话给个 star，谢谢。

## 往期推荐

[搭建一个快速开发油猴脚本的前端工程](https://juejin.cn/post/7437887483259584522 "https://juejin.cn/post/7437887483259584522") `18+` 👍🏻 `28+` 💚

[金九银十招聘季，IT 打工人，该怎么识别烂公司好公司？](https://juejin.cn/post/7272395084259377212 "https://juejin.cn/post/7272395084259377212") `70+` 👍🏻 `80+` 💚

[为什么就这个文件的 ESLint 检查失效了？](https://juejin.cn/post/7266672372808089659 "https://juejin.cn/post/7266672372808089659")

[学会 TypeScript 体操，轻松看懂开源项目代码](https://juejin.cn/post/7263852533142618149 "https://juejin.cn/post/7263852533142618149")

[别人休息我努力，悄悄写个 cli 工具，必须提升效率，skr\~](https://juejin.cn/post/7256702654579310652 "https://juejin.cn/post/7256702654579310652") `60+` 👍🏻 `110+` 💚

[一文掌握 eslint，再也不怕项目报错](https://juejin.cn/post/7254476003065020473 "https://juejin.cn/post/7254476003065020473") `20+` 👍🏻 `30+` 💚

[开发一个 npm 库应该做哪些工程配置？](https://juejin.cn/post/7240461753934889021 "https://juejin.cn/post/7240461753934889021") `40+` 👍🏻 `50+` 💚

[分享我在前端学习与开发中用到的神仙网站和工具](https://juejin.cn/post/7237065904781508645 "https://juejin.cn/post/7237065904781508645") `40+` 👍🏻 `110+` 💚

[uniapp 踩坑记录（二）](https://juejin.cn/post/7229899772768370725 "https://juejin.cn/post/7229899772768370725") `130+` 👍🏻 `150+` 💚

[闲来无事，摸鱼时让 chatgpt 帮忙，写了一个 console 样式增强库并发布 npm](https://juejin.cn/post/7216182763237916729 "https://juejin.cn/post/7216182763237916729") `100+` 👍🏻 `110+` 💚

[uniapp 初体验踩坑记录](https://juejin.cn/post/7208071684590452773 "https://juejin.cn/post/7208071684590452773") `30+` 👍🏻 `60+` 💚

[两小时学会 JS 正则表达式，终身不忘](https://juejin.cn/post/715094547376583470 "https://juejin.cn/post/715094547376583470")

排序方式 :

### **成为第一条评论！**

Nobody's responded to this post yet. Add your thoughts and get the conversation going.

```json
{"@context":"https://schema.org","@graph":[{"@id":"https://www.angryecho.com/c/qian_duan_she_qu/p/P2TBByen31i36te313Alc#post","@type":"DiscussionForumPosting","commentCount":0,"dateModified":"2026-11-01T02:55:57.400Z","datePublished":"2026-10-31T12:32:07.923Z","description":"版权声明：本人文章仅在掘金平台发布，请勿抄袭搬运，转载请注明作者及原文链接 🦉 阅读提示：网页版带有主题和代码高亮，阅读体验更佳 🍉 废话不多说，先来一波截图纯享。","headline":"啵啵音乐迎来全新升级！","isPartOf":"https://www.angryecho.com/c/qian_duan_she_qu","mainEntityOfPage":"https://www.angryecho.com/c/qian_duan_she_qu/p/P2TBByen31i36te313Alc","text":"版权声明：本人文章仅在掘金平台发布，请勿抄袭搬运，转载请注明作者及原文链接 🦉\n\n阅读提示：网页版带有主题和代码高亮，阅读体验更佳 🍉\n\n废话不多说，先来一波截图纯享。\n\n\n\n\n\n\n\n标题\n\n\n\n\n\n\n\n\n\n\n\n\n\n2ec237a422ede164c18a09972186c686.jpg\n\n\n\n093617a5b0a68fe8fa1e3f9de78f9836.jpg\n\n\n\n45db506c797969687944a841717bc1cd.jpg\n\n\n\n\n\nd8d681fb94a4e53ef23fd35692fc3bf5.jpg\n\n\n\nb63918db8014e34f973b6bf7cc196698.jpg\n\n\n\n653f01a0edca58f34b644493231acea9.jpg\n\n一、动因\n\n两年前写上篇文章的时候，啵啵音乐刚过 1.0。之后一路迭代，今年五月更到 2.7.0，然后就停更了。\n\n不是不想动，是不敢动。\n\n这个项目是我边学 flutter 边写的，第一版什么成色，我心里有数 —— 能跑，但内里全是欠条。用得越久，欠条越多。随手列几条给大家品品：\n\n1.缓存的歌整首驻留内存，播放进度靠 300ms 轮询刷\n\n2.歌词时间轴的单位解析错了，滚动节奏永远对不上，比歌声慢十倍\n\n3.启动的时候，一边把待播放列表清空，一边又去读它，主打一个精神分裂\n\n4.SQL 里拼表名，删除逻辑宽匹配，一不小心就误删歌曲\n\n5.event_bus 满天飞，UI 直接读数据库，同一个状态三处各存一份\n\n6.任何状态一变，notifyListeners 全量通知，整棵组件树陪跑\n\n这就是 2.x 的现状。功能都能用，我自己也一直在用，但每次想加个新功能，都得先在欠条堆里刨半天。\n\n我也认真考虑过渐进重构，方案都写到 P5 了。写到一半发现不对劲：地基是歪的，装修救不了。旧代码没有任何测试保护，改一处崩三处，重构的成本不比重写低，收益还不如重写。\n\n而且这次和两年前不一样了 —— 两年前我是 flutter 新手，现在好歹算个熟手；另外，AI 参与开发的方式也完全成熟了（这个后面细说）。\n\n于是在某个深夜，我把 master 冻结存档，切了个新分支，然后把 lib/ 下的代码基本删光了。\n\n对，删光了。\n\n二、决策\n\n删代码一时爽，但有几个决策得先想清楚，不然就是自杀式重写。\n\n1.留什么？\n\n只留一样东西：B 站音源的 SDK。这是整个项目最值钱的资产 —— WBI 签名、风控票据、playurl 解析，全是当初一点点逆向出来的心血，重造不起。接入前做了番改造：剥掉和 UI 的耦合、错误类型化，再补了一轮特征测试（签名黄金值、解析快照），保证改造前后行为分毫不差。\n\n其余全部推倒。旧代码的去处是 git 历史和文档，不做长期双轨。\n\n2.状态管理用什么？\n\n这里要先给两年前吹过的牛磕一个：上篇文章结尾我说，\"下次开发我必然用上 getx\"。\n\n这次食言了，用的是 Riverpod 3。\n\n原因也想清楚了：getx 是把状态、路由、依赖注入塞进一个袋子的全家桶，爽是爽，但黑魔法太多。而 2.x 用 provider + event_bus 的组合把我坑惨了，我这次要的是\"编译期就报错\"和\"精确刷新\" —— Riverpod 的 codegen 正好对症，某个歌单变了就失效那一个 provider，不再全量刷新整棵树。event_bus 直接禁用，跨模块通信只准走 provider 依赖图。\n\n3.版本号怎么定？\n\n旧版已经发到 2.7.0，重写版从 3.0.0 起步，分支就叫 v3，对得起\"第三代\"这个名字。\n\n4.旧数据迁不迁？\n\n纠结过，迁移方案都写好了。最后还是砍掉：新旧数据库的表结构完全不同，硬迁移很容易把旧坑带进新实现。2.7.0 留档在 master，新旧互不干扰。\n\n三、设计\n\n这次我学乖了，动手前先写了一份重写计划，把功能清单、数据模型、状态域、播放内核一条条定死，其中光功能语义就有九条决策。举个 🌰：\n\n1.曲目和歌单成员是两个概念：一首歌全局只有一个身份，可以出现在 N 个列表里。\"喜欢\"、歌词、下载标记都是歌本身的属性，天然全局同步，不需要任何同步代码。你在我喜欢里点了心形，歌单里同一首歌自动是红的——不是做了同步，是它本来就只有一份。\n\n2.播放队列独立于歌单：插队、追加、允许重复、可调序，跨启动保留。\n\n3.本地歌曲的身份用内容指纹：拿\"文件大小 + 时长 + 归一化的标题歌手\"生成指纹当主键，重扫一百遍，你的喜欢和歌词也不会丢。\n\n数据库就四张表，SQL 全参数化，Repository 是唯一允许碰 SQL 的层。UI 想读库？没门，走 provider。\n\n播放内核是重头戏，2.x 那套\"整首进内存 + 300ms 轮询\"必须死。新的方案是一个自定义的代理音频源：\n\n1.Range 请求直取 CDN，边下边播，顺序流边播边落盘，播完自动转正为缓存\n\n2.缓存按歌曲身份做 key，LRU 淘汰，上限 2GB\n\n3.主链挂了自动降级备用直链，15 秒没动静看门狗直接介入\n\n4.断点续播：歌曲、进度、播放模式、队列全部落盘，冷启动恢复\n\n调这块的时候踩了个很阴的坑：播放一直没声音，日志也不报错。排查半天发现是看门狗的 Timer 声明了 late 但没初始化，空指针异常被上层的 catch 吞掉了，流程悄悄断了，落盘一个 0 字节的流。从那以后我对 \"静默的 catch\" PTSD 了。\n\n系统媒体这块换了路子。audio_service 用下来，媒体通知的样式定制被模板天花板死死挡住，想要的样子根本画不出来，最后干脆整个换掉，自研了一个轻量媒体中心：Kotlin 和 Swift 各写一层薄桥，通知自己画，锁屏、控制中心、耳机线控全接。想画成什么样就画成什么样，再也不用跟别人的抽象搏斗。\n\n音源从单一 B 站扩到了五个：B 站、酷我、网易、QQ、千千，搜索页一键切换。歌词做成三级检索链：站内直连 → 开放歌词库 → 备选源，自动匹配不到就手动搜，还支持时间校准。\n\n本地扫描也重做了：后台 isolate 扫描，进度走流式事件，页面上一边扫一边出结果。UI 上则定了一整套设计系统——颜色全部走 token，全项目禁止硬编码色值；背景是\"极光玻璃\"风，三颗光斑慢速漂移加呼吸脉动，用缓存图层加位移实现，合成器零重绘；空状态统一换成平面矢量插画。\n\n四、聊聊这一周\n\n实际的施工从删库那天算起，到打出正式包定版，一共七天，108 个 commit。\n\n前面的方案设计和 UI 原型打磨不算在内——那部分花的时间比施工长。慢设计，快施工，这笔账是算得过来的。\n\n这周的开发体验和两年前写 1.x 时完全是两个世界，最大的变量是 AI。\n\n两年前的模式是：我边学边写，卡住了找 AI 救火，AI 给的代码经常跑不起来，因为它推荐的库基本都断更了。这次反过来：我先把架构决策写成文档，给 AI 立好规矩——跨模块只准走 provider、SQL 全参数化、颜色只准走 token、静态检查零 issue 加测试全绿才准交付——然后让 AI 按图施工，我做方案决策、代码评审和真机验收。\n\n规矩之外还有记忆：每个模块踩过的坑、修过的 bug、模块之间的关系，都沉淀成文档，下次开工先读记忆再动手。等于给项目配了一个不会离职、还过目不忘的同事。\n\n当然，AI 也不是银弹。播放内核这种高度依赖真机行为的部分，AI 写的代码必须一条条用真机验。比如 B 站视频失效返回 -404，这种只有线上才知道的事，文档里不会写，AI 也编不出来，只有真机能说话。\n\n测试这次是当交付门槛用的：五源各配了假实现，播放引擎有替身，切歌策略和状态机时序都有矩阵测试，全部加起来一百七十多例。2.x 是零测试，所以它才敢腐化。\n\n最后必须吐槽一下构建环境，这七天里最脏的活其实是它：\n\n1.工具链整体升级，132 个依赖 major 升级，AGP 9、Gradle 9.3.1、JDK 17 一步到位\n\n2.AGP 9 砍了 Jetifier，但一堆插件的 Kotlin 版本跟不上，连锁报错；某个知名分享插件的新版本在 AGP 9 下编译丢类，只能锁死在旧版本窗口\n\n3.NDK 想钉旧版？直接被拒，必须 28.2\n\n4.本机 JAVA_HOME 指着 JDK 8，只能在构建脚本里再钉一遍 17\n\n5.首次构建各种下载超时，满屏报错看着像全完了，其实重试就好\n\n这些坑没有一个是业务逻辑，但每一个都能让你卡一晚上。好在这次有 AI 陪着排错，两年前那种\"环境问题耗三天\"的剧情没有重演。\n\n五、结语\n\n一周时间，从删库到 3.0.0：五个音源、三级歌词链、自研的播放内核和系统媒体桥、一百七十多例测试。两年前\"免费听歌、无广告\"的初心没变，变的只是里子。\n\n重写最大的感受是：欠的债总是要还的，早还早超生。删代码的那天，是真的爽。\n\n想下载体验的朋友请访问 GitHub Releases：啵啵音乐。\n\n想看源码的朋友请访问 github 仓库：bobomusic。喜欢的话给个 star，谢谢。\n\n往期推荐\n\n搭建一个快速开发油猴脚本的前端工程 18+ 👍🏻 28+ 💚\n\n金九银十招聘季，IT 打工人，该怎么识别烂公司好公司？ 70+ 👍🏻 80+ 💚\n\n为什么就这个文件的 ESLint 检查失效了？\n\n学会 TypeScript 体操，轻松看懂开源项目代码\n\n别人休息我努力，悄悄写个 cli 工具，必须提升效率，skr~ 60+ 👍🏻 110+ 💚\n\n一文掌握 eslint，再也不怕项目报错 20+ 👍🏻 30+ 💚\n\n开发一个 npm 库应该做哪些工程配置？ 40+ 👍🏻 50+ 💚\n\n分享我在前端学习与开发中用到的神仙网站和工具 40+ 👍🏻 110+ 💚\n\nuniapp 踩坑记录（二） 130+ 👍🏻 150+ 💚\n\n闲来无事，摸鱼时让 chatgpt 帮忙，写了一个 console 样式增强库并发布 npm 100+ 👍🏻 110+ 💚\n\nuniapp 初体验踩坑记录 30+ 👍🏻 60+ 💚\n\n两小时学会 JS 正则表达式，终身不忘","url":"https://www.angryecho.com/c/qian_duan_she_qu/p/P2TBByen31i36te313Alc","author":{"@type":"Person","name":"北岛贰","url":"https://www.angryecho.com/u/F2Obei_dao_er"},"interactionStatistic":[{"@type":"InteractionCounter","interactionType":"https://schema.org/LikeAction","userInteractionCount":0},{"@type":"InteractionCounter","interactionType":"https://schema.org/DislikeAction","userInteractionCount":0},{"@type":"InteractionCounter","interactionType":"https://schema.org/ViewAction","userInteractionCount":0},{"@type":"InteractionCounter","interactionType":"https://schema.org/ShareAction","userInteractionCount":0}]}]}
```

## Sitemap

See the full [sitemap](https://www.angryecho.com/sitemap.md) for all pages.
