最近

打开网页,发现最后一条已在四月,一眨眼快已半年。
一直有着写点什么的想法。但依旧迈不起步子,时间在为了糊口的工作和娱乐中耗散殆尽。
依旧没有什么创造性成果,但还是日常鞭策自己去吸收一点东西,做些记录和感想。

CS168

耗子叔之前在 Twitter 上分享了 CSDIY,是一个国外计算机网络课程学习的网站(推荐大家了解下)。
现在这份工作需要不仅仅做页面,还要用 ORM 操作数据库,美其名「大前端」,亦算是半个 CURD Boy。带着这个目的去了解下数据库原理,从中挑选了一门数据库教程进行学习 CS168。课程很有趣,视频和笔记都解释得很清楚,能称得上「喂饭式」教程了,也令我理解到 CSDIY 作者的感受
课程从 SQL 开始,然后 Bottom-Up 地说明数据库的内容,辅以课程笔记和实验项目,基本上可以让学习者窥探出数据库的组成部分及其简单的细节。

  • SQL -> 查询过程构建/优化 -> 索引管理 -> 页/缓存 -> 存储管理
  • 事务的抽象,以及 ACID 的实现,还有数据库恢复等话题

特别是 rookieDB 项目,让我抓耳挠腮好几个月,最后把单元测试和集成测试都跑通了,还是很开心的。
https://github.com/kamilic/sp23-rookiedb-kamilic

岗位自身的属性,以及产品这种快糙猛的迭代,这是在工作中难以体验,难得的快乐。虽然知道很快就会忘掉技术细节,但是还是美好的一段经验。

一些软件工程书籍

还是耗子叔的推荐,看了《代码大全》与《人月神话》,都是好书,都是经典,想到这些项目里面工作。
人越缺什么就越想去寻找什么,书里的内容,离我如此近又如此远。一线开发中每天都在坑中挣扎,书中随便挑几条阅读都是扎心的,但似乎看不到有人会去讨论和研究这些问题。
怪谁呢?是哪儿出了问题呢?想不明白,唯一想明白的是想去一个思考这些问题的团队里面工作。

吃太饱了

公司效益不错,薪资比上一份工作有着不错的提升,但工作内容方面却倒退不少。
因此时常陷入负面情绪,而后被工资治愈,朋友都说吃太饱了,想太多,但我还是不是这样想。

火车旅行

疫情前去了两次日本,分别逛了 JR 东西两边的铁道博物馆,逐渐地喜欢上了日本的火车。特别是最后一次去高山旅游,在寒冷的天气里,运行时带着柴油发动机运转的气动车。
疫情这几年不少的日常消遣就是看拍车佬和铁路相关的 up 主视频,辞职空档期也趁国内疫情稳定和朋友出差的机会到西北,在国内跑了几趟非空调绿皮车,又爱上了火车旅行。
趁着 JR Pass 在 10 月要涨价之前,给自己安排了一个火车旅行,去几个日本农村走走。但最近东京电力的事情搞到舆论沸沸扬扬,恐怕要多费口舌才能得到家人的理解。

抗议归抗议,该吃的吃该喝的喝。
作为人民群众,不论在哪儿生活,本就是板上鱼肉。不论持何立场,要沟通才能相互理解,少点互相伤害,多点真诚沟通。

懒惰

最近没有什么动力去学习,自从学完 CS168 之后就没有什么东西输入了。
虽然有很多东西想做和学习和吸收,但是在成长路上最大的问题依旧是不知道如何去创造和表达。

一首歌

♪ ばらばら~星野源

关于 utility-first 的碎碎念

缘起

这篇文章是在讨论提出引入 utility-first 类的 css-in-js 框架,一时兴起写了一些对于这种类型框架的想法。
稍微调整一下,再加入新的想法,发到这里。

迷之见解

当时提议引入 windicss 的人有这么几点见解:

  1. 解决命名焦虑,windicss 让开发者不需要考虑命名
  2. 利用 JIT 做到动态解析类参数生成 css 的 windicss 出现了,在没 design token 规范的项目都可以很自由地使用任意参数
  3. 样式逻辑都内聚在一个地方,换句话说就是复制粘贴方便

理由都很好,特别是最后一条让我无法反驳,甚至让自己对 css-in-js 有新的理解。

