Email
2026-07-27
MJML(Mailjet Markup Language)是一种专门用于编写响应式 HTML 邮件的标记语言。相当于是 HTML 在电子邮件场景下的更高层的封装,由编译器生成兼容各种邮箱客户端的 HTML。主要解决邮件 HTML 的痛点:
- Gmail / Outlook / Apple Mail 支持差异巨大
- 邮件需要大量 table 布局
- CSS inline 化复杂
- 移动端适配困难
注意:MJML 只负责 UI 不含模板功能。如果需要使用变量或者其他模板语法,可以另外选择任何一种语言进行渲染,比如 Node Handlebars,Golang 模板,Python Jinja2 等。
MJML 最初由法国邮件服务公司 Mailjet 在 2015 年开源。目前项目已经独立维护,属于开源生态:
- 语法:MJML
- 编译器:Node.js 实现
- 输出:标准 HTML Email
GitHub Repository: mjmlio/mjmlio
MJML 文件格式
扩展名:.mjml
<mjml>
<mj-head>
<mj-title>Welcome</mj-title>
</mj-head>
<mj-body>
<mj-section>
<mj-column>
<mj-text font-size="20px">
Hello Woody
</mj-text>
<mj-button href="https://example.com">
Click
</mj-button>
</mj-column>
</mj-section>
</mj-body>
</mjml>
通过一下方法可以直接预览效果:
- VSCode 拓展 [MJML Official]
- 官方 MJML Live Editor
- 自己实现 Preview 服务,AI 很容易生成一个
渲染 HTML
安装:
npm install -g mjml
转换:
mjml template.mjml -o template.html
生成的 HTML 会包含:
- table 布局
- inline style
- Outlook VML兼容代码
- media query
Python
2026-07-26
开发者
2026-07-24
传说中,编程有两大难题,一个是缓存失效,另一个是变量起名。
其中,布尔变量尤其难起名。怎样才能贴切地表达,这个变量是表示"真"(true)和"伪"(false)的布尔值呢?
我最近读到一篇文章《布尔变量起名的艺术》,提出使用四个前缀就能给布尔变量正确起名,我觉得很有启发。
is-:描述事物的状态,后面跟形容词,比如 isActive,isDeleted,isEmpty。
has-:描述事物的所有权或包含关系,后面跟名词,比如 hasAccess,hasChildren,hasValidationErrors。
can-:描述事物的能力或权限,比如 canEdit,canDelete,canRetry。
should-:描述事物的意图或逻辑,比如 shouldRetry,shouldCacheResponse。
除了这四个前缀,起名还有另一条规则:永远不在布尔变量名中使用否定词。
比如,不使用 isDisabled,而要用 isEnabled = false。
地理
2026-07-24
阮一峰的新闻周刊介绍了高德的位置口令,高德本月推出了一个新功能,可以位需要提供准确位置的用户生成一个 6 位编码,其他人直接搜索这个编码就能找到那个位置,而不需要知道这个位置的名称。
文章后面说:要是国家出台统一算法就好了,为每个地点生成一个固定不变的位置码,用处就大了。
我想,这倒没有必要,为所有位置提供编码的方案不就是经纬度么?
经纬度在日常表达方面确实会有一些障碍,读起来不大方便,而且我不确定有多少人知道经纬度,虽然这是初中就学了的。
我设计了一个 8 位编码方案(BASE32,只包含数字和大写英文字母):
经纬度 1 度大约偏差 111 公里,精确到小数点后 3 位小数,可以控制偏差在 100 米范围内,对于日常使用是可以接受的。
总共 360,000 x 180,000 = 648 亿个点。
二进制 36 位可以表达(687 亿),也就是 5 Bytes。
BASE32 8 位足够可以表达了(10995 亿)(7 位只有 343 亿,差一点)。
在百度地图开放平台上用坐标拾取器获取两个坐标用来测试,结果如下:
- 华中科技大学的毛泽东像位置是
(114.420025,30.514723),精度控制到小数点后三位,换算过来就是 BRLMVBKC

- 美国自由女神像位置是
(-74.044500, 40.689225),精度控制到小数点后三位,换算过来就是 ARYMQF7B

