碎碎念

Viki 的碎碎念小角落,记录生活中的点滴想法和言论,共 87 条内容,累计 8,141 字。

#86·

只要把 X-Real-IP 和 X-Forwarded-For 请求头设置成国内 IP,就能绕过不少服务的地区限制。

之前用 CloudFlare Worker 部署 60s API 的 B 站热搜接口一直失效,因为 B 站会限制海外 IP。最近在 NeteaseCloudMusicApi 项目的 Issue 里偶然学到这个方法,亲测有效,思路很巧妙。其实 60s API 项目的 /ip 接口本身返回的 IP 也是从这些请求头里取的,刚好 B 站也是这样判断 IP 的,所以用这个办法就能让 CF Worker 也能正常访问 B 站热搜数据了。

#85·

好歹毒的面,哪个小机灵鬼想出来的。

好歹毒的面,哪个小机灵鬼想出来的。... 的图片 1
#84·

我的 2025 年终总结(简短版):

<2025>
🥽 🛋️ Comfort in VRChat
❓ Question Mark / qm
🐈 🎂 Cola turned one
🏠 New cozy nest
💼 💸 A modest salary increase
🎧 R&B, Rap, DT, 小宇 & T
🥰 At last with Mio
🥽 🎓 Graduated from VRChat
🎮 CS2, It Takes Two and HoK
✈️ Flew to see Mio in Chengdu
😋 Durian, Durian and Durian!
🏺 Making crafts together
✍️ 💬 New blog, new thoughts
🤖 👋 Hi mioki! Bye KiviBot
🎆 🥂 New Year‘s Eve together
</2025>

#83·

对象:(突然严肃起床穿衣服)
我:(疑惑脸)你起这么早干嘛?
对象:(头也不回的)起来打 CS
我:啊?(大脑飞速运转)

之前和我线上打 CS,她打的一点游戏体验都没有,后面就没怎么一起玩了。昨晚重新手把手教她好好打了会 CS,了解了一些基础知识,没想到竞技模式还杀了不少人,有点天赋,打的还可以。但没想到竟然玩上瘾了,一大早就爬起来打 CS。🤷

#82·

我不允许有人没吃过白象的大辣娇板面。🍜 😋

#81·

为什么没有榴莲的 emoji 啊?=.=

#80·

关于 B 站投稿视频 ID 的历史和现状:

  • 最开始 URL 中使用 AV 号,以 av 开头加上数字 ID,如 av80433022,按上传顺序发放序号,URL 参数名为 aid
  • 最早的 AV 序号是 2,由站长 碧诗 于 2009/09/09 09:09:09 上传
  • 最后一个时序 AV 序号 99999999 诞生于 2020-03-28 19:45:02,至此结束了传统按上传顺序发放的模式,改为随机发放
  • AV 号 99999999 诞生的五天前,B 站推出了全新视频 ID —— BV 号,以 BV 开头的固定 12 位字符串,如 BV1GJ411x7h7,URL 参数名为 bvid
  • BV 号的实质是 AV 号的 Base58 编码形式,并移除了容易混淆的字符,两者可以通过算法转换
  • 目前新上传的视频仍会随机分配较长的 AV 号,但主要使用 BV 号进行标识

BV 号和 AV 号的转换算法,来自 bilibili-API-collect

const XOR_CODE = 23442827791579n
const MASK_CODE = 2251799813685247n
const MAX_AID = 1n << 51n
const BASE = 58n

const data = 'FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf'

function av2bv(aid: number) {
  const bytes = ['B', 'V', '1', '0', '0', '0', '0', '0', '0', '0', '0', '0']
  let bvIndex = bytes.length - 1
  let tmp = (MAX_AID | BigInt(aid)) ^ XOR_CODE
  while (tmp > 0) {
    bytes[bvIndex] = data[Number(tmp % BigInt(BASE))]
    tmp = tmp / BASE
    bvIndex -= 1
  }
  ;[bytes[3], bytes[9]] = [bytes[9], bytes[3]]
  ;[bytes[4], bytes[7]] = [bytes[7], bytes[4]]
  return bytes.join('')
}