但我始终不能接受这些理由。

变形了的工具

因为我对设计系统,design token 等概念有所了解。并一直为这些美好的概念不能落地到实际工作场景中而感到苦恼。

Tailwind 让我看到了一个落地方式,框架可以引导使用者对 design token 有一个更为确切的认知。

但引入了动态化,我感受到一个工具的变形。
当然这不是 tailwind 的作品,他只是跟随着社区的变化引入了 JIT,但由此我们可以看到开发者需要什么样的工具。

于我,design token 是一个规则,他是死板的,引入了动态化无疑就是打了自己的脸,工具变形成大家希望的样子。

为什么有 utility-first 的 css

  1. 设计规范只有极小数公司有在执行
  2. 大多数设计不懂设计规范和 design token 这些以程序员角度思考的 UI 定义和规则

没有规则就不能做抽象,所以就不要谈 UI 组件化的事情了,这些都是小部分开发能享用的好东西,大多数前端处理业务,都是用开源的组件换个皮做的功能。

  1. 极小粒度的抽象层次
  2. 提升了清晰度的 css,大家喜欢吃糖

前端总是被设计牵着鼻子走,每次开发辛苦抽象出来的设计内容,隔几个版本就会被打破。如此循环往复,开发只能被逼地把抽象粒度压到最小,其抽象层次落到了 css 的属性层面上了,也就诞生出了 tailwind 这些 utility-first 的 css 框架。

为什么搞出了 JIT

因为在大多数情况下,设计是不会管开发的抽象的,他们的脑洞依旧还在疯狂生长,打破 design token 更是家常便饭。

没技术的开发,使用 !important,反正层叠样式表,叠就是了。

有技术的开发,得带点技术来摆烂,那就掏出编译器来制定规则化 util 类,来承担更为动态化的需求。

utility-first 的好处

我认为好处有这些点:

  1. 可自定义样式别名系统
  2. CSS 样式语法糖
  3. 归纳与重构工具

关于第三点想着重说一下:
由于抽象粒度拆到极细,工具能在更高维度理解到整个项目的 css 属性使用情况,从而能够让整个项目的 css 限制在一个固有的集合里面,是一种程序化的自动抽象功能。其产出物的复用率达到了极致,而且开发者们还是可以继续写这些重复代码,且使用者没有理解成本,大家都开心,这是我觉得 utility-first 的 css 框架与其他 css 编写方案的最主要不同,也是最显著的特点。

「就算疯狂堆屎山都没关系,产出物都是高效的,真是一款完美的清理机器。」

感谢诞生了 Headless UI

这个概念很好,也是再次证明技术被设计逼疯了,只能抽象出纯逻辑的控件。

最后

技术被设计逼疯可能只是一个说辞,也有可能是我被设计逼疯了才得出这个结论。
功能粒度变得越来越细,越来越精细化分工,可能才适合这个复杂的世界吧。

总想写点什么东西

Hello Again.

把无人问津的域名重新买起来,博客又重新复活了。
翻看旧时的羞耻文学,把它收回到源代码的不可见处,再一次重新出发。

关于写作

个人写作的场景十分有限,工作之外一般都是写点矫情文字或是无聊的情感表达。同时自己也没有什么分享欲望,所以总憋不出什么文字。
但随着年岁渐长,日渐觉得表达和输出是让人了解自身的一种方式,羡慕推特或是独立博客的博主们围绕着一个话题输出自己的见解和想法很酷。
那就「见步行步」,学着写点吧。

技术感受

最初开始搭建这个网站只是用于实践工作中搞的自动化部署,用了一些开源的 CI/CD 工具,写了点部署脚本跑通流程。
今年重新拾起之后,换用 netifly & vercel 这些好用的部署平台,不仅自带 cdn 还一站式帮你搞定部署,通过 cloudflare dns 跳过去,连服务器都省了。
甚至连项目都不用拉下来,现在这篇文章是在 github codespace 中写成的。
真感叹技术发展之快,DX 之美好,同时也想吐槽为什么新公司的技术还是如此落后,一点都跟不上时代的步伐,为什么国外前端能用着这么美好的工具,而我们还在刀耕火种。

catastrophic-backtracking

前言