Base32 字母出现的频率较多,为了减少英文字母的使用,我改成十六进制的表达方式,只多了一位而已,可以接受。测试结果如下:
- 华中科技大学的毛泽东像位置
(114.420025,30.514723) 换算过来就是 0C56CA8542
- 美国自由女神像位置
(-74.044500, 40.689225) 换算过来就是 0470C817E1
Python 代码示例:
# woody geo codec
import base64
# encode = base64.b32encode
# decode = base64.b32decode
encode = base64.b16encode
decode = base64.b16decode
def wgc_encode(lat:float, lon:float) -> str:
lat_num = int((lat + 180) * 1000)
lon_num = int((lon + 90) * 1000)
num = lat_num * 180000 + lon_num
return encode(num.to_bytes(5, byteorder="big")).decode()
def wgc_decode(code:str) -> tuple[float, float]:
num = int.from_bytes(decode(code))
lat_num = num // 180000
lon_num = num % 180000
print(lat_num, lon_num)
lat = (lat_num - 180000) / 1000
lon = (lon_num - 90000) / 1000
return lat, lon
for lat, lon in ((114.420025, 30.514723), (-74.044500, 40.689225)):
print((lat, lon))
code = wgc_encode(lat, lon)
print(code)
print(wgc_decode(code))
AI LLM
2026-07-24
看了《你需要知道的 AI 内存知识》,我才知道:
- 本地运行大模型的核心资源主要有三个:内存容量、内存带宽和计算算力,三者共同决定模型规模和运行速度。
- 统一内存架构(Unified Memory) 可以让 CPU 和 GPU 共享大容量内存,从而突破传统独立显卡显存容量限制,更容易加载大模型。
- 模型名称中的 MoE(Mixture of Experts,混合专家模型) 表示模型只会激活部分参数进行计算,可以显著降低推理所需的内存和算力。
- 当前消费级设备,即使价格达到数万元,也仍然存在明显限制:要么模型规模受限,要么推理速度不足,实际更适合运行中小型模型或经过优化的 MoE 模型。
哈希 MD5 SHA256
2026-07-24
JavaScript hashing speed comparison: MD5 versus SHA-256(Daniel Lemire, 2025-01-11)通过 JavaScript 基准测试对比了 MD5 与 SHA-256 在 Node.js 23 和 Bun 运行时下的哈希性能,核心结论颠覆了“MD5 更快”的传统认知:
- 测试背景与方法:使用 1GB 随机 Uint8Array 数据,在 ARM(Apple M2、Graviton 4)和 x86(Intel Ice Lake)系统上,分别对
md5Hash 和 sha256Hash 进行基准测试,底层实际调用 OpenSSL 实现。
- 关键性能数据:在现代 CPU 上 SHA-256 显著快于 MD5。例如在 Apple M2(Node.js)上,SHA-256 约 2.6 GB/s,MD5 仅约 0.6–0.7 GB/s;在 Intel Ice Lake 上 SHA-256 也达到 1.2 GB/s,优于或持平 MD5。
- 原因分析:SHA-256 虽然在算法设计上更复杂,但现代处理器(ARMv8、x86 等)普遍带有 硬件密码学扩展指令(如 Intel SHA-NI),对其进行了加速;而老旧且已被破解的 MD5 并未获得类似优化。
- 最终建议:不应再使用 MD5。它既在安全性上已被彻底攻破(存在碰撞攻击),在实际速度上也失去了优势,开发者应默认选择 SHA-256 或更先进的算法(如 BLAKE3,若可用)。
这是采用的 JavaScript 做的基准测试,但是结论说明的是通用硬件现象:现代 CPU(ARM/x86)有 SHA-256 硬件指令加速,而 MD5 无优化,任何语言调底层实现都如此。
因此,在现代处理器上,SHA256 性能大幅优于 MD5 的结论在所有语言上都是通用的。
WebDev 开发者
2026-07-16
- 原备案信息(阿里云 · 2019):备案名称「码厩Markjour」,备案号 鄂ICP备15002462号。
- 迁移原因:不再使用虚拟主机,业务迁至百度云低成本主机,需在百度云重新提交接入/备案,备案名称变更为「码厩开发者笔记」。
初审被拒,原因:
- 应急联系电话校验:应急手机号不能与负责人手机号码相同,需提供另一位真实可联系人的号码(当年没有这个要求)。
- 前台菜单合规审核:初审要求网站不得出现“留言”和“给我写信”菜单,这算页面交互入口(推测当年备案时尚未增加这两个菜单)。
- 这两个功能从没用过,暂时屏蔽前端入口以通过核验,后面再看如何处理。
初审通过之后,备案进入 “管局审核中-短信校验中” 状态。
几分钟之后就会收到工信部短信验证码,需要进入指定链接输入验证码、手机号、身份证后六位进行核验。
短信核验之后过一会儿,备案进入 “管局审核中-人工审核中” 状态。
Git 开发者
2026-07-11
AI 总结了 The Git Commands I Run Before Reading Any Code,可以作为参考。
PS:其中部分命令的有效性是建立在提交规范执行良好的基础之上。我过去经手的项目全部没有提交规范。😂
Five git log commands that diagnose a new codebase before you open a single file: code churn hotspots, bus factor, bug clusters, and crisis patterns.
1. 变更热点 列出去年改动最频繁的 20 个文件,榜首常是团队"无人敢碰"的那个。高变更本身不糟,但若叠加"无人愿拥有",便是代码库拖累的最强信号——每次修改都是补丁叠补丁,小改动的影响范围不可预测。微软 2005 年研究证实,变更率指标比复杂度更能预测缺陷。
git log --format=format: --name-only --since="1 year ago" | sort | uniq -c | sort -nr | head -20
2. 总线因子 按提交数排名贡献者。一人占 60%+ 即为风险;若其已离职半年便是危机。长尾同样关键:30 位贡献者中仅 3 位近一年活跃,说明建造者已非维护者。注意 squash-merge 会压缩作者信息,下结论前需确认合并策略。
git shortlog -sn --no-merges
3. Bug 集群 形态同变更热点,但过滤含 bug 关键词的提交。同时出现在两张列表的文件风险最高:反复出错、反复修补却从未根治。此法依赖提交信息规范。
git log -i -E --grep="fix|bug|broken" --name-only --format='' | sort | uniq -c | sort -nr | head -20
4. 提交节奏 按月统计提交数。稳定节奏健康;单月减半常因有人离职;6–12 个月下滑曲线揭示团队失速;周期性尖峰后沉寂则是批量发布模式。这是团队数据,而非代码数据。
git log --format='%ad' --date=format:'%Y-%m' | sort | uniq -c
5. 回滚与热修频率 一年中偶发回滚正常;若每几周一次,说明团队不信任部署流程,背后是测试不可靠、缺预发环境或回滚困难。零结果同样值得玩味。
git log --oneline --since="1 year ago" | grep -iE 'revert|hotfix|emergency|rollback'
Linux Ubuntu
2026-07-10
从官网下载的钉钉 deb 文件,安装之后竟然无法启动。
-> % ll ~/Downloads/com.alibabainc.dingtalk_8.1.0.6021101_amd64.deb
-rw-rw-r-- 1 catroll catroll 387M 2026-07-10 21:39:31 /home/catroll/Downloads/com.alibabainc.dingtalk_8.1.0.6021101_amd64.deb
-> % apt install ~/Downloads/com.alibabainc.dingtalk_8.1.0.6021101_amd64.deb
一边查看日志(journalctl --user -f),一边点击钉钉图标,可以看到以下错误日志:
7月 10 21:49:27 eqr com.alibabainc.dingtalk.desktop[361857]: ubuntu
7月 10 21:49:27 eqr com.alibabainc.dingtalk.desktop[361857]: ubuntu branch
7月 10 21:49:27 eqr com.alibabainc.dingtalk.desktop[361857]: preload_libs=./libgbm.so ./plugins/dtwebview/libcef.so
7月 10 21:49:27 eqr com.alibabainc.dingtalk.desktop[361873]: ERROR: ld.so: object './libgbm.so' from LD_PRELOAD cannot be preloaded (cannot open shared object file): ignored.
7月 10 21:49:27 eqr com.alibabainc.dingtalk.desktop[361873]: Run Main is_gpu=0 is_zygote=0 is_render=0 is_crashpad_handler=0 cmd : ./com.alibabainc.dingtalk
7月 10 21:49:27 eqr com.alibabainc.dingtalk.desktop[361873]: Load /opt/apps/com.alibabainc.dingtalk/files/8.1.0-Release.6021101//dingtalk_dll.so failed! Err=/opt/apps/com.alibabainc.dingtalk/files/8.1.0-Release.6021101//dingtalk_dll.so: cannot enable executable stack as shared object requires: Invalid argument
7月 10 21:49:27 eqr systemd[4607]: Started app-gnome-com.alibabainc.dingtalk-361857.scope - Application launched by gnome-shell.
经过 AI 的一番分析,结论是:
钉钉 deb 包依赖的 ELF 动态链接配置与当前系统环境不匹配。patchelf 修正了二进制的解释器、RPATH 或依赖库路径,使程序能正确加载运行时库之后就可以启动了。
sudo apt install -f patchelf
sudo patchelf --clear-execstack /opt/apps/com.alibabainc.dingtalk/files/8.1.0-Release.6021101/dingtalk_dll.so
个人
2026-07-09
冰淇淋制作流程
1. 混合(Blending):打好地基
将乳脂(奶油/牛奶)、糖、乳化剂、奶粉混合加热。这一步决定了冰淇淋的“底子”好不好,乳脂越高,口感越顺滑。
2. 均质(Homogenization):打碎脂肪
通过高压把牛奶中的大脂肪球打成微米级的小球。这一步是为了防止脂肪上浮,让口感变得细腻、均匀,没有粗糙感。
3. 老化(Aging):让蛋白质休息
将混合物迅速降温至4℃左右静置4小时以上。目的是让蛋白质充分吸水膨胀,这样后面打入空气时才不容易塌陷,质地更稳定。
4. 凝冻(Freezing):魔法发生的地方
这是最关键的一步!在-6℃至-10℃的凝冻机里疯狂搅拌。
- 混入空气:让体积膨胀(膨化率),变得蓬松。
- 形成冰晶:快速搅拌防止生成大冰晶,只留下微小的冰晶,这就是“细腻”的来源。
5. 硬化(Hardening):定型
如果是硬冰淇淋(如哈根达斯),会放入-30℃的冷库急冻,固定形状。
为什么不同品牌口感不同?
- 哈根达斯(硬冰淇淋):膨化率低(空气少),乳脂极高,所以口感像吃冷冻的奶油,非常厚重。
- KFC/麦当劳(软冰淇淋):凝冻后直接出机,膨化率高,空气多,所以口感轻盈像云朵,但也化得快。
- 蜜雪冰城:为了控本,非乳脂固形物(如奶粉、糖浆)多,乳脂少,冰晶相对较大,所以感觉没那么“润”。