function bv2av(bvid: string) {
  const bvidArr = Array.from<string>(bvid)
  ;[bvidArr[3], bvidArr[9]] = [bvidArr[9], bvidArr[3]]
  ;[bvidArr[4], bvidArr[7]] = [bvidArr[7], bvidArr[4]]
  bvidArr.splice(0, 3)
  const tmp = bvidArr.reduce((pre, bvidChar) => pre * BASE + BigInt(data.indexOf(bvidChar)), 0n)
  return Number((tmp & MASK_CODE) ^ XOR_CODE)
}

console.log(av2bv(80433022))
console.log(bv2av('BV1GJ411x7h7'))
#79·

JavaScript 的 sort() 函数的默认行为:

const sorter = (a, b) => String(a) > String(b) ? 1 : String(a) < String(b) ? -1 : 0
#78·

🎄 Merry Christmas!

#77·

如果不需要额外的属性和状态,可以使用抽象类并结合静态方法来避免类实例化,从而降低运行时开销。

// Abstract class with static method
export abstract class Service {
  static handle(name: string): string {
    return `Hi ${name}!`;
  }
}

// Usage
Service.handle('Mio') // "Hi Mio!"
#76·

嘻嘻

#75·

Punycode(国际化域名编码)是一种表示统一码和 ASCII 码的有限的字符集,它将非 ASCII 字符转换为仅包含 ASCII 字符的字符串,实现了「兼容现有域名系统的同时支持多语言字符」的能力。例如中文「上海」经过 Punycode 编码后为「fhqz97e」,浏览器访问「上海.com」其实是访问「xn--fhqz97e.com」。其中,「xn--」是国际化域名的前缀,也被称为 ACE Prefix(ASCII Compatible Encoding)。

#74·

电脑硬盘格式限制升不了 Win11 25H2,插 U 盘进 PE 将系统盘从 MBR 转为 GPT 格式,创建 ESP 和 MSR 分区,然后修复 UEFI 引导,重开发现进不了系统了,一直转圈圈… 😓 不知道哪里点错被我搞坏了,好在没什么重要东西就索性重装了,另外 1T 多的游戏和综艺都在另一张固态里,装完重新导入 Steam 就好了,系统现在干净如初。

#73·

习惯备注「外卖挂门上」好久了,第一次听到有骑手提醒我「开门的时候注意别弄掉了」,有点暖心(虽然他把外卖放在门把手最内侧,开门基本不会掉)。因为之前确实有骑手挂在门把手外侧而不是转轴内侧,一开门外卖就会摔到地上,别问我是怎么知道的。

#72·

工作笔记应有的逻辑及其四个内容维度。 有写过一阵子的工作笔记,现在工作内容不算多,大多数时间摸鱼去了,懒得写,但对以后去到下家公司后的工作记录还是很有参考价值的。

#71·

是开心的一刻 🥰 🏦

#69·

之前有提到可以 自定义 <select> 元素,具体来说是通过设置 CSS 属性 appearance 为 base-select 来实现的,不得不说,效果确实很牛批,不仅是样式可自定义,连布局排版都可以高度定制化。目前已在 Chrome 135 可用,Chrome 官方发布了一篇文章说明这个功能: 现在可以使用 CSS 自定义 <select> 元素,团队成员 Una 也发了篇个人博客来讲述技术细节: Updates to the customizable select API

#67·

好耶,mxx 和宗主终于回来了!mxx 担任指挥,有他队内氛围也好了,chopper 换 tN1R 等于火力补强了,宗的自由人更是有自己的一套打法,期待一下明年的比赛。🤩 一分钱不花换了两个 Major 冠军上来,哪家俱乐部还有这种手笔?

#65·

强烈谴责,动不动就扣我五毛钱!

#64·

一只迷路的蚂蚁问另一只蚂蚁:“你都如何回蚁窝?” 被问的蚂蚁:“带着笑或是很沉默?”

#63·

之前有提到 无 token 的 npm 包发布方案,今天下午尝试了之后,踩了几个坑卡了好久,记下来长个记性。😭

  1. 只有最新的 npm 才支持,需要在 GitHb Workflow 里加一个将 npm 升级到最新版本的步骤
  2. pnpm publish 底层直接调用的 npm publish 命令,升级 pnpm 版本无法解决问题
  3. 确保 GitHb Workflow 文件名无误(注意扩展名是 yml 还是 yaml)
  4. GitHb Workflow 要开启 id token 写权限