最近一个星期都在加班赶专题项目,腾讯 9 月 9 日公益日,我们公司踊跃(beipo)捐款 100w 换来腾讯大佬一点流量进行一下品牌推广。专题页面几个端推广,webview / app webview / 微信浏览器 / 小程序 webview / 手机浏览器。这个项目里面有个图片上传功能,一开始项目没那么紧张的时候就把几个端的图片上传兼容性问题整理一下,抽取一个各个端都可用的图片上传功能组件。

在测试的时候发现我们家的 app 在图片转换过程中卡死半分钟才有反应,然后开始排查问题,在最后找出了一个正则导致的问题。

围绕这个找一些资料,发现这个是一个叫做回溯灾难(backtracking catastrophic)的问题。

问题

在我的组件中,图片最终都会转为一个文件对象,会给出一个 FileDetail 的对象,大概是这样的, 两个字段,会给出类型和 base64。

1
2
3
4
5
{
base64: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEBLAEsAAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA....',
type: 'image/jpeg'
}

我的实现是这样的,后面我们看看正则表达式

1
2
3
4
5
6
7
8
9
10
11
class FileDetail {
constructor(base64) {
const regMineType = /data\:(.+\/.+)\;/;
let execResult = regMineType.exec(base64);
if (execResult === null) {
throw new Error("非法的 base64 字符串");
}
this.base64 = base64;
this.type = execResult[1];
}
}

正则表达式长这样,它是用来匹配 base64 字符串的 minetype 的。
在电脑中测试得十分完美,微信中也是一样,没有什么大问题,但是在我家 app 中就会导致手机发热,卡死长达数十秒的问题。

1
const regMineType = /data\:(.+\/.+)\;/;

后面我将表达式改成如下,就解决掉了问题了。

1
const regMineType = /data\:(.+?\/.+)\;/;

正则表达式通配的匹配方式

正则中的 * + 这些都是采用贪心匹配的,也就意味着他们会尽可能多地匹配字符。
用以上的一段 base64 为例(这段 base64 只是节选,真正的 base64 都超级长,意味着匹配性能会差上百或者上千倍)。

1
const regMineType = /data\:(.+\/.+)\;/;

这段表达式的表现是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-  data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEBLAEsAAD/2wBDAAIB/AQIBAQICAgICAgICA/wUDAwMDAwYEBAMFBwYHBwc/GBwcICQsJCAgKCAcHCg0KCgsMD/AwMBwkODw0MD/gsMDAz/wYEBAMFBw

1 data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEBLAEsAAD/2wBDAAIB/AQIBAQICAgICAgICA/wUDAwMDAwYEBAMFBwYHBwc/GBwcICQsJCAgKCAcHCg0KCgsMD/AwMBwkODw0MD/gsMDAz|wYEBAMFBw(;)

2 data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEBLAEsAAD/2wBDAAIB/AQIBAQICAgICAgICA/wUDAwMDAwYEBAMFBwYHBwc/GBwcICQsJCAgKCAcHCg0KCgsMD/AwMBwkODw0MD|gsMDAz/wYEBAMFBw(;)

3 data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEBLAEsAAD/2wBDAAIB/AQIBAQICAgICAgICA/wUDAwMDAwYEBAMFBwYHBwc/GBwcICQsJCAgKCAcHCg0KCgsMD|AwMBwkODw0MD/gsMDAz/wYEBAMFBw(;)

4 data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEBLAEsAAD/2wBDAAIB/AQIBAQICAgICAgICA/wUDAwMDAwYEBAMFBwYHBwc|GBwcICQsJCAgKCAcHCg0KCgsMD/AwMBwkODw0MD/gsMDAz/wYEBAMFBw(;)

5 data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEBLAEsAAD/2wBDAAIB/AQIBAQICAgICAgICA|wUDAwMDAwYEBAMFBwYHBwc/GBwcICQsJCAgKCAcHCg0KCgsMD/AwMBwkODw0MD/gsMDAz/wYEBAMFBw(;)

6 data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEBLAEsAAD/2wBDAAIB|AQIBAQICAgICAgICA/wUDAwMDAwYEBAMFBwYHBwc/GBwcICQsJCAgKCAcHCg0KCgsMD/AwMBwkODw0MD/gsMDAz/wYEBAMFBw(;)

