Shane.xin

写点技术笔记,主要是给自己看的

平时做前端和一些小工具,遇到问题习惯记下来。这里放的多半是踩坑记录—— 当时想明白了,过半年多半又忘了,写下来省得再查一遍。

更新不定期 · 内容以实际遇到的问题为主

最近的笔记

静态站配了强缓存,然后发现改动推不下去

2026-07-30

给一个纯静态站配 nginx,顺手给 CSS 和 JS 加了七天强缓存。当时觉得挺合理—— 文件不常改,能省不少流量。结果改完样式重新部署,自己手机上打开还是旧的, 清了缓存才对。

原因很直白:文件名没变,浏览器在缓存过期前根本不会来问服务器。 Cache-Control: max-age=604800 的意思就是「这七天你别来烦我」, 服务器上换了内容它也不知道。

正经做法是文件名带内容哈希,改一次换一个名。但没有构建步骤的项目做不了这个, 于是折中:HTML 里给资源挂个版本查询串,nginx 那边把 CSS/JS 改成协商缓存。

location ~* \.(css|js)$ {
    add_header Cache-Control "no-cache";
    etag on;
}

no-cache 这个名字有点误导,它不是「不缓存」,而是「每次先来问一下」。 没变就回 304,几百字节的事。图片仍然留着长缓存——它们的内容确实不怎么变。

nginx缓存

一个让元素永远不出现的写法

2026-07-29

做滚动揭示动画,元素初始状态用 clip-path: inset(0 0 100% 0) 藏起来, 进视口后再由 IntersectionObserver 加个类展开。写完发现有两个元素死活不出现, 滚到跟前也没反应。

排查半天才想明白:被 clip-path 完全裁空的元素,在 IntersectionObserver 眼里 面积是零,永远不算「进入视口」。于是它等着观察器触发,观察器等着它可见—— 自己把自己锁死了。

改法是别让被观察的元素本身不可见:外层 overflow: hidden, 内层做位移,观察挂在外层上。

<span class="line"><i>要揭示的文字</i></span>

.line { overflow: hidden; }
.line > i { transform: translateY(103%); }
.line.in > i { transform: none; }

opacity 为 0 倒是不影响观察器,它只看几何位置。真正会出问题的是 clip-path、祖先的 overflow 裁剪,还有 display: none。

CSS踩坑

srcset 的宽度描述符,浏览器是怎么挑的

2026-07-30

给手机端做图片优化,用 srcset 给了两档尺寸,结果发现手机还是在下大图。 以为是写错了,其实是算错了。

<img src="a.jpg"
     srcset="a-sm.jpg 1300w, a.jpg 1920w"
     sizes="(max-width: 700px) 100vw, 33vw">

浏览器的算法是:先按 sizes 算出这张图实际要显示多宽(CSS 像素), 再乘设备像素比,得到真正需要的物理像素数,然后从 srcset 里挑第一个不小于它的。

iPhone 上 390 CSS 像素宽、DPR 是 3,实际需要 1170px。我给的小图是 1300px, 看着是够的。但换成 430 宽的大屏手机,需求就变成 1290px,1300 只差一点点, 稍微一变就退回去用 1920 的原图了。

另外调试时别信 naturalWidth。带 srcset 的图片,这个属性返回的是 密度校正之后的 CSS 尺寸,不是文件真实宽度——我一度以为图片被压坏了。 要看实际选了哪张,用 currentSrc

前端性能

浏览器自带的语音合成,够用但有两个坑

2026-07-30

想给页面加语音朗读,又不想为了这个引第三方服务。浏览器自带的 SpeechSynthesis 其实够用,零依赖零流量,中文音色各系统都有内置。

第一个坑是长文本。整段丢进去,Chrome 念十几秒就会静默停下,不报错也不触发 onend。解决办法是按句号切开,排成队列一句一句念,前一句 onend 再启动下一句。

第二个坑是 iOS。Safari 只允许在用户手势里首次触发朗读,页面加载完自动念是不行的。 绑在按钮点击上就没问题。

还有个细节:getVoices() 是异步填充的,页面刚加载时拿到的经常是空数组, 得监听 voiceschanged。音色也别指望统一,各家系统内置的不一样。

Web API前端

关于

我是 Shane,做前端开发,平时也折腾一些服务器和小工具。 这个站点是我的个人技术笔记,纯粹记录,不接广告,也不做任何经营性业务。

站点是手写的静态页面,没有框架、没有构建步骤,部署在阿里云上。

站点
shane.xin
邮箱
请在此填写你的联系邮箱
性质
个人非经营性网站