#62·

😎

#61·

12 月 4 号爆出的 React RSC 漏洞 虽然修了,但只修了一点点... -_-||| 被发现还 存在 DoS 和源码泄漏问题。手快的人(比如我)还得再把 React 相关包升级到 19.2.3 及以上版本才行。

#60·

大表哥带领 FaZe Clan 从第一阶段一路打到总决赛,最终拿到 Major 亚军。有点励志,虽然没夺冠有点可惜,但也正如「总监」 Twistzz 所说:无论输赢,这都是一段值得骄傲的旅程。

#59·

Donk 都 C 麻了还是输了,打完给人气哭了都。FaZe 这个队伍让人看得赏心悦目,依旧地狱归来。

#58·

也是上过荣耀王者的人了,Mio 打得真不错。

#57·

一种主动缓解 npm 包供应链攻击的可选方案,通过 --before 标志安装指定日期之前发布的包版本,让整个 npm 生态有时间来响应和修复潜在的安全问题。pnpm 也提供了 minimumReleaseAge 配置来达到类似目的。

npm install express --before="$(date -v -7d)"
#56·

熬大夜没白看,zweih 发力 ACE 五杀了,2-0 带走猎鹰!三个 Donk 怎么打!

#55·

基于 ViewTransition API 实现的页面同一内容自然过渡效果,要求较新的浏览器版本,鸽了好久,终于有机会给博客整上了 🤤

#54·

存档一下随机结果,看看是随机对的多还是自己选对的多。

#52·

什么时候可以改掉一紧张就语速飞快的毛病(─.─|||)

#51·

使用原生 CSS 属性 content-visibility: auto 开启 尺寸局限 并搭配 contain-intrinsic-size 指定尺寸局限尺寸,可以大幅提升长页面渲染性能,被认为是传统虚拟滚动的现代化可选方案。一些区别:

  1. 它避免了大量的 JavaScript 计算和事件监听,性能开销比传统方案更低
  2. 它并非传统虚拟滚动,只是跳过不可见部分的渲染,依然存在于 DOM 结构和无障碍树中
  3. 正因第二点,没渲染的部分仍然可以被 Ctrl + F 搜索到
  4. 不仅针对列表,也适用于任何长页面内容,更加通用
  5. 它在 2024 年进入基线特性,整体来说兼容性不错,但仍有部分老旧浏览器不支持
#50·

几行 CSS 实现的带有模糊背景的固定比例图片效果,核心代码如下,目前已经应用到「碎碎念」和「Mio 说」板块的图片元素,这是为了统一图片高度防止加载过程中的布局抖动而采取的一种妥协方案。

如果对这个实现感兴趣,你也可以在 React Online 上在线调试这种图片背景效果。

.image-wrapper {
  display: grid;
  /* background: var(--bg) 50% / cover; */

  &::before,
  img {
    z-index: 1;
    grid-area: 1/1;
    aspect-ratio: 16 / 9;
    object-fit: contain;
  }

  &::before {
    backdrop-filter: blur(8px) brightness(0.8) contrast(0.7);
    content: '';
  }
}
#49·

优雅的 Toast 库 Sonner 的作者 Emil Kowalski 最近分享了一篇文章: Build a Toast Component,讲述了 Sonner 的从头到尾,包括命名、设计和实现细节等,挺有参考价值。有意思的是,文章最后提到了他的动画课程的卖课网站,果然程序员的尽头都是卖课么。

#48·

强烈推荐阅读由微软 Edge 平台产品经理 Patrick Brosset 发布的博客文章:Masonry: Things You Won’t Need A Library For Anymore。文中他详细阐述了一系列现代网络特性和替代方案,这些特性和方案可以帮助开发者减少对第三方库的依赖,从而简化代码库并提升性能。

阅读笔记:

#47·

当子元素滚动到边缘时继续滚动,这个时候父元素的滚动行为可以通过 CSS 属性 overscroll-behavior 来控制,即:浏览器在到达滚动区域边界时的行为。这种滚动行为的默认链式表现,也叫 滚动链