7 data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEBLAEsAAD|2wBDAAIB/AQIBAQICAgICAgICA/wUDAwMDAwYEBAMFBwYHBwc/GBwcICQsJCAgKCAcHCg0KCgsMD/AwMBwkODw0MD/gsMDAz/wYEBAMFBw(;)

8 data:image/jpeg;base64,/9j|4AAQSkZJRgABAQEBLAEsAAD/2wBDAAIB/AQIBAQICAgICAgICA/wUDAwMDAwYEBAMFBwYHBwc/GBwcICQsJCAgKCAcHCg0KCgsMD/AwMBwkODw0MD/gsMDAz/wYEBAMFBw(;)

9 data:image/jpeg;base64,|9j/4AAQSkZJRgABAQEBLAEsAAD/2wBDAAIB/AQIBAQICAgICAgICA/wUDAwMDAwYEBAMFBwYHBwc/GBwcICQsJCAgKCAcHCg0KCgsMD/AwMBwkODw0MD/gsMDAz/wYEBAMFBw(;)

10 data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEBLAEsAAD/2wBDAAIB/AQIBAQICAgICAgICA/wUDAwMDAwYEBAMFBwYHBwc/GBwcICQsJCAgKCAcHCg0KCgsMD/AwMBwkODw0MD/gsMDAz/wYEBAMFBw(;)

11 [data:image|jpeg;]base64,/9j/4AAQSkZJRgABAQEBLAEsAAD/2wBDAAIB/AQIBAQICAgICAgICA/wUDAwMDAwYEBAMFBwYHBwc/GBwcICQsJCAgKCAcHCg0KCgsMD/AwMBwkODw0MD/gsMDAz/wYEBAMFBw(;)

因为是贪心匹配,一开始它会找到最后的一个斜杠,然后在去寻找分号,但发现没有分号,就需要缩小搜索范围(也就是不那么贪心,去找倒数第二个斜杠)。
按照这样一路持续下去,它要找 11 次才找到带分号的完整匹配。

后面按照我们的改造,改成非贪心匹配。

1
const regMineType = /data\:(.+?\/.+)\;/;
1
2
3
-  data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEBLAEsAAD/2wBDAAIB/AQIBAQICAgICAgICA/wUDAwMDAwYEBAMFBwYHBwc/GBwcICQsJCAgKCAcHCg0KCgsMD/AwMBwkODw0MD/gsMDAz/wYEBAMFBw

1 data:image|jpeg;base64,/9j/4AAQSkZJRgABAQEBLAEsAAD/2wBDAAIB/AQIBAQICAgICAgICA/wUDAwMDAwYEBAMFBwYHBwc/GBwcICQsJCAgKCAcHCg0KCgsMD/AwMBwkODw0MD/gsMDAz/wYEBAMFBw

因为我们选用了非贪心匹配,所以看到正则会先选择最短的匹配长度进行匹配,它会按照尽可能小的搜索范围来进行匹配。

实际测试了一下,两种方法的时间相差了快几万倍,原因是实际的 base64 会很长,数据中会存在很多斜杠,导致贪心匹配会比非贪心匹配浪费了很多时间进行无效的匹配。

catastrophic backtracking

https://www.regular-expressions.info/catastrophic.html
这个网站说了一些关于回溯灾难的例子,所谓回溯,就是匹配中过度匹配,因为匹配过程中过度匹配了,导致程序需要重新按照另一种策略回头再匹配一次的过程。

就像贪心算法需要缩小搜索范围来搜索的过程,这就是过度匹配带来的问题,按照上面那个例子来说,就是该正则表达式回溯了 10 次才找到正确的结果。

我们需要对匹配结果和源匹配字符串有充分的理解才不会掉入重复回溯的灾难中。

每一个正则表达式都是一台状态机,真的需要好好构造它,我们才能够做出高性能的匹配。

贪心和从心

正则表达式是贪心好呢还是怂好呢,贪心又会掉入性能陷阱做无用功,但怂呢有时候又会匹配到错误结果。
这又让我想到了个两难问题,人是贪心点好呢,还是怂点好呢。
其实我发现这问题之后,就暗自决定自己以后要写得怂一点,匹配错了再贪心一点也不为过,一开始太贪心反而会出问题。
我自己还是比较怂的,物似主人形嘛。

