#32 Email Verification API
Email WebDev 2026-09-18传统邮箱验证通常采用 Double Opt-In:用户提交邮箱后,服务端发送验证邮件,用户切换到邮箱客户端打开邮件,再点击链接返回网站完成验证。这个流程虽然可靠,但存在明显的行为断点。新的 Email Verification API 试图让浏览器在用户不离开当前页面的情况下完成邮箱所有权验证。
coding in a complicated world
传统邮箱验证通常采用 Double Opt-In:用户提交邮箱后,服务端发送验证邮件,用户切换到邮箱客户端打开邮件,再点击链接返回网站完成验证。这个流程虽然可靠,但存在明显的行为断点。新的 Email Verification API 试图让浏览器在用户不离开当前页面的情况下完成邮箱所有权验证。
把代码高亮从 DOM 层搬到 CSS 渲染层的 2 KiB 方案
前端圈有一条隐形的墙:DOM 负责结构、文本、无障碍与交互;Canvas / WebGL 负责像素、Shader、粒子与 3D。两边各自很强,却很难融合。
HTML-in-Canvas 是 WICG 正在孵化的提案,试图把这道墙拆掉;
而 Canvas UI 则是基于它(并带降级方案)第一个走红的"GPU 级视觉组件库"。
我用 Golang 开发了一个本地站点,跑在 Caddy 上(动态注册路径),并使用阿里云 DNS 将域名解析到本地 IP。
今天突然发现网页无法打开,浏览器一直处于 Pending 状态,而 curl 访问正常。关闭网络代理和浏览器插件、清理缓存后问题仍然存在。
怀疑是 DNS 解析的问题,使用 curl -vk --resolve d3v.cn:443:192.168.31.31 https://d3v.cn/markjour/ 访问也正常。
更奇怪的是,发现使用访客模式可以打开。
最后还发现,在关闭一些浏览器 Tab 后,普通模式也恢复正常。暂时推测可能与浏览器 HTTP/2 连接复用有关:
以后再次遇到的时候再检查验证。
后续多次遇到这个本地网站访问异常问题,今天抽空继续排查。
发现同一台机器上的 Edge,不同 Profile 对 d3v.cn 的访问结果不同:一个可以正常打开,另一个持续 ERR_TIMED_OUT。
进一步查看 Edge 的网络信息发现(d3v.cn 解析为 ::1 和 192.168.31.31,而正常 Profile 解析为 127.0.0.1。随后检查本机 /etc/hosts,发现配置了:
127.0.0.1 localhost
127.0.0.1 eqr.hosts.d3v.cn eqr eqr6
::1 eqr.hosts.d3v.cn
同时公网 DNS 中 eqr.hosts.d3v.cn 也存在地址记录。因此,同一域名实际上同时存在本地 hosts 和公网 DNS 两套解析结果,Edge 不同 Profile 可能缓存或选择了不同地址。
这次基本确认问题核心在域名解析结果不一致,而不是 Caddy、TLS 或 HTTP/2。后续应避免让同一域名同时依赖 hosts 和公网 DNS 提供不同地址,或者至少确保两者解析结果一致。
初审被拒,原因:
初审通过之后,备案进入 “管局审核中-短信校验中” 状态。
几分钟之后就会收到工信部短信验证码,需要进入指定链接输入验证码、手机号、身份证后六位进行核验。
短信核验之后过一会儿,备案进入 “管局审核中-人工审核中” 状态。
本文不涉及自动签发、自动更换证书,只对 acme.sh 工具的简单用法做一个记录。
mime-type:image/png larger-than:1K
# Edge 官方示例,表示过滤出大于 1K 的 PNG 图片
domain:*.csdn.net method:POST
# 过滤出 CSDN 域的 POST 请求。
has-response-header:Content-Type -domain:*.baidu.com
# 过滤出带 Content-Type 头的请求,排除百度域。
| 属性 | 详情 |
|---|---|
domain |
仅显示来自指定域的资源。您可以使用通配符(*)来包含多个域。例如, *.com 会显示所有以 .com 结尾的域名资源。DevTools 会将所有找到的域名填充到自动完成下拉菜单中。 |
has-response-header |
显示包含指定 HTTP 响应头的资源。 DevTools 会将所有找到的响应头填充到自动完成下拉菜单中。 |
is |
使用 is:running 查找 WebSocket 资源。 |
larger-than |
显示大于指定大小的资源,单位为字节。 设置值为 1000 相当于设置值为 1k。 |
method |
显示通过指定的 HTTP 方法类型检索的资源。 DevTools 会将所有找到的 HTTP 方法填充到下拉菜单中。 |
mime-type |
显示指定 MIME 类型的资源。 DevTools 会将所有找到的 MIME 类型填充到下拉菜单中。 |
mixed-content |
显示所有混合内容资源(mixed-content:all),或仅显示当前显示的资源(mixed-content:displayed)。 |
scheme |
显示通过不安全的 HTTP(scheme:http)或安全的 HTTPS(scheme:https)检索的资源。 |
set-cookie-domain |
显示具有与指定值匹配的 Set-Cookie 标头中的 Domain 属性的资源。DevTools 会将所有找到的 Cookie 域填充到自动完成中。 |
set-cookie-name |
显示具有与指定值匹配的 Set-Cookie 标头中的名称的资源。DevTools 会将所有找到的 Cookie 名称填充到自动完成中。 |
set-cookie-value |
显示具有与指定值匹配的 Set-Cookie 标头中的值的资源。DevTools 会将所有找到的 Cookie 值填充到自动完成中。 |
status-code |
显示与特定 HTTP 状态码匹配的资源。 DevTools 会将所有找到的状态码填充到自动完成下拉菜单中。 |
- 表示取反,小技巧:可以通过输入 - 之后的自动补全查看所有支持的选项。看了 How to split JavaScript strings into sentences, words or graphemes with "Intl.Segmenter" 了解到,现在 Web 已经支持分词了:
const text = `我爱北京天安门,天安门上太阳升。伟大领袖毛主席,指引我们向前进。`;
const granularities = ["sentence", "word", "grapheme"];
granularities.forEach(function (granularity) {
// console.log([granularity, index, self])
let segmenter = new Intl.Segmenter("zh", { granularity: granularity });
let seg = segmenter.segment(text);
// console.log(seg) // Segments{}
let result = Array.from(seg, (s) => s.segment);
console.log(result);
});
// ['我爱北京天安门,天安门上太阳升。', '伟大领袖毛主席,指引我们向前进。']
// ['我', '爱', '北京', '天安门', ',', '天安门', '上', '太阳', '升', '。', '伟大', '领袖', '毛主席', ',', '指引', '我们', '向', '前进', '。']
// ['我', '爱', '北', '京', '天', '安', '门', ',', '天', '安', '门', '上', '太', '阳', '升', '。', '伟', '大', '领', '袖', '毛', '主', '席', ',', '指', '引', '我', '们', '向', '前', '进', '。']
随着浏览器厂商的持续推进,现在 WebApp 也能实现很多底层交互了
Hybird App,在 App 中加入 WebView,实现部分逻辑
根据 Native 和 Web 部分的比例,可以分成:
交替
后端和框架核心通信,实现窗口管理,系统调用,WebView。