#46·

depx.co 这个服务可以让你生成指定 npm 包的依赖数的 Badge 图,适合大小敏感、关心依赖数的项目展示在 README 里。比如下面这个 Badge 展示了 Vue 依赖数。

dependencies

#45·

拭目以待。

#44·

一个值得思考的 useMediaQuery 实现:

function useMediaQuery(query) {
  return useSyncExternalStore(
    (callback) => {
      const mql = window.matchMedia(query);
      mql.addEventListener('change', callback);
      return () => mql.removeEventListener('change', callback);
    },
    () => window.matchMedia(query).matches,
    () => false // SSR fallback
  );
}
#43·

Zig 项目(Bun 的主要开发语言)因 GitHub 工程衰退和 CI 系统崩溃而将代码仓库 迁移至 Codeberg,建议捐赠者转向 Every.org。

#41·

周杰伦阔别三年,携手方文山、黄俊郎共同打造音乐大碟《催什么催》,名字别具一格,每一首歌都相当硬核,延续周氏曲风...

#40·

为什么会有水果蛋糕这种东西?把切开的草莓、蓝莓放到甜奶油上面,吃的时候不会感觉到草莓不仅不甜,反而觉得很酸、影响口感吗... ┑( ̄Д  ̄)┍

#39·

在第三阶段 2-0 组晋级赛,绿龙拿下老鼠之后,Donk 给教练当翻译 🤣

#38·

CookieStore API 在今年七月被正式列入 基线特性,相比传统的 document.cookie,它更现代、结构化、异步、安全,适合复杂和高安全需求场景。

// 写入指定 Cookie
await cookieStore.set({
  name: 'username',
  value: 'Viki',
  expires: new Date(Date.now() + 24 * 60 * 60 * 1000), 
  domain: 'viki.moe',
}) 
// 读取指定 Cookie
const cookie = await cookieStore.get('username')
const cookie = await cookieStore.get({ name: 'username' })
// 读取所有 Cookie
const cookies = await cookieStore.getAll()
const cookies = await cookieStore.getAll('username')
const cookies = await cookieStore.getAll({ url: 'https://viki.moe/' })
// 删除指定 Cookie
await cookieStore.delete('username')
// 监听 Cookie 变化
cookieStore.addEventListener('change', (event) => {
  console.log('Cookie changed:', event.changed)
  console.log('Cookie deleted:', event.deleted)
})
#37·

发现了一个挺有想法的浏览器插件 Ad Break。正如其名称一样,它用小游戏来覆盖广告界面让用户玩游戏休息一下(Break),不仅能够低成本地解决「广告越来越难屏蔽」的问题,还不影响作者的广告收益,最终减少了用户的广告厌恶感,算是一个双赢的方案。同时,它也保留了时间进度和跳过广告的功能,如果用户不想玩游戏也可以仅显示空白界面。

广告你尽管放,该给视频主的广告播放时长我一定支持,但广告内容我是一点没看 —— 玩玩小游戏先。 🤪

#36·

原来 JavaScript 还支持这种语法,真第一次见... 参考 Static initialization blocks(静态初始化块)。

class AppConfig {
  static platform;
  static config;
  static {
    this.platform = typeof window === 'undefined' ? 'server': 'client';
    try {
      this.config = JSON.parse(localStorage.getItem('config'));
    } catch {
      this.config = {};
    }
  }
}
#35·

CSS 自定义属性 (CSS Custom Properties) 和 CSS 变量 (CSS Variables) 本质是 同一个东西,只是叫法不同。日常交流叫 CSS 变量,因为其用法类似于编程语言中的变量,强调的是它的功能,存储和复用值,类似编程语言中的变量概念。但在正式场合、技术文档中,一般叫 CSS 自定义属性,强调的是它在 CSS 中的本质,是一种特殊的属性(以 -- 开头)。

#34·