the-understanding-of-promise

我对 Promise 的理解

今天在下班路上,突然想起 Promise,然后又有点兴致,就记录一下。
也没打算像大佬们动不动就我们一起写个 Promise A+,就简单谈谈关于自己对 Promise 的一些理解。

异步 / callback

我理解的异步操作,本质上就是一种等待通知的过程。
通知的接收者需要有某种东西来承载,我们的 callback 就是充当通知的接收者。
自己大学有玩过单片机,它们与单片机中的中断很相似,单片机接受到外部的中断信号,会跳到处理中断的内存地址上,运行中断函数的逻辑。

回调陷阱是自然的

Promise 的文章很多都会说到,它是为了解决回调陷阱(callback hell)的问题。
这没毛病,真的没毛病,但我就想说说自己的想法。
我认为,按照命令式的思路来编写程序,写出回调陷阱的代码是十分自然的。

举个例子,我要做三个异步操作 A B C,三个操作之间是有数据依赖的,就是 C 依赖于 B,B 又依赖与 A。在通知的角度看,C 需要等待 B 的通知才能进行,而 B 需要等待 A 的通知才能进行。
按照普通操作,我写出这样的代码。good,一个完美的回调陷阱。

1
2
3
4
5
6
7
8
A(/* callbackA */ () => {
B(/* callbackB */ () => {
C(/* callbackC */ () => {
// OK
})
})
})

我用伪代码写下,可以知道,其实 A 的结束就意味着 B 的开始,B 的结束就意味着 C 的开始。(结束不是语句结束,而是逻辑真正结束,开始与结束之间存在等待通知的过程)
一个 tab 代表,在一个封闭的上下文中的一套独立的操作(callback)。
看,这不是自然而然的回调陷阱吗,按照这种想法编写,肯定就会出现回调陷阱。

1
2
3
4
5
6
START A
(END A
START B)
(END B
START C)
END C

异步流程控制

你别说,其实回调陷阱式的 callback 调用也是一种解决问题的方法,只不过不符合所谓的“优雅”罢了。
不管是回调陷阱的调用还是 Promise,他们都是去处理一个问题,就是把异步操作按顺序调用,以解决各个操作之间数据依赖的问题。
很多说 Promise 的文章都有提到了队列。
队列这种数据结构十分适合用来处理按顺序处理的操作,Promise 用队列来管理各个回调,从而使各个操作可以按顺序进行。
再来说 A B C 三个异步操作,现在是 Promise 形式的操作,熟悉的感觉。

1
2
3
A()
.then(() => B())
.then(() => C());

嗯,优雅

其实每一个 then 都是一次把监听函数塞进队列的过程,其实就是先让监听函数和 THEN 排个队。

1
2
3
4
A Promise Queue
|-----------------
(A) (THEN) (THEN)
|-----------------

A 的内部操作 (B C 同理)

1
2
3
4
5
A() {
return new Promise((resolve, reject) => {
RA(/* callbackA */ () => resolve()); // RA 代表真正的 A 操作
});
}

等 A 的操作通知触发 Promise 内部的监听函数(通过 resolve),触发后就会调用一次 then 的回调函数,也就是运行 B。
此时 B 也会返回一个 Promise(详细的我不说,例如返回一个值也会被当作一个 resolved 的 Promise), 之后 A 的 Promise 得到此结果,则把 A 的监听器移走,把队列交接给 B 的 Promise.

1
2
3
4
A Promise Queue -> B Promise Queue
|----------------- |--------------
(A) (THEN) (THEN) (B) (THEN)
|----------------- |--------------

B 与 C 交接同理

1
2
3
4
A Promise Queue -> B Promise Queue -> C Promise Queue
|----------------- |-------------- |-----------------
(A) (THEN) (THEN) (B) (THEN) (C)
|----------------- |-------------- |-----------------

C 最后发现自己队列中空了,就代表操作完成

当然这是一种情况,会有队列花式合并的过程,比如 A 的 then 返回了一个 Promise 后面还加了一堆 then 的情况。

