🌸
357 subscribers
127 photos
17 videos
197 links
记录一些 学习笔记,工具,和其它奇怪的东西
Download Telegram
重新续了 GitHub Copilot,需要重新授权下 GitHub 帐号,在 VSCode 找了半天 sign out 都没找到,Google 了一下才发现要通过 activity bar 的 icon 登出。

但是 activity bar 常年是被我隐藏的,因此我需要先把它显示出来,登出,然后再隐藏回去。

感觉这设计太蠢了,为什么不把登出这种操作集成到 Command Palette (Cmd-Shift-P),隔壁的 IDEA (Cmd-Shift-A) 我就是这么登出的。

#吐槽
Rollup 插件开发:让 Service Worker 中的 import 支持 Firefox

我有一个 packet module,它是一个实现了 binary communication protocol 的 common module,这意味着它不只在 SW 中使用,还会在通常的 browser context 中使用,因此可以将该 module 打包为独立的 bundle,以便能在这两个不同的地方分别 import 它。

但是,import statement in workers 在 Chrome、Edge、Safari 都得到了支持, Firefox 却不支持。要想让 SW 在 Firefox 也能正常工作,就必须考虑另外一个方案:将 packet module 分别为 SW 和 browser context 打包两份,也就是说 packet 一份代码抽象为两份存在,此时便不再需要 import。这没关系,因为它足够小且足够关键。

那么,问题是,在 Rollup 中每个 module 由一个 unique id 标识,这意味着 不可能让一份 id 产出两份代码。在这里,我选用更加灵活的 virtual module 实现这一目的。

操作步骤是,在 transform hook 中 parse SW entry 的 AST,traverse 里面所有的 import nodes,并分别将它们替换,如 import {pack, unpack} from '../utils/packet' 替换为 import {pack, unpack} from 'sw-import:../utils/packet'

替换后的模块名会进入 resolveId hook,在这里需要为其补全 extension,如 `sw-import:../utils/packet.ts`,并在随后的 load 中将其从 fs 加载即可。最后还需要在 manualChunks 中,检查 id 是否包含 `sw-import:`,如若包含,将其与 SW scripts 打包到一起,否则打包到 common bundle 并服务于常规的 browser context。

#experience
👍1
2202 年我的 React 搭配

状态管理:局部 jotai、全局 zustand
网络请求:swr
路由:React Router
CSS:emotion + Tailwind CSS
UI:MUI、Mantine
动画:react-spring
表单验证:React Hook Form + zod
日期:Day.js
汇总:强制创建 layer 的属性,及 side effects

will-change: transform:会创建新的 containing block,导致 position: fixed 或 position: absolute 异常

transform: translateZ(0):与 will-change: transform 有同样问题。早期的 Chrome、Safari 会引发闪烁,因此使用 backface-visibility: hidden 作为替代

backface-visibility: hidden:会把 backface 的内容隐藏,在 3D 空间旋转元素时会有问题

will-change: opacity:会创建新的 stacking context,影响元素的呈现顺序,但可以通过 z-index 恢复到预期状态

一般使用 backface-visibility 或 will-change: opacity,副作用相对最小。

See also: https://t.me/handrush/151
#learning
FPS 不是流畅动画的全部

今天测试 CSS 与 JS 动画的性能差异时,发现一个有趣的现象:使用 JS 创建的动画,虽然能持续跑满 60fps,但动画本身“看起来”并不流畅;而 CSS 动画很流畅,但它却并不总是保持在 60fps。

稍微研究了下,发现这跟一个叫 subpixel 的东西有关,具体来说,对于 sub-pixel animations,它会在像素与像素之间添加模糊,使人眼看起来更平滑。

由于并不存在“半个像素”,一些更细小的动画效果就需要通过 subpixel 达到,它让人看起来动画是在“动”,实际上物体并没有动,而只是像素之间的交替变换。

因此,动画应尽量使用 CSS,或 Animations API。CSS 动画不仅能更充分利用 GPU,还能为动画提供诸如 subpixel 的优化,以减少 paint/composite 次数,并让视觉感到流畅。此外,它还不会破坏现有文档结构,而招致的频繁 lay out。

#learning
👍1
CSS Pixel 与 Device Pixel

CSS Pixel 不与真实的 Device Pixel 一一对应。在具有高 Device Pixel Ratio (DPR) 的设备上,一个 CSS Pixel 可能由多个 Device Pixel 表示,如被排列为 2x2 的方格。

DPR 为方格的高度,或宽度,2x2 grid 的 DPR 是 2。对于图像,其宽高与 Device Pixel 相关,若 image 元素 width 为 200px,对应 Device Pixel 为 400px,则图像的清晰显示宽度应为 400px。

#learning
https://featurepolicy.info/

一个 Permissions-Policy 可用值的列表。在里边我发现了 sync-script 这个从来没见过的东西,不过目前也还没任何浏览器支持

#tools
👍1
原来 MIME 还定义了 'example' media type,如 image/example、text/example,一般作为例子占位用。