交作业了,这次也是一起选的。FaZe 给我赢 ε=(o`ω′) ノ

#33·

听 Tom Forsyth 讲述在 半条命 2 (Half Life 2) 的 VR 版本移植过程中发生的趣事:一个由「门」引发并存在了十几年的严重游戏 bug。(剧透提示:这个 bug 是由于浮点运算精度变化导致的。2004 年编译用 32 位精度,2013 年用 64 位精度,小数点差异造成守卫脚趾位置偏移几毫米,碰到了门导致门无法打开。

#32·

标记为 Secret 的 GitHub Gist 实际上 是公开的?? 拿到链接的任何人都能随意访问,66666 🤷

#31·

没想到 React 也整了个致命漏洞,还挺严重的。

React 19 版本正式引入的 RSC (React Server Component) 存在致命 RCE(Remote Code Execution,远程代码执行)漏洞,评分 10.0。攻击者可在服务端执行任意危险代码,覆盖 19.0, 19.1, 19.2 等版本,影响了包括 Next.js, Waku, React Router 等在内的主流前端框架,只有「仅客户端应用」幸免。参考 CVE-2025-55182React 官方公告

#29·

都是神队,第二阶段作业 果然给我炸光了,只对了三个。不过好消息是,大家都彼此彼此,哈哈哈哈哈哈哈哈。 可惜天禄还是没把握住机会啊,本来能多赢两图的。

#28·

#16,以下类型解决了 Omit 在复杂 联合类型 下的局限性。它通过 K extends keyof T 来确保 K 必须是 T 的键之一,并利用 分布式条件类型 来处理联合类型情况下的 T。DistributiveOmit<A | B, K> 实际上等价于 Omit<A, K> | Omit<B, K>,而不是 Omit<A | B, K>

type DistributiveOmit<T, K extends keyof T> = T extends any ? Omit<T, K> : never;

type TypeBase = { name: string; };
type TypeA = TypeBase & { type: 'A' };
type TypeB = TypeBase & { type: 'B' };

type Result = DistributiveOmit<TypeA | TypeB, 'name'>;
// Result => { type: 'A' } | { type: 'B' }

type BrokenResult = Omit<TypeA | TypeB, 'name'>;
// BrokenResult => { type: 'A' | 'B' }
#27·

通过 Mac 系统内置的「音频 MIDI 设置」APP,可以添加并配置「多输出设备」,实现同时输出到多个音频设备的效果。如果你家里有多台音响设备,就可以营造更好的环绕声效果。或者如果你有对象,可以同时输出到两台蓝牙耳机,方便一起听音乐或看电影。

#26·

这两年 JSON 给我 import 爽了,马上也可以 import textbytes 了 🤩。

import obj from "path/to/file.json" with { type: "json" }

import text from "path/to/file.txt" with { type: "text" }
import uint8array from "path/to/file.txt" with { type: "bytes" }
#25·

关于 <input type="number" /> 的一些笔记:

#24·

Bun 宣布加入 Anthropic,但其核心路线不变,变的是 Claude Code 及其 SDK 因 Bun 团队而受益。Bun 在 22 年暑假发布了 v0.1.0,开始在前端领域掀起大浪。比较认同 Bun 的核心理念,性能、效率、Just Work,只是目前 Bun 在生态和兼容性上仍需完善。

#22·

如果面向 Node.js 的包想标记一个方法为弃用,除了使用 JSDoc 的 @deprecated 注释指令让编辑器展示删除线样式外,还可以使用 Node.js 内置的 util.deprecate() 函数,它会在运行时发出警告,提醒开发者该方法已被弃用。

#21·

继 npm 支持 Provenance 以验证发布来源之后,又支持了 Trusted Publisher 以配置 OIDC 来支持「无 Token 发布」,也是对近期 npm 包供应链攻击频发 而强制使用有到期时间的 Token 的一种用户体验和安全双保障的改进措施。

#20·

Node.js, Yes! ✌️

- node --disable-warning=ExperimentalWarning --experimental-transform-types index.ts
+ node index.ts
#18·

原来 幸运咖 也是蜜雪旗下的。🤔

#17·

早在 20 年初就了解到 Ink (中文意为「墨水」)这个「React in Terminal」库,它提供了一个终端渲染器,类似终端下的 react-dom,通过 React 来开发终端程序。包括 GitHub 的 CLI gh 和 CloudFlare 的 Wrangler 在内的很多早期知名的 CLI 项目都基于它开发,非常看好。

出于兴趣,在 24 年也基于它写过一个终端 QQ,叫 Chatee用来摸鱼的,但随着 ICQQ 的闭源和 QQ 协议的更新,已经基本废弃了。

25 年 2 月,基于 Ink 的 Claude Code 发布,Ink 也被更多人熟知。随之而来的是 Gemini CLI 等一批基于 Ink 的终端应用,Ink 生态也逐渐活跃起来,非常感慨。截止 25 年 12 月,Ink 已收获 33K+ Star,npm 周下载量 200 万 +。

#16·

通过将 TypeScript 类型包装在元组 [T][U] 中,可以禁用 分布式条件类型

type AllExtend<T, U> = [T] extends [U] ? true : false // ✅ 
type AllExtendBroken<T, U> = T extends U ? true : false // ❌

type WrapInArray<T> = [T] extends [unknown] ? T[] : never // ✅
type WrapInArrayBroken<T> = T extends unknown ? T[] : never // ❌

type Result1 = AllExtend<'cat' | 42, string> // false ✅
type Result2 = AllExtendBroken<'cat' | 42, string> // true | false ❌
type Result3 = WrapInArray<'cat' | 42> // ('cat' | 42)[] ✅
type Result4 = WrapInArrayBroken<'cat' | 42> // 'cat'[] | 42[] ❌
#15·

一些常见的 ECMAScript 规范与运行时的兼容性、API 对比工具网站:

#14·

第二阶段,和 Mio 一起选的,希望能过~

#13·

Major 第一阶段作业 开局 3-0、0-3 炸差不多了还能救回来是我没想到的 😂。LVG 和 RA 怎么双双 0-3 回家,一图都没赢啊??RNM 退钱 😡。Faze 依旧地狱归来 👻。菊花果然被淘汰了,真给力啊 (๑¯◡¯๑),不救我的 3-0 那就直接淘汰让其他人作业都爆炸吧。💥 后面就看天禄了,加油。

#12·

通过修改 NITZ(网络标识和时区)下发规则实现的运营商名称广告和百度地图的路面广告一样流氓。🤷

#11·

建议全世界奶茶店都学习一下这种糖分标注。每次看到「不额外加糖」都得想一想到底是多少糖。😑

#10·

压力我,小心我变爆米花蹦你。

#9·

糯米粘牙是支链淀粉结构导致的。吃糯米饭需要注意趁热、慢嚼、少量。🍚

#8·

交作业交作业,LVG 加油 (ง・_・)ง

#7·

开个「Mio 说」板块,应该不会被说成公开秀恩爱吧,大概。💖

#6·

博客重构到晚上九点半才下班,果然摸鱼是第一生产力。卡点打卡拒被动加班,摸鱼拿钱做人生赢家。

#5·

测试一下发布碎碎念的 API 以及 Markdown 渲染格式是否正常。

  • 这是 加粗
  • 这是 斜体
  • 这是 删除线
  • 这是 链接
  • 这是 code

下面是代码块。

const message = "Hello world!"

下面是测试图片。

#4·

完整的 URL 结构中还包含 Text Fragments,用于在 URL 中指定具体文本,浏览器打开即可高亮对应的文本,方便定位分享。基于 Chromium 的浏览器已经在右键菜单内置了复制命令。尝试点击这个 文本高亮链接 试试看,它会重新打开当前页面,并突出高亮“记录生活” 这四个字。

#2·

除了常见的 CPU、GPU、APU 处理器之外,还有 NPU、TPU、DSP、FPGA。

  • CPU:万能管家,什么都管但速度一般。
  • GPU:画图专家,擅长同时处理千万件小事。
  • APU:经济实用,CPU + GPU 的平价组合。
  • NPU:AI 保姆,专门伺候人工智能。
  • TPU:谷歌家的 AI 天才。
  • DSP:信号美容师,让音视频更完美。
  • FPGA:万能变身器,想要什么功能现场定制。
#1·

使用 all: unset 可以快速重置按钮样式,相比于 div 方案更加语义化且易于维护。

- <div role="button" tabindex="0" />
+ <button /> + { all: unset; }