1
2
3
4
5
6
7
8
9
A Promise Queue
|-----------------
(A) (THEN) (THEN)
(THEN)
(THEN)
(THEN)
(THEN)
(THEN)
|-----------------

那队列交接时这个队列应该调整一下变成

1
2
3
4
5
6
7
8
9
A Promise Queue -> B Promise Queue
|----------------- |---------------------------------------------
(A) (THEN) (THEN) (B) (THEN) (THEN) (THEN) (THEN) (THEN) (THEN)
(THEN)
(THEN)
(THEN)
(THEN)
(THEN)
|----------------- |---------------------------------------------

Promise 就是用队列教会了我们怎么样好好的排队。

为了优雅就要增加复杂度。

最后

看得懂吗?我其实只是写给自己看的。

最近我一直觉得最好还是能够去理解技术背后的思想,或者是希望解决什么问题,而不仅仅它是怎么样的。
虽说这是老生常谈,但是自己好像就没有具体的感觉,今天不知道怎么的,突然有实感了,有那种问题出现了,而 Promise 正好能解决它的感觉,就简单地记下来。

关于排队

前排去日本,有个很冲击心灵的瞬间,在惠比寿的地下铁扶手电梯旁边,那天地铁口很多人。
东京的扶手电梯都是左立右行的,于是我看到一条延长到地铁站内的左立道,那队简直整齐得就像电梯的延长线。
这是在国内看惯扶梯口有一团人头的我所感觉到震惊的。。(⊙o⊙)
右边完全空出了一条行走通道,服了日本人准守规则的能力,不过也正是这种规则才能得到高效的通行效率。
在铁道博物馆,有部分电车驾驶舱参观需要排队,日本人带着他们的孩子过来玩,不时听到他们都会一直对小朋友说 “行列だよ”(要排队哦),这种习惯还真是从小养成的。

深夜小狗神秘事件

好困,迟点再谈吧。
好穷,之后再买本小说读一下。

在开始之前,当我坐在那偌大的剧场中,听着那周围的谈话声,人们走来走去的噪声。
我看着头顶那块大大的 led,我在想,为什么我会坐在这里?
我也觉得很奇怪,可能就是想更多地了解吧,所以我坐在了这里。

散场之后的彩蛋,虽然是个很简单的直角三角形证明问题,那种解法对于中国学生来说完全没有什么特别的哈哈哈哈。
但是最后,啪!一声飞出的纸花却让我感觉到很快乐,那可以是我有生以来,第一次在现场看到漫天纸屑。
那个 moment,特别美妙,神奇。
最后我还塞了几张小纸片在钱包,拿回家做纪念。

Can see something hidden if you act like a hacker.

Flame - 土岐麻子

こわごわと
小心翼翼地

子供のように
像小朋友一般

ひと息に
屏息之间

マッチを擦る
擦亮火柴

ベッドには
映照在床上

波紋のような
波浪般

キャンドルの
蜡烛的

影が泳いで
影子在漂浮

暗い夜の中で
在这漆黑的夜晚

望みをあぶり出すよう
流露出希望

こわごわと
小心翼翼地

子供のように
像孩子一般

それを見ていた
看着这微弱焰火

これが愛なら
如果这是爱的话

あの人照らす
能成为照亮那个人

光になるかな
那一道光吗

どうか教えて
请告诉我吧

小さく灯る炎
这微小的烛光

誰にも吹き消せない
谁都不能让它熄灭

揺れる 揺れる 火を
摇摇晃晃的火焰

肌の中に隠してる
藏在肌肤之中

おそれ戸惑って
恐惧与疑惑

吹き消そうとするたびに
在要吹熄的那一瞬间

風に乗ってなお
会借着风

メラメラと強く燃える
燃烧得更加强烈

あの人は
那个人

子供のような
用孩子般的侧脸

横顔で
面对着

マッチを擦る
点亮火柴

開け放つ
向着那

窓の向こうへ
敞开的窗外

小さな火
那微小的火焰

吸っては吐いて
在一吸一呼之间

時間が灰になり
时间便成灰烬

たちまち崩れてゆく
一瞬间走向崩溃

生きている そんなことを
初次感受到活着

はじめて思う
这种感觉

これが愛なら
假如这是爱的话

誰かのことを
能够不去对谁

傷つけるかな
造成伤害吗