还发现 Content-type: text/plain; charset=utf-8 中的 charset 原来是 MIME 的一部分,并不是特定于 Content-type 的。

#learning
👍1
一个最小的 CSS 压缩实现

它并不完备,也不能保证 cover 所有 cases,但它足够小,且对我来说够用。
👍1
Service Worker 最佳缓存实践

SW script 的文件名应该是固定的,而不该包含 hash 或其它任何动态内容,如 sw.5b6aeb1b.js 是不被提倡的。因为 Chrome 68 及更高版本,在检查 SW 更新时会忽略缓存

此外,在 SW 的 installation lifecycle,一般会将 index.html 加入缓存,这意味着 index.html 由 SW 提供(依赖于 SW),若 SW 具有非固定文件名,这需要体现在 index.html 中(依附于 index.html),这会引发 circular dependency。

因此最佳策略是,将整个项目构建的 unique id 添加到 SW script,并在下次请求时,浏览器检查到 SW script 变更,重新执行完整的 SW lifecycle,并对包括 index.html 在内的所有静态资源重新缓存。当然也可以配合 skipWaiting 使其提前进入 activation lifecycle。

#experience
👍1
学到两个新词:
Elegant degradation 优雅退化
Progressive enhancement 渐进式增强
alpha-value 如果告诉我是 alpha channel 倒很好理解。如果说成是 transparency,每次都要在脑子里想一想。

因为,100%透明 = 不透明,而不是“完全透明”,0% 透明 = 完全透明 🤯

如果这里说成 opacity,100% 不透明 = 不透明,0% 不透明 = 完全透明。感觉会更符合直觉(
👍2
JavaScript NaN 捡屎

NaN 是 Not a Number 的缩写,如果这么理解,很快会发现 typeof NaN === 'number'

比较 NaN === NaN,结果 false,这说明 "Not a Number" not "Not a Number",但它确实是 number,这有点反直觉

---
我更倾向 JS NaN 是指 "Not a NaN”:试着检查一个值是否为 NaN,isNaN(NaN) === true,这很好,但很快又会发现 isNaN('foo') === trueisNaN([]) === true

在比较时,将关注点放在了 NaN,而不是 Number 上,或许它应该是一个独立类型,毕竟它作为独立属性出现在 top-level scope 中,而不像其它语言那样使用 float('nan')Float::NAN 等形式表示

好在,Number.isNaN() 被加到了 ES6,它只检查一个值是否 "Not a NaN",而不是 "Not a Number",“不是数字”的东西很多,而 NaN 只是 NaN

这并不是全部,JavaScript 中还有许多为了 backwards compatibility 而保留至今的糟粕设计,如在一众语言中 pow(1, Infinity) == 1,而 JS 确是 NaN;如从 Java 1.0 引进的 Date 至今也仍在使用,而早在 JDK 1.1,Date 中的很多方法就被废弃了

---
UPDATED:调整了下叙述逻辑

#屎前考古
两个 delta compression 算法

Bsdiff:https://github.com/mendsley/bsdiff
VCDIFF (RFC 3284):https://github.com/google/open-vcdiff

Bsdiff 基于 suffix array 编码数据,性能上优于 VCDIFF。

#tools
选择视频编解码器

了解了下各个 codec,从编码质量和压缩率来看,目前开放格式里最好的选择就是 WebM/AV1/Opus 这组了,专利格式则是 MP4/HEVC/AAC

不过 AV1 目前并未得到浏览器广泛支持,就连硬解在各个平台上都还不太支持。考虑上这点的话,WebM/VP9/Opus 会更具有吸引力。

尽管如此,如果有可能,始终需要为最坏情况准备好 MP4/AVC (H.264)/AAC 的 fallback 选项。

#learning
HTTP/2 Server Push 细节

HTTP/2 push cache 独立于 HTTP cache,由 server 推送的资源,仅在使用后进入 HTTP cache 并从 push cache 中移除,在推送时也不会检查其 Cache-Control header。

它基于 connection,若 connection closed,则 push cache 消失。由于 HTTP/2 中的一个 connection 除了被多个 requests 复用,还可以在多个 pages 间共享,因此 push cache 也会相应得到共享。这甚至意味着,它可以在具有同一 IP、同一 cert 的跨多个 origin 间共享缓存(即使包含任何应用状态)。

push cache 与其它一众 cache 相比,具有最低的 priority,若 pushed resource 已出现在 HTTP cache 且尚未过期,则使用“较旧的” HTTP cache,若该 resource 仍在传输,则浏览器应该发送一个 CANCEL 或 REFUSED_STREAM frame 以终止 stream。

另外,出于隐私目的,浏览器可能会为带有 credentials 和未带有它的 requests 创建两个独立的 connection,这里的 credentials 可能是 Cookies、HTTP authentication 或 Client certificate。

#learning
刚刚读文章发现两个有意思的写法(但没用!)

false.true = ''

(function () { return arguments.callee })()()


上面两个在 non-strict mode 下都可以运行。想了想第二个可以实现匿名函数递归,不过一般不用 arguments.callee。