62个站的前端库版本:中位停在四年前,最老的是2011年
本文目录
- 一个版本号,为什么等于一张出厂日期?
- 怎么从压缩过的产物里,把版本号读出来?
- 三条通道,各有各的漏
- 先拿已知答案的样本验尺子
- 漏斗与那56个读不出来的站
- 这些库的年龄,中位数落在哪一年?
- 按库拆开,差别很大
- 最老的那几个,分别是什么?
- 同一个站里,最老和最新的库能差多少年?
- 同一个库的两个版本一起上
- 挂在公共CDN上的库,会不会自动变新?
- 换个平台,库的年龄差多少?
- 版本停在四年前,做SEO的到底亏在哪?
- 体积这一头是真的,但别指望它救命
- 扫描器读的,也是库名加版本号
- 最实际的用处,是拿它当维护信号
- 别把它说成排名因子
- 这一轮的尺子错在哪,又是怎么被抓出来的?
- 自基线:同一份文件再抓一次,答案变不变
- 提取器放宽的那一次,差点引入误报
- 已知的三处口径限制
- 自己站上怎么查一遍出厂年份?
- 常见问题解答
- 库版本旧会直接影响Google排名吗?
- 怎么知道我的站上到底装了哪些库?
- 发现一个库停在五年前,该马上升级吗?
- 公共CDN上的库会自动更新到最新版吗?
- 为什么有的站一个版本号都读不出来?
- 用扫描工具查依赖漏洞,和本文这套办法有什么区别?
- 这批数据能不能套到我自己的站上?
- 权威参考资料
摘要:131个海外品牌电商站,首页能打开的118个,能从字节里读出至少一个前端库版本号的62个。这62个站一共读出99个版本实例,其中97个能对上官方仓库里的发布日期:年龄中位数4.5年,43.3%超过五年,11.3%超过十年。最老的一个是getquip.com上的jQuery 1.7.1,发布于2011年11月,比这批品牌里的一半岁数都大。
接手一个别人做的独立站,判断它有没有人在维护,最快的办法不是看博客更新日期,也不是看页脚版权年份——那两样都能靠脚本自动续命。真正说实话的是页面上跑着的那些库的版本号。
这类“从页面上反读年龄”的活我做过几轮。域名注册日期不可靠,因为存档里的第一份快照往往比注册记录还早;页脚年份更不可靠,把系统时间调到2031年,19%的站页脚跟着变。版本号是这一类线索里最硬的一个,因为它同时带着名字和日期,而且伪造它没有任何好处。
版本号这个东西有意思在哪儿?它不是一个抽象的编号。3.4.1对应的是2019年5月1日那一天,2.8.3对应2014年6月。每一个版本号背后都有一个可查的发布日期,精确到天。一旦你能把页面上的版本号读出来,你就等于拿到了一张时间表:这个站的每一块拼图,分别是哪一年被放上去的。
所以这一轮我干了件挺笨的事:把131个海外品牌电商站的首页拉下来,顺着script标签一份份翻它们的JavaScript,从里面找版本号,再一个个去对发布日期。
一个版本号,为什么等于一张出厂日期?
前端这行有个约定俗成的规矩叫语义化版本,主版本号、次版本号、修订号,三段数字各管一件事。这套规矩本身跟时间无关,但它的副产品跟时间有关:每一次发版都会在包管理器上留一条记录,带着精确到秒的时间戳。npm官方文档里对语义化版本的说明写得很清楚,版本号是发布者对这次改动性质的承诺,而那条发布记录是不可篡改的。
这就给了我们一把尺子。你在页面上读到jQuery 3.6.0,查一下就知道那是2021年3月2日发布的;读到Bootstrap 3.3.5,那是2015年6月16日。中间隔了多少年,一减就出来了。
更关键的是第二层含义。一个库的版本停在某一天,通常不代表那天之后没人碰过这个站,而是代表那天之后没人碰过这一块。有人还在改主题的颜色、加新的落地页、换首页大图,但那个躺在assets目录里的vendor.js,从第一次被放进去就再没人动过。这两件事可以同时成立,而且经常同时成立。
我把97个能定出日期的版本实例按发布年份摊开,分布是这样的:
| 发布年份 | 版本实例数 | 发布年份 | 版本实例数 |
|---|---|---|---|
| 2011 | 1 | 2019 | 8 |
| 2014 | 2 | 2020 | 15 |
| 2015 | 1 | 2021 | 10 |
| 2016 | 7 | 2022 | 9 |
| 2017 | 1 | 2023 | 13 |
| 2018 | 1 | 2024 | 17 |
| —— | —— | 2025—2026 | 12 |
2016年那一格有7个,2019年8个,2020年15个。这些不是历史文物展,是2026年9月这一刻,几十个还在正常卖货的品牌站上正在被浏览器下载执行的代码。
怎么从压缩过的产物里,把版本号读出来?
难点在于现在的前端产物基本都被打包器嚼过一遍。文件名是一串内容哈希,代码被压成一行,变量名剩单个字母。这种情况下版本号还能剩下什么?
三条通道,各有各的漏
第一条是URL路径。有些站还在用公共CDN,地址里就写着版本,比如/ajax/libs/jquery/3.6.0/或者/npm/swiper@8.4.5/。自托管的也有一部分保留了原始文件名,像jquery-1.12.4.min.js这种。这条通道零成本,看一眼地址就有答案,但只对没被改名的文件有效。
第二条是banner注释。打包器默认会保留带感叹号的那类许可注释,/*! jQuery v3.4.1 | (c) JS Foundation */就是典型。这一层的留存情况我在另一轮关于JS产物里许可声明的实测里单独量过,114个站里有40个的产物一条声明都不剩——留声明是默认行为,但删得掉。同一类“构建过程漏出来的痕迹”还有两处:HTML注释里常常记着构建日期和供应商名单,而能整份下载的sourcemap里连未上线的实验名都在。版本号只是这一堆痕迹里最规整的一种。
第三条最硬,是库自己在代码里给版本变量赋的值。jQuery会写.fn.jquery = "3.6.0",core-js会往一个共享对象里塞version: "3.6.5",Moment、Vue、Bootstrap都有类似的自报动作。这一层压缩器压不掉,因为那是运行时要用的字符串。
先拿已知答案的样本验尺子
写完提取器我没直接上全量,先做了一件事:从CDN上下载16份版本已知的官方文件——jQuery 3.7.1、jQuery 1.12.4、Modernizr 2.8.3、Swiper 11.1.14、Bootstrap 5.3.3、lodash 4.17.21这类——拿提取器跑一遍,看它读出来的是不是那个数。
结果是:文件内容通道命中12个,漏掉4个,读错0个。漏掉的那4个(Modernizr、lodash、GSAP、normalize.css)在压缩版里把版本字符串藏得比较深,我的模式没够着。零误报这一点很重要,它意味着后面所有数字都是下限:真实的老版本只会比我数出来的更多,不会更少。
漏斗与那56个读不出来的站
131个站,首页返回200的118个(12个403、1个418)。这118个里,能读出至少一个版本号的62个,占52.5%。剩下56个一个都读不出来。
我一开始以为读不出来是因为这些站脚本少。数了一下,不是:读不出来的站script标签数中位51,读得出来的站是58,基本一个水平。真正的原因是打包方式——它们的产物里,库的身份标识被彻底抹平了,混进业务代码一起压成了一坨字节。
这一点得说清楚。读不出版本号不等于版本新,只等于没人能从外面看出来是什么版本。包括站主自己,如果他手上没有那份lock文件的话。顺带一提,字体文件那一层反而藏不住,授权条款和制作日期就写在字体文件自己的元数据里,压缩器碰不到它。
这些库的年龄,中位数落在哪一年?
99个版本实例里97个能对上发布日期(对不上的两个:一个是被厂商改过版本串的1.12.4-aem,一个是早年没发到npm上的Sentry老版本)。按2026年9月6日这一天算年龄:
| 年龄门槛 | 实例数 | 占比 |
|---|---|---|
| 超过1年 | 86 / 97 | 88.7% |
| 超过2年 | 84 / 97 | 86.6% |
| 超过3年 | 62 / 97 | 63.9% |
| 超过5年 | 42 / 97 | 43.3% |
| 超过8年 | 13 / 97 | 13.4% |
| 超过10年 | 11 / 97 | 11.3% |
中位数1646天,也就是4.5年。平均值4.7年,跟中位数差得不远,说明分布没有被少数极端值拽偏——不是几个老古董拉高了平均,是整体就这么老。
再换个角度看:每个站取它最老的那个库,62个站的这个数中位是4.5年,最大14.8年。也就是说,一半的站身上至少背着一件四年半以上没动过的东西。
按库拆开,差别很大
| 库 | 站数 | 读到几种版本 | 年龄中位 | 最老那一版 |
|---|---|---|---|---|
| core-js | 34 | 11 | 2.0年 | 3.5.0(2019-12) |
| jQuery | 12 | 8 | 6.9年 | 1.7.1(2011-11) |
| Tailwind CSS | 7 | 6 | 1.2年 | 3.3.5(2023-10) |
| js-cookie | 5 | 2 | 3.4年 | 3.0.5(2023-04) |
| Flickity | 5 | 2 | 4.7年 | 2.3.0(2021-12) |
| Moment | 5 | 3 | 2.7年 | 2.24.0(2019-01) |
| Swiper | 5 | 5 | 3.6年 | 4.5(2019-02) |
| Bootstrap | 2 | 2 | 9.7年 | 3.3.5(2015-06) |
| Modernizr | 2 | 1 | 12.3年 | 2.8.3(2014-06) |
Tailwind那一行最年轻,中位1.2年,7个站读到6种版本——说明用它的团队在跟版本。core-js中位2.0年,但它不是团队勤快,是它基本不由人管,跟着框架走,后面会说到。
Swiper那一行有个数字容易被略过:5个站读到5种版本,最老的4.5是2019年2月的,而Swiper当前的主版本已经到14。同一个库,同一批站,跨了十个大版本。
最老的那几个,分别是什么?
把每个站最老的那一件单独拎出来排个序,前十名长这样:
| 站点 | 库与版本 | 发布日期 | 年龄 |
|---|---|---|---|
| getquip.com | jQuery 1.7.1 | 2011-11-21 | 14.8年 |
| dreametech.com | Modernizr 2.8.3 | 2014-06 | 12.3年 |
| monos.com | Modernizr 2.8.3 | 2014-06 | 12.3年 |
| casetify.com | Bootstrap 3.3.5 | 2015-06-16 | 11.2年 |
| bolia.com | Hammer.js 2.0.8 | 2016-04-22 | 10.4年 |
| assos.com | jQuery 1.12.4 | 2016-05-20 | 10.3年 |
| magicspoon.com | jQuery Migrate 1.4.1 | 2016-05-20 | 10.3年 |
| traeger.com | Slick 1.8.1 | 2017-10-03 | 8.9年 |
| charleskeith.com | Bootstrap 4.1.3 | 2018-07-24 | 8.1年 |
| cluse.com | PhotoSwipe 4.1.3 | 2019-01-08 | 7.7年 |
第一名jQuery 1.7.1,2011年11月。那年iPhone 4S刚上市,Chrome还在15版。这个版本比jQuery官方后来发布4.0测试版时公告里提到的整套现代化改造早了十二年多。
Modernizr 2.8.3出现在两个互不相干的站上。这个库当年的用途是探测浏览器支持哪些特性,好让你写降级方案;它的官方变更日志显示2.8.3是2.x系的最后一版,之后3.x换了完全不同的构建方式。留在页面上的是十二年前的那一版。
Slick 1.8.1这个我得替它说句公道话:这个轮播库的最后一个正式版本就是1.8.1,之后作者基本停更了。所以traeger.com上那个8.9年不能算“没升级”,它是“没得升”。这类库在样本里不止一个,判断的时候得区分开——版本老有两种,一种是你没跟,一种是它没动。
同一个站里,最老和最新的库能差多少年?
62个站里有23个读到了两个以上的版本实例,这23个站内部的年代跨度中位是2.8年,最大的一个是casetify.com:
- Bootstrap 3.3.5——2015年6月
- jQuery Migrate 1.4.1——2016年5月
- Moment 2.29.1——2020年10月
- GSAP 3.6.1——2021年3月
- core-js 3.49.0——2026年3月
从2015到2026,同一个首页上并排跑着跨度10.7年的五代东西。这不是这个站特别糟糕,恰恰相反,它说明这个站一直在被改——只是每次改动都只碰新加的那一层,底下那几层原封不动。地质学上管这叫沉积。
同一个库的两个版本一起上
样本里有4个站同时加载了同一个库的两个版本:
| 站点 | 库 | 同时存在的版本 | 年代差 |
|---|---|---|---|
| avocadogreenmattress.com | core-js | 2.6.12 / 3.46.0 | 4.9年 |
| cybex-online.com | core-js | 3.6.5 / 3.27.0 | 2.7年 |
| dreametech.com | jQuery | 1.12.4 / 2.2.3 | 1.5个月 |
| monos.com | jQuery | 2.2.3 / 3.4.1 | 3.1年 |
monos.com那一份vendor.js我打开看了,91 KB的文件里依次躺着Modernizr 2.8.3的注释头、一句jquery-2.2.3.min.js的文件名标记、然后是jQuery 3.4.1的完整banner。三个不同年份的东西被打包工具缝进了同一份文件,然后一起发给每一个访客。
挂在公共CDN上的库,会不会自动变新?
这是我做之前预期最强的一条,结果被数据打了脸。
| 托管方式 | 实例数 | 年龄中位 | 最老 |
|---|---|---|---|
| 自有域名下 | 77 | 4.5年 | 14.8年 |
| 公共CDN | 9 | 6.0年 | 10.3年 |
挂在公共CDN上的那批,比自己托管的还老一年半。
回头想想其实很合理。公共CDN的地址里写死了版本号——/ajax/libs/jquery/1.12.4/jquery.min.js,这个地址永远返回1.12.4,十年后还是。它之所以看起来像“托管在别人那儿”,只是文件不在你服务器上,跟“由别人替你维护”完全是两回事。真正会自动更新的是那种不带版本号的地址,比如各家统计脚本的入口文件,那类东西的漂移我在第三方脚本两天半自己变一遍的那轮实测里量过,跟本文说的完全是两种机制。
反过来讲,自托管也不天然更糟。自托管至少意味着这个文件在你的构建流程里,理论上跟得动;粘在页面上的一条CDN地址,除非有人主动去改那一行HTML,否则它会一直停在被粘上去的那一天。这类“贴上去就没人再看”的外部地址还有两种典型症状:preconnect指着的域名有8个已经不存在了,以及三个月前抄下来的那串完整性哈希把脚本整个拦死。
换个平台,库的年龄差多少?
把118个站按平台特征分组,差别相当明显:
| 平台 | 站数 | 能读出版本的比例 | 每站最老库年龄中位 |
|---|---|---|---|
| Shopify | 67 | 43.3% | 5.8年 |
| Next.js | 13 | 100% | 2.0年 |
| Salesforce | 9 | 55.6% | 4.2年 |
| 认不出平台 | 26 | 46.2% | 4.4年 |
| Magento | 3 | 100% | 2.7年 |
Shopify站的最老库中位5.8年,Next.js站2.0年,差了将近四年。这个差距不是因为用Shopify的团队更懒,而是两种东西的更新责任落在不同人身上:
Shopify主题里那些库是主题作者当年打包进去的,店主升级主题才会跟着换,而升级主题在电商团队眼里等于一次改版——要重新测支付、测集合页、测所有自定义过的区块,这个成本足够让人把它一推再推。改版本身的SEO风险我在换主题改版不掉流量的那份清单里拆过,不是杞人忧天。
Next.js那边的core-js是框架自己注入的,跟着框架版本走,团队升一次Next.js就跟着新一次。责任人不同,节奏就不同。
Next.js站100%能读出版本这一条也来自同一个机制:框架把core-js的版本写在了它自己生成的那份兼容包里,谁都跑不掉。
版本停在四年前,做SEO的到底亏在哪?
这个问题得诚实回答,因为很多人会直接跳到“老版本拖慢速度所以掉排名”,这个链条中间断了好几节。
体积这一头是真的,但别指望它救命
老版本通常更胖。jQuery 1.x走完gzip还有33 KB上下,3.x的slim构建只要24 KB;更要紧的是留着jQuery往往意味着还留着一堆依赖它的插件。这些字节要走完整条关键渲染路径,跟阻塞渲染的CSS与JS怎么拖慢首屏是同一件事。页面总字节数超标之后爬虫会发生什么,我在页面体积把商品链接顶掉的那轮实测里量过具体后果。真要抠这几十KB,比升级库更立竿见影的通常是先把服务器上只压HTML这一种类型的出厂配置改掉,以及给首屏资源排好加载优先级。
扫描器读的,也是库名加版本号
Lighthouse里有一条独立审计项就叫“检测到有已知安全漏洞的前端JavaScript库”,Chrome官方对这条审计的说明写得很直白:它比对的就是库名加版本号。你读得出版本号,扫描器也读得出。这条不进排名因子,但它进所有交付给客户的体检报告。安全这一头真出事的代价有多大,看看用破解版插件被注入之后被整站移出索引的那些案例就知道,老版本本身不等于被攻破,但它把门槛降低了。
最实际的用处,是拿它当维护信号
做竞品分析、做并购尽调、接手一个别人留下的站,你需要一个五分钟能得出结论的判断依据。版本号就是。一个站的库停在2016年,你基本可以推断它的前端在2016年之后只被打过补丁,没被系统性地碰过——这个推断比看博客更新频率靠谱得多,因为版本号没人会为了好看去伪造。
反过来对自己的站也一样。robots.txt那87行有33个站一个字没改过、页脚版权年份有19%是脚本自动跳的、head里的过时标签换个平台只是换一批老代码——这些都是同一类信号的不同切面。版本号是其中最难抵赖的一个,因为它同时带着名字和日期。
别把它说成排名因子
Google从来没说过“依赖版本旧会降权”,也不可能这么说。老版本影响的是它下游的那些东西:体积、渲染、可访问性、有没有踩到爬虫读不到内容的坑。这类坑到底发生不发生,我在JS渲染的三件担心里两件没发生那一轮里做过对照——真出问题的比大家以为的少,但一旦出就是硬伤。Google关于JavaScript SEO的基础文档讲的也是这个逻辑:它不关心你用什么版本,它关心渲染完之后有没有东西。
这一轮的尺子错在哪,又是怎么被抓出来的?
自基线:同一份文件再抓一次,答案变不变
做完全量我挑了34个站复测:把之前读出版本的那份文件重新下载一遍,比对字节哈希和提取出的版本号。结果是34个站的版本号全部一致,字节哈希只有casetify.com一个站变了——它中间发过版,文件内容变了,但读出来的版本号没变。这条自基线过了,后面的数字才敢用。
提取器放宽的那一次,差点引入误报
第一版提取器只命中10个已知样本,我想提高覆盖率,加了几条更宽松的模式,比如“文件里出现VERSION = "x.y.z"就当作版本”。这类模式极容易把业务代码里随便一个版本常量当成库版本。
改法是给每条宽松模式加一道前置门槛:文件里必须先出现这个库的特征串——比如要认lodash的VERSION,文件里得先有lodash.com;要认Moment,得先有momentjs.com。加了门槛之后命中从10升到12,而误报仍然是0。
这条经验值得单独记:放宽判据之前,先想好用什么把新引进来的噪音挡在外面。上一轮我在样式表里挖选择器时就是因为少了这一步,一个“修正版”正则把@media块里的东西整片挖没了,类名从1084掉到225我还差点信了——那次的教训写在CSS覆盖率那一轮里。
已知的三处口径限制
第一,每个站只抓了5份JS,是按文件名关键词加体积挑的,不是全部。所以“这个站最老的库”严格讲是“我抓到的那几份里最老的”,真实情况只会更老。
第二,读不出版本的56个站被整体排除在年龄统计之外,这批站的真实年龄未知。把它们默认当成“新”或者“老”都是错的。
第三,12个站返回403、1个返回418,进不去。这批站里有没有更老的东西,不知道。顺便说一句,这一轮每站只发7个请求,131个站跑完一个429都没有;上一轮每站17个请求,同一批站里50个把我限流了。站内连续请求数是独立于速率的第二个变量,这条教训我付过学费。
自己站上怎么查一遍出厂年份?
不用写脚本,浏览器里十分钟能查完,顺序是这样:
- 打开自己站的首页,开发者工具切到网络面板,按类型筛JS,按体积倒序。最大的三到五份就是要看的对象。
- 逐份点开预览,用Ctrl+F搜
version、搜/*!、搜@license。banner一般在文件最开头,几秒钟能扫完。 - 读到的每个“库名加版本”,去包管理器官网搜一下那个版本的发布日期。这一步没有捷径,但也就几分钟。
- 再看一遍HTML里的script标签,凡是地址里带版本号的(
?ver=、@1.2.3、/3.6.0/)单独列出来。这些是最容易被忘掉的,因为它们不在构建流程里,只在某个模板文件的某一行。 - 把这份清单存下来,写上今天的日期。半年后再跑一遍,对比一下哪些动了、哪些没动。没动的那些就是你真正的技术债台账。
做完这一遍,通常会发现两类东西。一类是你知道它老但一直没排期的,那属于计划问题;另一类是你完全不知道它在页面上——某个前同事加的插件、某个营销活动留下的脚本、某个第三方应用带进来的依赖。第二类才是真麻烦,因为没人认领的东西也没人会去升。哪些第三方域名是从哪儿冒出来的,可以对着只读HTML看不到的那8个第三方域名那一份清单一起盘。
保哥自己带客户做这一步时习惯加一列“谁能改”。一个户外装备品牌的独立站,前端库清单拉出来11项,其中4项写着“主题自带,改不了”——那4项后来成了他们决定换主题的最直接理由,比任何速度报告都管用。清单本身不解决问题,但它把“要不要动”变成了一个能开会讨论的东西。
常见问题解答
库版本旧会直接影响Google排名吗?
不会直接影响。Google的排名系统里没有“依赖版本”这个输入。它影响的是下游:页面字节数、渲染耗时、Core Web Vitals的三个指标、以及极端情况下爬虫拿不到内容。这些才是有关系的东西。把“库旧”和“掉排名”直接画等号,等于跳过了中间三四个环节。
怎么知道我的站上到底装了哪些库?
如果站是你自己构建的,最准的是lock文件(package-lock.json或yarn.lock),它记着每个包的确切版本。如果是Shopify、WordPress这类主题驱动的站,lock文件在主题作者手里,你只能从产物里反读——就是本文用的那套办法。两种情况都建议把结果存成一份台账,而不是每次现查。
发现一个库停在五年前,该马上升级吗?
先分清是“没跟”还是“没得升”。作者已经停更的库(Slick、Modernizr 2.x这类),升级选项根本不存在,要动就是换掉整个库,那是个项目不是个补丁。如果是还在维护的库,看跨了几个大版本:跨一个大版本通常是半天的活,跨三个以上基本等于重写调用它的所有代码,得排期。
公共CDN上的库会自动更新到最新版吗?
带版本号的地址不会,永远返回你写死的那一版。不带版本号的地址(各家统计和营销脚本的入口)会,而且是供应商想换就换、不通知你。本文实测里公共CDN上的库年龄中位反而比自托管的还老一年半,原因就在这儿。
为什么有的站一个版本号都读不出来?
打包器把库的身份标识和业务代码一起压平了,banner注释被删、版本字符串被内联成常量。这不是站主刻意藏,是构建工具的默认行为。对外看不出来的代价是对内也难查——出了安全通告,你连自己中没中招都得翻lock文件才知道。
用扫描工具查依赖漏洞,和本文这套办法有什么区别?
扫描工具查的是“这个版本有没有已公开的漏洞”,本文查的是“这个版本是哪一年的”。前者是安全问题,后者是维护问题。一个库可以既没有已知漏洞、又已经八年没人碰——那种情况扫描工具全绿,而它仍然是一笔债。两件事互补,不互相替代。
这批数据能不能套到我自己的站上?
不能直接套。样本是131个海外品牌电商站,平台构成里Shopify占了一多半,这个结构决定了很多数字。但方法可以直接套,而且成本极低——十分钟就能得出你自己站的那张表,那张表比任何行业中位数都有用。
权威参考资料
本文标题:《62个站的前端库版本:中位停在四年前,最老的是2011年》
本文链接:https://zhangwenbao.com/frontend-library-version-age-audit.html
版权声明:本文原创,转载与引用请注明作者与原文链接。许可协议: CC BY 4.0