どうか教えて
请告诉我吧

小さく灯る炎
这微小的烛光

誰にも吹き消せない
谁都不能将它吹熄

揺れる 揺れる 火を
摇摇晃晃的火焰

肌の中に隠してる
藏在肌肤之中

おそれ戸惑って
恐惧与疑惑

吹き消そうとするたびに
会在要吹熄的那一瞬间

風に乗ってなお
会借着那风

メラメラと強くなるの
燃烧得愈加猛烈


哎呀我的妈,
歌词太难翻译了啊..
真是少点文采都不行,读书少啊读书少啊,还是得多积累。

爱如焰火,有时可以强大到怎么吹都吹不熄。
一吹熄,一切化为灰烬。

歌词中这种感觉还是能体会到的,但是就是翻不出来啊摔!
最近都不想做东西,等有空把歌曲插件插进来,就可以边听边看了。
调整得好一点再传给云音乐,现在感觉还是没有那种 feeling。

2018-06-03

我还真传上云音乐了,希望没有观众拍我砖,迟点再过来把最终版补上。

coffee-brew-log

在家里冲咖啡时做做记录

sidamo

beans-1 spec

  • roast: medium
  • roaster: 凡仕咖啡
  • location: ethopia - guji
  • process: natural
  • tags: 野姜花 / 柑橘 / 蜜糖 / 红茶的甘醇 / 发酵的醇厚

这个豆子超级便宜啊,拿完新手优惠券才 18 块一包hhhh,也是打算用来练手,不期待好喝。
买回来的时候,看豆子有一大堆瑕疵 > . <。
果然便野冇好野。

18-05-27
  • 10g - 160ml
  • 较大颗粒
  • 聪明杯
  • 水开以后静置 2 min
  • 30s 闷蒸,1 min 注水,搅拌 3 次,盖上盖子;
  • 2 min 后打开阀门,总用时 2 min 45 s

淡啊,但没有小颗粒时萃取过度的杂味了(这次时完全没有萃取吧,淡成水 2333)。
待冷一点有那种西达摩的特有风味,酸味也突出了,但是还是很淡,水水的。
下次延长萃取时间试试吧。
又想起星辰小姐姐冲的那杯好喝的要死的,可惜周五去的时候她休息了,可惜。


18-06-03
  • 10g - 155ml
  • 旋紧旋钮后,放松 3 圈
  • 聪明杯
  • 92°C
  • 30s 闷蒸, 注水,放置 3min 后搅拌,盖上盖子;
  • 总用时 4 min

冲好之后闻味道都知道是不好喝了。。
总体是过萃,入口立刻可以感受到苦涩,喝苦瓜干水一样的味道。

很有趣的是,含着咖啡吸一大口气,居然可以感受到烟熏的感觉。
那味道有点像无牌小哥给我同安仔冲的那杯 rumors 的豆子, 1 : 10 的水粉比,日式冲法,入口是强烈的二手烟气息。
不苦,就很呛,感觉进了火灾现场一样。

西达摩独有的风味还是很突出,但是增加了一大堆苦涩味,我了个去这是不是有病啊。
还有非洲豆子特有的酸呢,去哪儿了?凉了都喝不出酸味啊?
我喝过正常的都是酸味占上风,然后尾段具有浓烈香气的呀。
只能说句,聪明杯都救不了不聪明的我。

今天买了温度计。
然后还定义了一种磨粉量度。因为那些渣渣手磨没有刻度可言,就粗略地定义把旋钮扭紧后,放松多少圈作为刻度吧。起码算个半定量,免得用感觉定义颗粒大小。

就酱~


18-06-04
  • 10g - 120ml
  • 旋紧旋钮后,放松 3 圈
  • 聪明杯
  • 92°C
  • 20s 闷蒸, 注水,盖上盖子;
  • 总用时 3 min

本来加少水是打算把一点百利甜倒进咖啡里面喝的,谁知道这次闻起来喝起来还不错喔,就直接喝了。
但是还是缺少了一点酸啊,是不是因为不新鲜的原因啊?
然后这次居然尝到咖啡中的甜味,之前我觉得咖啡是苦的,没可能有甜味啊。
应该是我平常吃东西有点重口,所以感受不到那些微量的糖分。。