[{"data":1,"prerenderedAt":1883},["ShallowReactive",2],{"blog-taxonomies":3,"blog-stats":507,"post-2025\u002F2025-11-22-关于前端包管理器npmpnpmyarn和bun":1083},[4,11,16,21,25,29,36,39,44,47,52,58,63,66,71,76,80,84,87,93,96,101,105,110,113,117,122,127,132,136,141,143,146,151,155,157,162,165,169,173,176,179,183,186,190,193,197,200,205,208,211,214,219,222,228,234,237,239,245,249,253,256,260,266,271,276,281,283,287,290,293,298,301,304,306,308,311,314,316,318,321,327,332,337,342,347,352,354,356,358,361,365,367,369,371,374,376,378,381,386,390,393,396,400,404,407,411,413,415,417,421,423,425,428,431,433,435,437,439,441,443,446,448,450,454,456,459,461,463,465,467,469,471,473,477,483,486,488,491,493,495,497,499,501,503,505],{"category":5,"tags":6},"笔记",[7,8,9,10],"QQ","插件","LiteLoader","美化",{"category":12,"tags":13},"其他",[14,15,10],"博客","IP签名",{"category":17,"tags":18},"杂谈",[19,20],"互联网","观点",{"category":5,"tags":22},[14,23,24],"GitHub","数据可视化",{"category":5,"tags":26},[14,27,28],"访问统计","不蒜子",{"category":30,"tags":31},"工具",[32,33,34,35],"Python","爬虫","选课脚本","广州大学",{"category":5,"tags":37},[38,5],"数学分析",{"category":40,"tags":41},"算法",[42,40,43],"数据结构","线性表",{"category":40,"tags":45},[42,46],"复习",{"category":5,"tags":48},[49,50,51],"高等代数","线性代数","知识点",{"category":5,"tags":53},[14,54,55,56,57],"Typora","PicGo","图床","Markdown",{"category":30,"tags":59},[60,61,62],"Jupyter","工具配置","中文",{"category":30,"tags":64},[65,61,10],"VSCode",{"category":30,"tags":67},[68,69,70,35],"路由器","OpenWrt","校园网",{"category":12,"tags":72},[73,74,75],"网络协议","Wireshark","DEVP2P",{"category":5,"tags":77},[73,74,78,79],"Lua","解码器",{"category":5,"tags":81},[82,5,83],"常微分方程","数学",{"category":5,"tags":85},[86,5,83],"概率论",{"category":30,"tags":88},[89,90,91,92],"Docker","WSL","Windows","磁盘清理",{"category":30,"tags":94},[32,33,95,35],"课程表",{"category":5,"tags":97},[14,98,99,100],"Hexo","Gitalk","评论系统",{"category":5,"tags":102},[14,103,98,104],"Obsidian","写作工具",{"category":12,"tags":106},[107,108,109],"课题研究","泊松分布","参数估计",{"category":12,"tags":111},[107,108,109,112],"组会",{"category":12,"tags":114},[109,115,108,116],"IM模型","统计学",{"category":17,"tags":118},[119,120,121],"软考","网络规划","信息系统项目管理",{"category":30,"tags":123},[124,125,126],"Arch Linux","AppImage","Linux",{"category":5,"tags":128},[129,130,131],"编程语言","计算机科学","内存管理",{"category":5,"tags":133},[134,135],"Canvas","前端",{"category":5,"tags":137},[14,138,139,140],"GitHub Actions","Vite","部署",{"category":5,"tags":142},[135],{"category":5,"tags":144},[135,145],"系统设计",{"category":5,"tags":147},[135,148,149,150],"安全","XSS","CSRF",{"category":152,"tags":153},"项目",[134,154],"PixiJS",{"category":152,"tags":156},null,{"category":40,"tags":158},[42,159,160,161],"栈","队列","JavaScript",{"category":40,"tags":163},[42,164,161],"哈希表",{"category":5,"tags":166},[167,161,168],"ES6","Set",{"category":40,"tags":170},[40,171,172],"动态规划","背包问题",{"category":40,"tags":174},[40,175],"滑动窗口",{"category":40,"tags":177},[42,178,161],"链表",{"category":40,"tags":180},[40,181,161,182],"ACM","面试",{"category":40,"tags":184},[40,161,185],"数据类型转换",{"category":40,"tags":187},[42,188,178,189],"顺序表","TypeScript",{"category":5,"tags":191},[135,154,134,192],"渲染",{"category":5,"tags":194},[135,195,134,196],"富文本编辑","wangEditor",{"category":40,"tags":198},[199,40,178],"力扣",{"category":5,"tags":201},[202,203,204,91],"DNS","加密","网络安全",{"category":5,"tags":206},[135,207],"Axios",{"category":5,"tags":209},[135,207,189,210],"接口封装",{"category":5,"tags":212},[135,213,145],"JWT",{"category":152,"tags":215},[134,216,217,154,218],"画布","React","项目开发",{"category":17,"tags":220},[221],"日志",{"category":5,"tags":223},[224,225,226,227],"Neo4j","图数据库","中心度分析","PageRank",{"category":12,"tags":229},[230,231,232,233],"论文","机器学习","随机森林","保险欺诈",{"category":5,"tags":235},[236],"人工智能",{"category":5,"tags":238},[231,40,32],{"category":12,"tags":240},[241,242,243,244],"共识算法","最长链","区块链","算法设计",{"category":5,"tags":246},[161,247,248,135],"闭包","作用域",{"category":5,"tags":250},[14,138,251,252],"RSS","自动化",{"category":30,"tags":254},[89,90,91,255],"安装",{"category":30,"tags":257},[258,91,259],"MSYS2","开发环境",{"category":5,"tags":261},[262,263,264,265],"团队协作","Git","Commit规范","代码审查",{"category":30,"tags":267},[268,269,270],"oh-my-posh","终端美化","CLI",{"category":5,"tags":272},[273,274,275],"上下文工程","Agent","RAG",{"category":5,"tags":277},[278,279,280],"前端工程化","技术选型","工程基建",{"category":5,"tags":282},[275],{"category":5,"tags":284},[285,286],"SDD","规格驱动开发",{"category":5,"tags":288},[289],"neovim",{"category":5,"tags":291},[292],"技术写作",{"category":5,"tags":294},[295,32,296,89,297],"FastAPI","后端","工程实践",{"category":152,"tags":299},[274,300],"Harness Engineering",{"category":5,"tags":302},[303],"RFC",{"category":152,"tags":305},[274,300,275],{"category":152,"tags":307},[274],{"category":40,"tags":309},[199,40,161,310],"两数之和",{"category":40,"tags":312},[199,40,161,313],"双指针",{"category":40,"tags":315},[199,40,161,178],{"category":40,"tags":317},[199,40,161,175],{"category":40,"tags":319},[199,40,161,320],"回溯",{"category":5,"tags":322},[323,324,325,326],"分布式处理与计算","Spark","Scala","RDD",{"category":5,"tags":328},[323,329,330,331],"线性回归","逻辑回归","监督学习",{"category":5,"tags":333},[323,334,335,336],"数值优化","牛顿法","收敛性",{"category":5,"tags":338},[323,339,340,341],"PCA","数据降维","正则化",{"category":5,"tags":343},[323,344,345,346],"聚类","层次聚类","K-Means",{"category":5,"tags":348},[323,349,350,351],"随机模拟","统计推断","EM算法",{"category":5,"tags":353},[24],{"category":5,"tags":355},[24],{"category":5,"tags":357},[24],{"category":40,"tags":359},[42,360],"图论",{"category":40,"tags":362},[42,363,364],"树","二叉树",{"category":40,"tags":366},[42,160],{"category":40,"tags":368},[42],{"category":40,"tags":370},[42,159],{"category":40,"tags":372},[42,40,373],"复杂度分析",{"category":40,"tags":375},[42,43,178],{"category":40,"tags":377},[42,43,188],{"category":40,"tags":379},[42,380],"基础概念",{"category":17,"tags":382},[383,384,385],"随记","照片","高中",{"category":17,"tags":387},[383,388,389],"团建","战地",{"category":17,"tags":391},[383,392],"生活",{"category":17,"tags":394},[383,395],"文学",{"category":17,"tags":397},[383,398,399],"SCP","模因",{"category":17,"tags":401},[383,402,403],"画集","艺术",{"category":17,"tags":405},[14,406,383],"写作",{"category":17,"tags":408},[383,409,410],"音乐","摇滚",{"category":17,"tags":412},[383,392],{"category":17,"tags":414},[383,409],{"category":17,"tags":416},[383,35,392],{"category":17,"tags":418},[383,419,420],"同人","手书",{"category":17,"tags":422},[383,392],{"category":17,"tags":424},[383,392],{"category":17,"tags":426},[383,427],"植物",{"category":17,"tags":429},[383,430],"读书",{"category":17,"tags":432},[383,392],{"category":17,"tags":434},[383,392],{"category":17,"tags":436},[383,392],{"category":17,"tags":438},[383,392],{"category":17,"tags":440},[383,392],{"category":17,"tags":442},[383,392],{"category":17,"tags":444},[14,445,383],"一周年",{"category":17,"tags":447},[383,392],{"category":17,"tags":449},[383,392],{"category":17,"tags":451},[35,452,453],"开源组织","SITE-193",{"category":17,"tags":455},[383,392],{"category":17,"tags":457},[383,458],"年末",{"category":17,"tags":460},[383,392],{"category":17,"tags":462},[383,392],{"category":17,"tags":464},[14,17],{"category":17,"tags":466},[383,392],{"category":17,"tags":468},[383,392],{"category":17,"tags":470},[383,392],{"category":17,"tags":472},[383,392],{"category":17,"tags":474},[383,475,476],"游戏","Control",{"category":30,"tags":478},[479,480,481,482],"Quadim","图像处理","四叉树","Rust",{"category":17,"tags":484},[14,485],"上线纪念",{"category":17,"tags":487},[383,395],{"category":30,"tags":489},[203,490],"测试",{"category":17,"tags":492},[383],{"category":17,"tags":494},[383,392],{"category":17,"tags":496},[383],{"category":5,"tags":498},[40],{"category":5,"tags":500},[40],{"category":5,"tags":502},[40],{"category":5,"tags":504},[40],{"category":5,"tags":506},[40],[508,512,516,520,524,528,532,536,540,544,548,552,556,560,564,568,572,576,579,583,587,590,594,598,602,606,610,614,618,622,626,630,634,638,642,646,650,654,658,662,666,670,674,678,682,686,690,694,698,702,706,710,714,718,722,726,730,734,738,742,746,750,754,758,762,766,770,774,778,782,786,790,794,798,801,805,809,813,817,821,825,829,833,837,841,845,849,853,857,861,865,869,873,877,881,885,889,893,897,901,904,907,911,915,919,923,927,930,934,938,942,946,950,954,957,961,965,969,973,977,981,984,988,992,996,1000,1004,1007,1011,1015,1019,1023,1027,1031,1035,1039,1043,1047,1051,1055,1059,1063,1067,1071,1075,1079],{"path":509,"words":510,"published":511,"date":156},"\u002Fposts\u002F2024\u002F2024-06-25-liteloaderqqnt",1296,"2024-06-25 11:03:08",{"path":513,"words":514,"published":515,"date":156},"\u002Fposts\u002F2024\u002F2024-06-26-ip签名",0,"2024-06-26 22:53:53",{"path":517,"words":518,"published":519,"date":156},"\u002Fposts\u002F2024\u002F2024-06-29-何加盐中文互联网正在加速崩塌",4853,"2024-06-29 00:47:13",{"path":521,"words":522,"published":523,"date":156},"\u002Fposts\u002F2024\u002F2024-07-12-github-chart",270,"2024-07-12 20:02:50",{"path":525,"words":526,"published":527,"date":156},"\u002Fposts\u002F2024\u002F2024-07-14-访客数统计",192,"2024-07-24 01:34:28",{"path":529,"words":530,"published":531,"date":156},"\u002Fposts\u002F2024\u002F2024-07-26-广大选课脚本",110,"2024-07-26 01:34:28",{"path":533,"words":534,"published":535,"date":156},"\u002Fposts\u002F2024\u002F2024-07-26-数学分析笔记其二",14,"2024-07-26 23:54:02",{"path":537,"words":538,"published":539,"date":156},"\u002Fposts\u002F2024\u002F2024-07-26-数据结构复习其二",12856,"2024-07-26 02:48:08",{"path":541,"words":542,"published":543,"date":156},"\u002Fposts\u002F2024\u002F2024-07-26-数据结构复习相关",32856,"2024-07-26 02:24:14",{"path":545,"words":546,"published":547,"date":156},"\u002Fposts\u002F2024\u002F2024-07-26-线性代数与空间解析几何知识点全汇总",6110,"2024-07-26 03:27:55",{"path":549,"words":550,"published":551,"date":156},"\u002Fposts\u002F2024\u002F2024-07-31-typora-picgo-兰空图床打造markdown写作环境",311,"2024-07-31 12:56:23",{"path":553,"words":554,"published":555,"date":156},"\u002Fposts\u002F2024\u002F2024-09-02-jupyter配置中文",441,"2024-09-02 23:49:05",{"path":557,"words":558,"published":559,"date":156},"\u002Fposts\u002F2024\u002F2024-09-02-自定义vscode背景图片",100,"2024-09-02 23:39:00",{"path":561,"words":562,"published":563,"date":156},"\u002Fposts\u002F2024\u002F2024-09-03-制作gzhu校园网路由器",1423,"2024-09-03 23:43:18",{"path":565,"words":566,"published":567,"date":156},"\u002Fposts\u002F2024\u002F2024-09-26-09-25-2024",136,"2024-09-26 00:49:26",{"path":569,"words":570,"published":571,"date":156},"\u002Fposts\u002F2024\u002F2024-11-04-创建一个网络包解码器分析devp2p协议",1943,"2024-11-04 16:45:19",{"path":573,"words":574,"published":575,"date":156},"\u002Fposts\u002F2024\u002F2025-03-09-2024一些笔记常微分",16,"2024-12-09 22:51:00",{"path":577,"words":574,"published":578,"date":156},"\u002Fposts\u002F2024\u002F2025-03-09-2024一些笔记概率论","2025-03-09 22:51:00",{"path":580,"words":581,"published":582,"date":156},"\u002Fposts\u002F2025\u002F2025-03-01-windows下释放docker占用的wsl空间",164,"2025-03-01 13:54:15",{"path":584,"words":585,"published":586,"date":156},"\u002Fposts\u002F2025\u002F2025-03-01-爬虫实战-爬取广州大学课程表",774,"2025-03-01 14:55:48",{"path":588,"words":530,"published":589,"date":156},"\u002Fposts\u002F2025\u002F2025-04-03-hexo集成gitalk的问题","2025-04-03 14:26:34",{"path":591,"words":592,"published":593,"date":156},"\u002Fposts\u002F2025\u002F2025-09-22-记一次配置obsidian配合hexo写博客",92,"2025-09-22 16:47:52",{"path":595,"words":596,"published":597,"date":156},"\u002Fposts\u002F2025\u002F2025-10-24-10-20课题",204,"2025-10-24 08:15:01",{"path":599,"words":600,"published":601,"date":156},"\u002Fposts\u002F2025\u002F2025-11-02-组会朝花夕拾",501,"2025-11-02 18:56:59",{"path":603,"words":604,"published":605,"date":156},"\u002Fposts\u002F2025\u002F2025-11-05-基于随机加权推断模型im的参数估计算法",481,"2025-11-05 20:51:41",{"path":607,"words":608,"published":609,"date":156},"\u002Fposts\u002F2025\u002F2025-11-09-关于软考高项网规",884,"2025-11-09 19:54:52",{"path":611,"words":612,"published":613,"date":156},"\u002Fposts\u002F2025\u002F2025-11-17-arch-linux运行appimage相关",365,"2025-11-17 09:59:59",{"path":615,"words":616,"published":617,"date":156},"\u002Fposts\u002F2025\u002F2025-11-19-相关概念",2058,"2025-11-19 11:06:20",{"path":619,"words":620,"published":621,"date":156},"\u002Fposts\u002F2025\u002F2025-11-21-web可视化实践canvas",6393,"2025-11-21 14:50:48",{"path":623,"words":624,"published":625,"date":156},"\u002Fposts\u002F2025\u002F2025-11-22-使用-github-actions-自动部署基于vite的项目到-github-pages",272,"2025-11-23 01:59:24",{"path":627,"words":628,"published":629,"date":156},"\u002Fposts\u002F2025\u002F2025-11-22-关于前端包管理器npmpnpmyarn和bun",4890,"2025-11-22 21:21:15",{"path":631,"words":632,"published":633,"date":156},"\u002Fposts\u002F2025\u002F2025-11-23-undo-redo-机制具体实现",7839,"2025-11-23 19:15:00",{"path":635,"words":636,"published":637,"date":156},"\u002Fposts\u002F2025\u002F2025-11-27-前端安全-关于xss与crsf",1866,"2025-11-28 02:05:25",{"path":639,"words":640,"published":641,"date":156},"\u002Fposts\u002F2025\u002F2025-12-05-现代协同-2d-画布编辑器pixijs-v8",4285,"2025-12-05 02:00:16",{"path":643,"words":644,"published":645,"date":156},"\u002Fposts\u002F2025\u002F2025-12-07-杂谈-课题组系统设计",3710,"2025-12-07 21:09:33",{"path":647,"words":648,"published":649,"date":156},"\u002Fposts\u002F2025\u002F2025-12-25-javascript中的数组方法与栈stack和队列queue的实现",474,"2025-11-29 16:58:15",{"path":651,"words":652,"published":653,"date":156},"\u002Fposts\u002F2025\u002F2025-12-25-关于javascript-实现哈希表",189,"2025-11-27 11:31:39",{"path":655,"words":656,"published":657,"date":156},"\u002Fposts\u002F2025\u002F2025-12-25-关于javascript的set方法",332,"2025-12-25 15:26:18",{"path":659,"words":660,"published":661,"date":156},"\u002Fposts\u002F2025\u002F2025-12-25-关于动态规划(背包问题为例)",1238,"2025-11-17 18:24:04",{"path":663,"words":664,"published":665,"date":156},"\u002Fposts\u002F2025\u002F2025-12-25-关于滑动窗口",712,"2025-11-20 15:04:40",{"path":667,"words":668,"published":669,"date":156},"\u002Fposts\u002F2025\u002F2025-12-25-关于链表(javascript)",500,"2025-11-25 13:00:48",{"path":671,"words":672,"published":673,"date":156},"\u002Fposts\u002F2025\u002F2025-12-25-面试算法acm模式构建构建输入输出模板",2051,"2025-12-25 23:09:45",{"path":675,"words":676,"published":677,"date":156},"\u002Fposts\u002F2025\u002F2025-12-26-javascript-数字数组字符串的处理",483,"2025-12-26 11:20:25",{"path":679,"words":680,"published":681,"date":156},"\u002Fposts\u002F2025\u002F2025-12-26-javascripttypescript-的顺序表链表实现",446,"2025-12-26 12:16:12",{"path":683,"words":684,"published":685,"date":156},"\u002Fposts\u002F2025\u002F2025-12-27-前端画布设计vol-1-实现基础yuan素渲染和状态控制",2136,"2025-12-28 01:23:08",{"path":687,"words":688,"published":689,"date":156},"\u002Fposts\u002F2025\u002F2025-12-27-前端画布设计vol-2-实现富文本编辑",1838,"2025-12-28 02:58:47",{"path":691,"words":692,"published":693,"date":156},"\u002Fposts\u002F2025\u002F2025-12-27-算法刷题-关于链表操作",2135,"2025-12-27 16:12:42",{"path":695,"words":696,"published":697,"date":156},"\u002Fposts\u002F2025\u002F2025-12-27-配置dnscrypt-proxy实现加密dns服务windows",1899,"2025-11-15 01:06:37",{"path":699,"words":700,"published":701,"date":156},"\u002Fposts\u002F2025\u002F2025-12-28-前端-关于网络请求xhrajaxfetchaxios",7788,"2025-10-28 15:48:46",{"path":703,"words":704,"published":705,"date":156},"\u002Fposts\u002F2025\u002F2025-12-28-前端-接口封装与请求规范axios为例",2373,"2025-12-29 01:01:18",{"path":707,"words":708,"published":709,"date":156},"\u002Fposts\u002F2025\u002F2025-12-28-前端-身份验证管理-基于-jwt-token-的实现",2709,"2025-11-23 22:04:41",{"path":711,"words":712,"published":713,"date":156},"\u002Fposts\u002F2025\u002F2025-12-29-记canvas画布项目开发",6254,"2025-12-29 04:52:15",{"path":715,"words":716,"published":717,"date":156},"\u002Fposts\u002F2025\u002Fabout-site",116,"2025-03-31 16:44:24",{"path":719,"words":720,"published":721,"date":156},"\u002Fposts\u002F2025\u002F使用neo4j图数据科学库gds进行中心度分析",143,"2025-04-02 23:37:24",{"path":723,"words":724,"published":725,"date":156},"\u002Fposts\u002F2026\u002F2026-01-03-论文实训草稿",10445,"2026-01-03 14:10:49",{"path":727,"words":728,"published":729,"date":156},"\u002Fposts\u002F2026\u002F2026-01-06-人工智能导论",11461,"2026-01-07 03:25:49",{"path":731,"words":732,"published":733,"date":156},"\u002Fposts\u002F2026\u002F2026-01-07-机器学习相关算法",82,"2026-01-08 02:28:13",{"path":735,"words":736,"published":737,"date":156},"\u002Fposts\u002F2026\u002F2026-01-16-关于低复杂度最长链共识算法设计",4466,"2026-01-16 10:33:23",{"path":739,"words":740,"published":741,"date":156},"\u002Fposts\u002F2026\u002F2026-01-19-杂记2026-01-19",5754,"2026-01-20 01:03:26",{"path":743,"words":744,"published":745,"date":156},"\u002Fposts\u002F2026\u002F2026-01-22-github-action-自动同步博客到-github-主页",1069,"2026-01-22 16:31:47",{"path":747,"words":748,"published":749,"date":156},"\u002Fposts\u002F2026\u002F2026-03-01-windows-wsl安装docker",257,"2026-03-01 20:55:31",{"path":751,"words":752,"published":753,"date":156},"\u002Fposts\u002F2026\u002F2026-03-14-关于-msys2",701,"2026-03-15 03:05:04",{"path":755,"words":756,"published":757,"date":156},"\u002Fposts\u002F2026\u002F2026-03-28-团队项目协作规范随记",2419,"2026-03-29 05:33:50",{"path":759,"words":760,"published":761,"date":156},"\u002Fposts\u002F2026\u002F2026-03-29-oh-my-posh配置分享",104,"2026-03-29 16:34:43",{"path":763,"words":764,"published":765,"date":156},"\u002Fposts\u002F2026\u002F2026-06-13-从上下文工程到-agent-harness-engineering",8840,"2026-06-13 21:06:20",{"path":767,"words":768,"published":769,"date":156},"\u002Fposts\u002F2026\u002F2026-07-13-简谈前端基建质量标准与ai友好建设",3814,"2026-07-13 21:06:20",{"path":771,"words":772,"published":773,"date":156},"\u002Fposts\u002F2026\u002F2026-07-15-agentic-rag-系统实践",13357,"2026-07-15 21:06:20",{"path":775,"words":776,"published":777,"date":156},"\u002Fposts\u002F2026\u002F2026-07-18-简谈sdd规范驱动开发-copy",4476,"2026-07-18 11:06:20",{"path":779,"words":780,"published":781,"date":156},"\u002Fposts\u002F2026\u002F2026-07-20-个人neovim配置分享",366,"2026-07-22 13:46:50",{"path":783,"words":784,"published":785,"date":156},"\u002Fposts\u002F2026\u002F2026-07-20-关于技术写作-设计文档",6763,"2026-07-20 13:46:50",{"path":787,"words":788,"published":789,"date":156},"\u002Fposts\u002F2026\u002F2026-07-29-fastapi-工程实践随记",3417,"2026-07-29 04:58:34",{"path":791,"words":792,"published":793,"date":156},"\u002Fposts\u002Frfc_agentic_project\u002F2026-06-19-rfc-liskin_code_agent",3563,"2026-06-19 15:18:20",{"path":795,"words":796,"published":797,"date":156},"\u002Fposts\u002Frfc_agentic_project\u002F2026-07-25-rfc-glushkov_ogas",13461,"2026-07-28 23:46:50",{"path":799,"words":800,"published":793,"date":156},"\u002Fposts\u002Frfc_agentic_project\u002F2026-08-03-adr-ogas-arkhiv",2698,{"path":802,"words":803,"published":804,"date":156},"\u002Fposts\u002Frfc_agentic_project\u002Frag\u002F2026-08-12-arkhiv_rag-vol-1",4366,"2026-08-12 15:18:20",{"path":806,"words":807,"published":808,"date":156},"\u002Fposts\u002Fleetcode\u002F2025-12-25-力扣百题速练javascripttypescriptvol-1",3108,"2025-12-25 13:57:08",{"path":810,"words":811,"published":812,"date":156},"\u002Fposts\u002Fleetcode\u002F2025-12-26-力扣百题速练javascripttypescriptvol-2",3185,"2025-12-26 13:57:08",{"path":814,"words":815,"published":816,"date":156},"\u002Fposts\u002Fleetcode\u002F2025-12-28-力扣百题速练javascripttypescriptvol-3",2533,"2025-12-29 02:15:44",{"path":818,"words":819,"published":820,"date":156},"\u002Fposts\u002Fleetcode\u002F2026-01-17-力扣百题速练javascripttypescriptvol-4",3024,"2026-01-17 22:53:01",{"path":822,"words":823,"published":824,"date":156},"\u002Fposts\u002Fleetcode\u002F2026-01-19-力扣百题速练javascripttypescriptvol-5",293,"2026-01-19 15:15:12",{"path":826,"words":827,"published":828,"date":156},"\u002Fposts\u002F分布式处理与计算\u002F分布式处理与计算spark系统与scala语言",4320,"2026-07-06 14:10:49",{"path":830,"words":831,"published":832,"date":156},"\u002Fposts\u002F分布式处理与计算\u002F分布式处理与计算分类与回归分析",2081,"2026-07-07 12:40:29",{"path":834,"words":835,"published":836,"date":156},"\u002Fposts\u002F分布式处理与计算\u002F分布式处理与计算数值优化方法",2182,"2026-07-07 13:50:29",{"path":838,"words":839,"published":840,"date":156},"\u002Fposts\u002F分布式处理与计算\u002F分布式处理与计算数据降维",1583,"2026-07-07 21:50:29",{"path":842,"words":843,"published":844,"date":156},"\u002Fposts\u002F分布式处理与计算\u002F分布式处理与计算聚类分析",1882,"2026-07-07 16:50:29",{"path":846,"words":847,"published":848,"date":156},"\u002Fposts\u002F分布式处理与计算\u002F分布式处理与计算随机模拟与统计推断",2433,"2026-07-06 22:40:29",{"path":850,"words":851,"published":852,"date":156},"\u002Fposts\u002F数据可视化\u002F2026-06-14-数据可视化-基本图像",10011,"2026-06-14 20:18:20",{"path":854,"words":855,"published":856,"date":156},"\u002Fposts\u002F数据可视化\u002F2026-06-14-数据可视化-概论",10030,"2026-06-14 15:18:20",{"path":858,"words":859,"published":860,"date":156},"\u002Fposts\u002F数据可视化\u002F2026-06-15-数据可视化-可视化库",2032,"2026-06-15 20:18:20",{"path":862,"words":863,"published":864,"date":156},"\u002Fposts\u002F数据结构\u002F2025-09-29-数据结构-图",5451,"2025-04-15 20:48:06",{"path":866,"words":867,"published":868,"date":156},"\u002Fposts\u002F数据结构\u002F2025-09-29-数据结构-树",4211,"2025-04-30 02:19:06",{"path":870,"words":871,"published":872,"date":156},"\u002Fposts\u002F数据结构\u002F2025-09-29-数据结构-队列",921,"2025-04-11 15:30:08",{"path":874,"words":875,"published":876,"date":156},"\u002Fposts\u002F数据结构\u002F关于数据结构的一些想法",456,"2025-04-04 19:13:49",{"path":878,"words":879,"published":880,"date":156},"\u002Fposts\u002F数据结构\u002F数据结构-栈",920,"2025-04-18 00:17:37",{"path":882,"words":883,"published":884,"date":156},"\u002Fposts\u002F数据结构\u002F数据结构-算法复杂度",2153,"2025-04-05 16:09:55",{"path":886,"words":887,"published":888,"date":156},"\u002Fposts\u002F数据结构\u002F数据结构-线性表链表",4572,"2025-04-06 00:24:40",{"path":890,"words":891,"published":892,"date":156},"\u002Fposts\u002F数据结构\u002F数据结构-线性表顺序表",4191,"2025-04-05 22:09:59",{"path":894,"words":895,"published":896,"date":156},"\u002Fposts\u002F数据结构\u002F数据结构-绪论",2881,"2025-04-05 11:31:22",{"path":898,"words":899,"published":156,"date":900},"\u002Fposts\u002F杂记\u002F2023-12-26-2023年12月26日",7,"2023-12-26 23:51:33",{"path":902,"words":899,"published":156,"date":903},"\u002Fposts\u002F杂记\u002F2024-01-01-2024团建","2024-01-01 01:51:27",{"path":905,"words":581,"published":156,"date":906},"\u002Fposts\u002F杂记\u002F2024-01-09-随记","2024-01-09 20:50:35",{"path":908,"words":909,"published":156,"date":910},"\u002Fposts\u002F杂记\u002F2024-03-12-花名",5275,"2024-03-12 16:04:23",{"path":912,"words":913,"published":156,"date":914},"\u002Fposts\u002F杂记\u002F2024-04-15-逆模因",1353,"2024-04-15 00:26:16",{"path":916,"words":917,"published":156,"date":918},"\u002Fposts\u002F杂记\u002F2024-05-03-艺作",27,"2024-05-03 00:01:00",{"path":920,"words":921,"published":156,"date":922},"\u002Fposts\u002F杂记\u002F2024-05-21-关于博客",1720,"2024-02-21 23:21:11",{"path":924,"words":925,"published":156,"date":926},"\u002Fposts\u002F杂记\u002F2024-06-20-dnvrc维克多崔专访-2023",2036,"2024-06-20 00:28:39",{"path":928,"words":760,"published":156,"date":929},"\u002Fposts\u002F杂记\u002F2024-06-25-6月25日随记","2024-06-25 12:46:36",{"path":931,"words":932,"published":156,"date":933},"\u002Fposts\u002F杂记\u002F2024-06-25-歌名-想見星星",148,"2024-06-25 22:47:59",{"path":935,"words":936,"published":156,"date":937},"\u002Fposts\u002F杂记\u002F2024-07-11-桂花岗",6,"2024-07-11 00:28:17",{"path":939,"words":940,"published":156,"date":941},"\u002Fposts\u002F杂记\u002F2024-07-15-rolling-bocci",9,"2024-07-15 21:25:56",{"path":943,"words":944,"published":156,"date":945},"\u002Fposts\u002F杂记\u002F2024-07-22-七月九日",34,"2024-07-09 17:54:23",{"path":947,"words":948,"published":156,"date":949},"\u002Fposts\u002F杂记\u002F2024-07-22-六月随记",1052,"2024-07-22 01:03:19",{"path":951,"words":952,"published":156,"date":953},"\u002Fposts\u002F杂记\u002F2024-07-22-石菖蒲",41,"2024-07-10 01:26:05",{"path":955,"words":514,"published":156,"date":956},"\u002Fposts\u002F杂记\u002F2024-07-23-新书","2024-07-23 01:05:49",{"path":958,"words":959,"published":156,"date":960},"\u002Fposts\u002F杂记\u002F2024-07-29-年前",74,"2024-07-29 23:59:07",{"path":962,"words":963,"published":156,"date":964},"\u002Fposts\u002F杂记\u002F2024-07-30-周边",138,"2024-07-30 02:03:23",{"path":966,"words":967,"published":156,"date":968},"\u002Fposts\u002F杂记\u002F2024-07-31-2024-7-31日",185,"2024-07-31 01:21:52",{"path":970,"words":971,"published":156,"date":972},"\u002Fposts\u002F杂记\u002F2024-08-02-八月",1710,"2024-08-30 00:31:02",{"path":974,"words":975,"published":156,"date":976},"\u002Fposts\u002F杂记\u002F2024-09-01-九月",1484,"2024-09-26 16:30:15",{"path":978,"words":979,"published":156,"date":980},"\u002Fposts\u002F杂记\u002F2024-09-08-2024年9月8日",97,"2024-09-08 13:59:36",{"path":982,"words":514,"published":156,"date":983},"\u002Fposts\u002F杂记\u002F2024-09-19-博客站的一周年","2024-09-18 23:36:24",{"path":985,"words":986,"published":156,"date":987},"\u002Fposts\u002F杂记\u002F2024-10-03-十月",1028,"2024-10-31 23:54:27",{"path":989,"words":990,"published":156,"date":991},"\u002Fposts\u002F杂记\u002F2024-11-04-十一月",2134,"2024-11-30 00:04:39",{"path":993,"words":994,"published":156,"date":995},"\u002Fposts\u002F杂记\u002F2024-11-10-gzhu-site-193",80,"2024-11-10 19:19:12",{"path":997,"words":998,"published":156,"date":999},"\u002Fposts\u002F杂记\u002F2024-12-01-十二月",455,"2024-12-30 23:13:47",{"path":1001,"words":1002,"published":156,"date":1003},"\u002Fposts\u002F杂记\u002F2024-12-21-年末",23,"2024-12-21 23:21:49",{"path":1005,"words":522,"published":156,"date":1006},"\u002Fposts\u002F杂记\u002F2025-01-30-一月","2025-01-30 23:44:41",{"path":1008,"words":1009,"published":156,"date":1010},"\u002Fposts\u002F杂记\u002F2025-03-01-二月",224,"2025-02-28 00:11:08",{"path":1012,"words":1013,"published":156,"date":1014},"\u002Fposts\u002F杂记\u002F2025-03-01-博客杂谈",344,"2025-03-01 16:03:18",{"path":1016,"words":1017,"published":156,"date":1018},"\u002Fposts\u002F杂记\u002F2025-03-05-三月",2090,"2025-03-05 19:17:28",{"path":1020,"words":1021,"published":156,"date":1022},"\u002Fposts\u002F杂记\u002F2025-07-10-七月",361,"2025-07-10 22:04:32",{"path":1024,"words":1025,"published":156,"date":1026},"\u002Fposts\u002F杂记\u002F2025-09-21-九月",781,"2025-09-21 20:53:37",{"path":1028,"words":1029,"published":156,"date":1030},"\u002Fposts\u002F杂记\u002F2025-10-09-十月",591,"2025-10-09 00:35:07",{"path":1032,"words":1033,"published":156,"date":1034},"\u002Fposts\u002F杂记\u002Fcontrol",13,"2023-09-25 13:03:59",{"path":1036,"words":1037,"published":156,"date":1038},"\u002Fposts\u002F杂记\u002Fquadim四叉树图像处理",1094,"2023-10-21 11:27:06",{"path":1040,"words":1041,"published":156,"date":1042},"\u002Fposts\u002F杂记\u002Fsuccess",24,"2023-09-22 15:59:07",{"path":1044,"words":1045,"published":156,"date":1046},"\u002Fposts\u002F杂记\u002F三光年",295,"2023-11-18 04:18:40",{"path":1048,"words":1049,"published":156,"date":1050},"\u002Fposts\u002F杂记\u002F加密测试",961,"2023-11-21 14:24:01",{"path":1052,"words":1053,"published":156,"date":1054},"\u002Fposts\u002F杂记\u002F啊啊啊啊啊啊啊",54,"2023-09-20 17:29:33",{"path":1056,"words":1057,"published":156,"date":1058},"\u002Fposts\u002F杂记\u002F杂谈",124,"2023-09-25 23:11:32",{"path":1060,"words":1061,"published":156,"date":1062},"\u002Fposts\u002F杂记\u002F艹",25,"2023-10-21 17:40:43",{"path":1064,"words":1065,"published":1066,"date":156},"\u002Fposts\u002F神经网络\u002F神经网络与深度学习卷积神经网络",3168,"2026-06-07 11:23:08",{"path":1068,"words":1069,"published":1070,"date":156},"\u002Fposts\u002F神经网络\u002F神经网络与深度学习循环神经网络",2348,"2026-06-07 11:33:08",{"path":1072,"words":1073,"published":1074,"date":156},"\u002Fposts\u002F神经网络\u002F神经网络与深度学习改进学习方法",3360,"2026-06-07 10:13:08",{"path":1076,"words":1077,"published":1078,"date":156},"\u002Fposts\u002F神经网络\u002F神经网络与深度学习深度生成模型",2727,"2026-06-07 11:43:08",{"path":1080,"words":1081,"published":1082,"date":156},"\u002Fposts\u002F神经网络\u002F神经网络与深度学习神经网络基础",3205,"2026-06-07 10:03:08",{"id":1084,"title":1085,"abbrlink":1086,"body":1087,"category":5,"cover":1872,"date":156,"description":1873,"extension":1874,"mathjax":1875,"meta":1876,"navigation":1875,"path":627,"published":629,"readingMinutes":574,"seo":1879,"stem":1880,"sticky":156,"swiper_index":156,"tags":1881,"updated":156,"words":628,"__hash__":1882},"posts\u002Fposts\u002F2025\u002F2025-11-22-关于前端包管理器：npm,pnpm,yarn和bun.md","2025-11-22-简谈前端包管理器npm、pnpm、Yarn 与 Bun","15722",{"type":1088,"value":1089,"toc":1853},"minimark",[1090,1099,1102,1106,1123,1143,1159,1169,1172,1176,1179,1184,1227,1240,1260,1264,1275,1293,1302,1327,1330,1338,1344,1358,1362,1379,1388,1397,1411,1415,1427,1440,1443,1459,1462,1468,1491,1495,1498,1506,1516,1527,1536,1544,1550,1558,1566,1569,1586,1590,1593,1610,1626,1650,1668,1710,1730,1747,1778,1781,1784,1790,1799,1812,1821,1824,1827,1830,1839,1846],[1091,1092,1093,1094,1098],"p",{},"前端关于 npm、pnpm、Yarn 与 Bun 的选型，主要得看它们在生产环境的差异。好的包管理器不该让幽灵依赖爆掉，不该因为 ",[1095,1096,1097],"code",{},"workspace:"," 协议悄悄往 registry 发一个坏掉的包，lockfile 也不该团队成员谁都看不懂。",[1091,1100,1101],{},"下面先看机制，再看安装速度、lockfile、工作区、生产可靠性。",[1103,1104,1105],"h2",{"id":1105},"四款包管理器从哪来",[1091,1107,1108,1112,1113,1116,1117,1119,1120,1122],{},[1109,1110,1111],"strong",{},"npm"," 从一开始就捆绑在 Node.js 里。它算不上刻意设计，只是 Node 顺带带了个包管理器，registry 围着它长了起来。真正的大架构决策出现在 2015 年的 npm v3：把 ",[1095,1114,1115],{},"node_modules"," 拍平成单一的提升（hoisted）目录，以应付 Windows 的路径长度限制，以及旧式的深层嵌套安装树。npm v3 之前，",[1095,1118,1115],{}," 是嵌套的——每个依赖有自己的 ",[1095,1121,1115],{}," 装着自己声明过的依赖；这种结构可预测、干净，但树太深，Windows 路径会爆，同一份包也会被复制多份。扁平化解决了这两件事，但代价是一连串新的麻烦。",[1091,1124,1125,1128,1129,1131,1132,1134,1135,1138,1139,1142],{},[1109,1126,1127],{},"Yarn"," 2016 年 10 月发布，由 Facebook 与 Tilde、Exponent、Google 共同打造。当时的卖点很简单：确定性安装、并行网络 IO、离线缓存。它管用。但 pnpm 作者 Rico Sta. Cruz 当时正在给 pnpm 投入精力，Yarn 的发布让他失望——不是因为 Yarn 不好，而是因为 Yarn 用了和 npm v3 一样的扁平 ",[1095,1130,1115],{}," 结构，等于在解决速度与确定性，却没碰结构本身。随后 2020 年 Yarn 2 登场，内部代号 \"Berry\"，带来 Plug'n'Play——一次激进得多的押注：干脆干掉 ",[1095,1133,1115],{},"。Yarn 3、Yarn 4 在此基础上迭代。2017 年那个 Yarn（",[1095,1136,1137],{},"yarn.lock","、快速安装、npm 的即插即用替代品）现在叫 ",[1109,1140,1141],{},"Yarn Classic","，已进入维护模式。今天正经人在用的 Yarn 是 Berry，它不是同一个工具。",[1091,1144,1145,1148,1149,1151,1152,1155,1156,1158],{},[1109,1146,1147],{},"pnpm"," 由 Rico Sta. Cruz 于 2017 年 6 月首发，围绕一个执拗的想法：磁盘上一份内容寻址存储，硬链接进每个项目的 ",[1095,1150,1115],{},"。同一个包、同一个版本，全机只有一份，不管有多少项目在用，强制严格的依赖解析：代码只能 ",[1095,1153,1154],{},"import"," 真正声明过的包。Rico 当初的判断是：扁平化的 ",[1095,1157,1115],{}," 有一连串问题——模块可以访问未声明过的包、扁平化算法复杂、有些包仍要在项目内被复制多份——而 Yarn 不打算解决这些问题，所以他继续做 pnpm。",[1091,1160,1161,1164,1165,1168],{},[1109,1162,1163],{},"Bun"," 2023 年 9 月到 1.0。它是运行时、打包器、测试运行器、包管理器四合一，全部从零写起：最初用 Zig，2026 年正用 Rust 重写，作者是 Jarred Sumner。它的包管理器基本等于把 ",[1095,1166,1167],{},"npm install"," 重写一遍，只为极限速度优化。还有一件事：2025 年 12 月，Bun 加入 Anthropic。它不再是小独立项目，背后是一家大量内部使用它的 AI 大厂，虽然我好奇 Anthropic 为什么会看上它。",[1091,1170,1171],{},"历史遗留左右了各自的设计：npm 是顺势长成的默认项，Yarn 不停试图重新定义\"安装图该长什么样\"，pnpm 认准一个强观点并坚持到底，Bun 则激进重写，速度为王。",[1103,1173,1175],{"id":1174},"安装算法决定一切的底层","安装算法：决定一切的底层",[1091,1177,1178],{},"安装算法决定了一款工具的速度、磁盘占用、幽灵依赖的表现，以及 monorepo 会不会在 18 个月后变成噩梦。",[1180,1181,1183],"h3",{"id":1182},"npm拍平且宽容","npm：拍平且宽容",[1091,1185,1186,1187,1189,1190,1193,1194,1196,1197,1200,1201,1204,1205,1208,1209,1211,1212,1215,1216,1218,1219,1204,1221,1218,1223,1226],{},"跑 ",[1095,1188,1167],{}," 时，npm 读 ",[1095,1191,1192],{},"package.json","、构建依赖树，然后把能提的都提到 ",[1095,1195,1115],{}," 顶层。如果 ",[1095,1198,1199],{},"package-a"," 依赖 ",[1095,1202,1203],{},"lodash@4","、",[1095,1206,1207],{},"package-b"," 也依赖 ",[1095,1210,1203],{},"，那 ",[1095,1213,1214],{},"node_modules\u002Flodash"," 就只有一份，两个包都通过 Node 标准的模块解析上溯找到它。版本冲突时，比如 ",[1095,1217,1199],{}," 要 ",[1095,1220,1203],{},[1095,1222,1207],{},[1095,1224,1225],{},"lodash@3","——npm 就会挑一个提升、把输家嵌套到需要它的包下面。",[1091,1228,1229,1230,1233,1234,1236,1237],{},"这个提升导致了幽灵依赖，比如代码可以不声明 lodash 就写 ",[1095,1231,1232],{},"require('lodash')","，它能跑，因为 lodash 恰好在顶层。直到某天 ",[1095,1235,1199],{}," 在小版本里去掉了 lodash 依赖，lodash 不再被提升，import 在生产环境返回 ",[1095,1238,1239],{},"undefined",[1091,1241,1242,1243,1245,1246,1249,1250,1253,1254,1256,1257,1259],{},"npm 近几年的速度已经追上来不少，兼容性也是最广的：每个 CI runner、每个 Docker 镜像、每套接手的老代码，都默认 ",[1095,1244,1167],{}," 能跑。它的 lockfile 是 ",[1095,1247,1248],{},"package-lock.json","，npm v7 起为 ",[1095,1251,1252],{},"lockfileVersion: 3","，携带的信息足以复现整棵树，而不必重读 ",[1095,1255,1115],{}," 里每个 ",[1095,1258,1192],{},"。",[1180,1261,1263],{"id":1262},"pnpm硬链接加符号链接","pnpm：硬链接加符号链接",[1091,1265,1266,1267,1270,1271,1274],{},"pnpm 做的是真正不同的事。在机器上装过的每个包版本，都只存在一份，放在内容寻址存储里，通常在 ",[1095,1268,1269],{},"~\u002F.local\u002Fshare\u002Fpnpm\u002Fstore","。存储里的每个文件用内容哈希标识，所以两个恰好带同一份 ",[1095,1272,1273],{},"README.md"," 的不同包版本，用的是完全一样的磁盘字节。",[1091,1276,1277,1278,1281,1282,1284,1285,1288,1289,1292],{},"往项目里装包时，pnpm 不复制文件。它从存储往项目内的 ",[1095,1279,1280],{},"node_modules\u002F.pnpm\u002F"," 虚拟存储建硬链接，再用符号链接从这个虚拟存储搭出可见的 ",[1095,1283,1115],{}," 布局。结果是两层间接：",[1095,1286,1287],{},"require()"," 到的文件是链向 ",[1095,1290,1291],{},".pnpm\u002F"," 的符号链接，而它又是链向全局存储的硬链接。",[1091,1294,1295,1296,1298,1299,1301],{},"pnpm 不扁平化依赖树这件事，得从 npm v3 之前的结构说起。npm v2 时代，",[1095,1297,1115],{}," 是嵌套的——foo 依赖 bar，foo 的目录里就有一个自己的 ",[1095,1300,1115],{}," 装 bar。这种结构干净、可预测，但树太深会爆 Windows 路径，同一份包会被复制多份。npm v3 用扁平化解决了这两件事，但引出了幽灵依赖与算法复杂度。pnpm 选择了第三条路：保留\"每个包有自己的依赖入口\"这个干净属性，但用符号链接代替真实的子目录，避免树过深。",[1091,1303,1304,1305,1307,1308,1311,1312,1315,1316,1318,1319,1322,1323,1326],{},"pnpm 的 ",[1095,1306,1115],{}," 根目录下每个包都是一个符号链接，指向 ",[1095,1309,1310],{},".pnpm\u002F\u003C包名>@\u003C版本>\u002Fnode_modules\u002F\u003C包名>","；在那个内部目录里，包的依赖又是符号链接，指向同级的 ",[1095,1313,1314],{},".pnpm\u002F\u003C依赖名>@\u003C版本>\u002Fnode_modules\u002F\u003C依赖名>","。Node.js 在 ",[1095,1317,1287],{}," 时会忽略符号链接、走真实路径（realpath），所以 ",[1095,1320,1321],{},"require('foo')"," 最终执行的是虚拟存储里的 foo，而 foo 内部 ",[1095,1324,1325],{},"require('bar')"," 时，Node 沿目录树上溯，能找到同级符号链接过去的 bar。结构看上去绕，但每个包只能看到自己声明过的依赖，幽灵依赖被默认阻断。Rico 当初正是靠这一点把 pnpm 的算法做得足够简单——简单到一个人能跟上 Yarn 几十个贡献者的节奏。",[1091,1328,1329],{},"这套机制带来两笔收益：",[1091,1331,1332,1335,1336,1259],{},[1109,1333,1334],{},"磁盘节省惊人。"," 一台机器跑十个 Next.js 项目，在 pnpm 下可能只占几 GB 存储；同样的依赖用 npm 会是 30 多 GB 重复的 ",[1095,1337,1115],{},[1091,1339,1340,1343],{},[1109,1341,1342],{},"幽灵依赖消失了。"," 因为通过符号链接图，每个包只能看到自己声明过的依赖，代码无法意外 import 没声明过的东西。",[1091,1345,1346,1347,1349,1350,1353,1354,1357],{},"代价就是这个符号链接层本身。有些老构建工具假定 ",[1095,1348,1115],{}," 里都是真实目录里的真实文件，偶尔会撞上一个在符号链接下行为异常的。不过可以 ",[1095,1351,1352],{},".npmrc"," 里设 ",[1095,1355,1356],{},"node-linker=hoisted","，让 pnpm 产出拍平的 npm 式布局，用严格性换回兼容性。",[1180,1359,1361],{"id":1360},"yarn-berry问题出在-node_modules","Yarn Berry：问题出在 node_modules",[1091,1363,1364,1365,1367,1368,1370,1371,1374,1375,1378],{},"Yarn 2+ 的立场最激进：",[1095,1366,1115],{}," 是 Node 从未妥善解决的一个问题留下的 15 年 workaround，认为不该继续维护它。Plug'n'Play（PnP）彻底移除 ",[1095,1369,1115],{},"，改成生成一个单独的 ",[1095,1372,1373],{},".pnp.cjs"," 文件（早先是 ",[1095,1376,1377],{},".pnp.js","），它是一张大查找表，把（包名，版本）映射到磁盘位置，并打补丁让 Node 的模块解析器直接查这张表。",[1091,1380,1381,1382,1384,1385,1387],{},"这机制不再沿文件树上溯找 ",[1095,1383,1115],{},"，不再为每次 ",[1095,1386,1287],{}," 做 IO，只剩一次哈希查找。包以 ZIP 留在全局 Yarn 缓存里，Node 通过虚拟文件系统读取。磁盘与启动时间的收益是实的。",[1091,1389,1390,1391,1393,1394,1396],{},"麻烦在于整个生态都假定 ",[1095,1392,1115],{}," 存在。那些直接扫 ",[1095,1395,1115],{}," 的工具——某些打包器、某些 linter、某些老 Webpack 插件——在 PnP 下会坏。Yarn 提供了一个编辑器 SDK，教 VS Code 在 ZIP 里找类型。多数现代工具可用，但会在 npm、pnpm 用户永远不会遇到的边角上栽跟头。",[1091,1398,1399,1400,1402,1403,1406,1407,1410],{},"Yarn Berry 也仍支持常规的 ",[1095,1401,1115],{}," 安装，在 ",[1095,1404,1405],{},".yarnrc.yml"," 里写 ",[1095,1408,1409],{},"nodeLinker: node-modules","。拿到 Yarn 的 lockfile、工作区和插件，而不必押注 PnP。",[1180,1412,1414],{"id":1413},"bun和-npm-同样的想法只是由不耐烦的人重写","Bun：和 npm 同样的想法，只是由不耐烦的人重写",[1091,1416,1417,1420,1421,1423,1424,1426],{},[1095,1418,1419],{},"bun install"," 读 ",[1095,1422,1192],{},"、建树、下载缺失的部分、写出类似 npm 的拍平 ",[1095,1425,1115],{},"。没有花哨的间接层、没有符号链接存储、没有虚拟文件系统。快的原因在于整套东西是个用 Zig 写的编译产物（现迁往 Rust 了），吃满每个 CPU 核，并完全省掉 Node 的启动开销。",[1091,1428,1429,1430,1433,1434,1436,1437,1439],{},"Bun 的安装也有自己的全局缓存（",[1095,1431,1432],{},"~\u002F.bun\u002Finstall\u002Fcache","），会从缓存硬链接或复制，而不是重新下载。但可见的 ",[1095,1435,1115],{}," 就是构建工具期望的那种布局，所以兼容性问题很少。若只为了 ",[1095,1438,1419],{}," 而选 Bun，得到的只是一个形状相同、更快的 npm install；",[1103,1441,1442],{"id":1442},"安装速度",[1444,1445,1446,1450,1453,1456],"ul",{},[1447,1448,1449],"li",{},"npm：早期版本安装慢，npm v7+ 起经过优化显著提升，但仍落后于后起之秀。",[1447,1451,1452],{},"Yarn Classic (v1)：靠并行下载和高效缓存，安装速度通常快于同期 npm。",[1447,1454,1455],{},"pnpm：pnpm 的核心优势之一。利用全局内容寻址存储和硬链接，让每个包在全局只存一份实体文件。多个项目依赖同一个包时，只需向全局存储建硬链接，大幅减少文件复制和网络下载，安装极快。",[1447,1457,1458],{},"Bun：速度最快。Bun 从底层重写，靠 Zig 的执行效率，包安装速度通常是 npm 的几十倍，也明显快于 pnpm 和 Yarn。这种原生性能是其最大的卖点。",[1103,1460,1461],{"id":1461},"磁盘空间效率",[1091,1463,1464,1465,1467],{},"随着项目增多，",[1095,1466,1115],{}," 可能占据大量磁盘空间。",[1444,1469,1470,1476,1479,1488],{},[1447,1471,1472,1473,1475],{},"npm\u002FYarn Classic：都用扁平化 ",[1095,1474,1115],{}," 结构，相同包的不同版本、甚至相同版本在不同项目里都可能被多次复制，造成磁盘冗余。",[1447,1477,1478],{},"pnpm：磁盘效率冠军。所有包的实体文件只在全局存储一份，项目里通过硬链接或符号链接引用。即使上百个项目依赖同一个包，也只占一份磁盘空间。",[1447,1480,1481,1482,1484,1485,1487],{},"Yarn Berry (v2+)：用 Plug'n'Play (PnP)，彻底废弃 ",[1095,1483,1115],{},"。通过一个 ",[1095,1486,1373],{}," 文件直接解析模块路径，避免文件复制，磁盘效率极高。",[1447,1489,1490],{},"Bun：类似 pnpm，用符号链接把依赖项链到全局缓存，磁盘效率也很高。",[1103,1492,1494],{"id":1493},"lockfile","Lockfile",[1091,1496,1497],{},"每款包管理器都有 lockfile。目的一样：把每个传递依赖钉到具体版本与完整性哈希，使安装可复现。但它们在形态、可读性、处理合并冲突的方式上不同。表面无聊，实操痛苦。",[1091,1499,1500],{},[1109,1501,1502,1503,1505],{},"JSON：",[1095,1504,1248],{},"（npm，v3）",[1507,1508,1514],"pre",{"className":1509,"code":1511,"language":1512,"meta":1513},[1510],"language-json","{\n  \"name\": \"my-app\",\n  \"lockfileVersion\": 3,\n  \"requires\": true,\n  \"packages\": {\n    \"\": { \"name\": \"my-app\", \"dependencies\": { \"lodash\": \"^4.17.21\" } },\n    \"node_modules\u002Flodash\": {\n      \"version\": \"4.17.21\",\n      \"resolved\": \"https:\u002F\u002Fregistry.npmjs.org\u002Flodash\u002F-\u002Flodash-4.17.21.tgz\",\n      \"integrity\": \"sha512-...\"\n    }\n  }\n}\n","json","",[1095,1515,1511],{"__ignoreMap":1513},[1091,1517,1518,1519,1521,1522,1524,1525,1259],{},"diff 噪声大但可读。合并冲突通常是机械性的：接受对方版本再重跑 ",[1095,1520,1167],{},"。自 npm v7 起一直是 ",[1095,1523,1252],{},"，带的元数据足以重建整棵树，无需查 registry 或 ",[1095,1526,1115],{},[1091,1528,1529],{},[1109,1530,1531,1532,1535],{},"YAML：",[1095,1533,1534],{},"pnpm-lock.yaml","（pnpm）",[1507,1537,1542],{"className":1538,"code":1540,"language":1541,"meta":1513},[1539],"language-yaml","lockfileVersion: '9.0'\n\nimporters:\n  .:\n    dependencies:\n      lodash:\n        specifier: ^4.17.21\n        version: 4.17.21\n\npackages:\n  lodash@4.17.21:\n    resolution: { integrity: sha512-... }\n","yaml",[1095,1543,1540],{"__ignoreMap":1513},[1091,1545,1546,1547,1549],{},"YAML，比 npm 简洁得多，且 pnpm 花了真功夫让不同版本间的 diff 尽量小。一个含十几份工作区的 monorepo 里，它在代码评审中明显比 ",[1095,1548,1248],{}," 好读。合并冲突依然烦，但冲突面更小。",[1091,1551,1552],{},[1109,1553,1554,1555,1557],{},"文本：",[1095,1556,1137],{},"（Yarn，classic 格式）",[1507,1559,1564],{"className":1560,"code":1562,"language":1563},[1561],"language-text","\"lodash@^4.17.21\":\n  version \"4.17.21\"\n  resolved \"https:\u002F\u002Fregistry.yarnpkg.com\u002Flodash\u002F-\u002Flodash-4.17.21.tgz\"\n  integrity sha512-...\n","text",[1095,1565,1562],{"__ignoreMap":1513},[1091,1567,1568],{},"Yarn 自创了一种格式，YAML 味但并非 YAML。这是刻意选择：为可读 diff 优化。Berry 用基本相同的形式，并加了 PnP 的额外元数据。这个格式老化得比预期好。",[1091,1570,1571,1573,1574,1577,1578,1581,1582,1585],{},[1109,1572,1163],{}," 起初发的是二进制 lockfile ",[1095,1575,1576],{},"bun.lockb","。好处显而易见：文件更小、读取更快。痛处同样明显，开发者显然没法在 PR 里审一个二进制 lockfile，且合并冲突是灾难级的。Bun 后来出了基于文本的 ",[1095,1579,1580],{},"bun.lock","（JSONC）格式，并在 Bun v1.2 设为默认。已有项目可用 ",[1095,1583,1584],{},"bun install --save-text-lockfile"," 迁移。",[1103,1587,1589],{"id":1588},"工作区与-monorepo","工作区与 monorepo",[1091,1591,1592],{},"monorepo 是包管理器真正拉开差距的地方。下面从七个维度看四款工具的表现。",[1091,1594,1595,1598,1599,1601,1602,1605,1606,1609],{},[1109,1596,1597],{},"workspace 的声明形态。"," npm、Yarn、Bun 都用根 ",[1095,1600,1192],{}," 的 ",[1095,1603,1604],{},"workspaces"," 字段声明本地包；pnpm 单独用 ",[1095,1607,1608],{},"pnpm-workspace.yaml","。pnpm 的分离让工作区配置可以独立版本化，9.0 之后还能把依赖覆盖、catalog 放进同一个 yaml，在大型 monorepo 里更清晰。",[1091,1611,1612,1617,1618,1621,1622,1625],{},[1109,1613,1614,1616],{},[1095,1615,1097],{}," 协议。"," 这是 monorepo 里最关键的一条——它决定了\"本地包引用能不能被误解析成 registry 上的某个版本\"。npm 与 Yarn Classic 都不支持，只能写具体版本号，发布后引用关系不变。Yarn Berry、pnpm、Bun（1.x 起）都支持。pnpm 与 Yarn Berry 在发布时会把 ",[1095,1619,1620],{},"workspace:*"," 改写为实际版本号，且",[1109,1623,1624],{},"拒绝","解析为任何非本地副本。Bun 自 1.x 起追上了这一点，但仍是四者中最年轻的实现。",[1091,1627,1628,1629,1406,1632,1635,1636,1639,1640,1642,1643,1646,1647,1259],{},"用 npm 时，如果在 ",[1095,1630,1631],{},"apps\u002Fweb",[1095,1633,1634],{},"\"@my-org\u002Fui\": \"1.2.0\"","，npm 会先看本地工作区有没有 ",[1095,1637,1638],{},"@my-org\u002Fui@1.2.0","，有就用本地、没有就从 registry 拉。这个\"有就用本地、没就上网\"的回退路径是 npm monorepo 出事故的常见源头——一旦本地版本号对不上，npm 会悄悄去 registry 拉一个同名包，发布出去的 ",[1095,1641,1631],{}," 可能引用了一个",[1109,1644,1645],{},"别人发布的"," ",[1095,1648,1649],{},"@my-org\u002Fui",[1091,1651,1652,1655,1656,1658,1659,1661,1662,1664,1665,1667],{},[1109,1653,1654],{},"跨工作区的幽灵依赖防护。"," 幽灵依赖在 monorepo 里会变成\"工作区 A 能跑，是因为它意外 import 了工作区 B 的 ",[1095,1657,1115],{}," 里的包\"。npm、Yarn Classic、Bun 把依赖提升到根，所有工作区共享同一棵提升树，跨工作区幽灵依赖横行。pnpm 给每个工作区自己严格隔离的 ",[1095,1660,1115],{},"，跨工作区幽灵依赖被默认阻断。Yarn Berry PnP 通过 ",[1095,1663,1373],{}," 查表阻断得更彻底；但用 ",[1095,1666,1409],{}," 模式时退回到 npm 行为。在 10+ 工作区的 monorepo 里，这条差异通常是\"信得过\"与\"惊喜连连\"的分水岭。",[1091,1669,1670,1673,1674,1677,1678,1681,1682,1685,1686,1689,1690,1693,1694,1697,1698,1701,1702,1705,1706,1709],{},[1109,1671,1672],{},"跨工作区脚本执行。"," monorepo 经常需要\"在所有 ",[1095,1675,1676],{},"packages\u002F*"," 里跑 build\"、\"web 依赖 ui，先 build ui 再 build web\"这种拓扑顺序执行。npm 的 ",[1095,1679,1680],{},"npm run build --workspaces"," 能跑全部，但",[1109,1683,1684],{},"没有拓扑排序","，并行也没内置支持。Yarn Classic 同样没有。Yarn Berry 通过插件（如 ",[1095,1687,1688],{},"@yarnpkg\u002Fplugin-workspace-tools","）支持拓扑排序与过滤，插件生态是它在 monorepo 场景的真正强项。pnpm 的 ",[1095,1691,1692],{},"pnpm -r run build"," 自带拓扑排序，",[1095,1695,1696],{},"--filter"," 语法支持按依赖关系筛选——",[1095,1699,1700],{},"--filter @my-org\u002Fui..."," 表示\"ui 及其依赖的所有包\"，",[1095,1703,1704],{},"--filter ...@my-org\u002Fui"," 表示\"ui 以及所有依赖 ui 的包\"，是 monorepo 改一个包只测影响范围的关键能力，npm 与 Yarn Classic 都没有原生等价物。Bun 的 ",[1095,1707,1708],{},"bun run --filter '*' build"," 已支持，拓扑排序与并行执行在 1.x 里逐步完善，但仍不如 pnpm 成熟。",[1091,1711,1712,1715,1716,1718,1719,1721,1722,1725,1726,1729],{},[1109,1713,1714],{},"发布行为。"," monorepo 发包时，本地工作区之间的版本引用要被改写成实际版本号，否则发布出去的包会带着 ",[1095,1717,1097],{}," 字样，registry 不认。npm 与 Yarn Classic 不支持 ",[1095,1720,1097],{}," 协议，没有改写问题，但代价是发布前要手动同步版本号，容易漏。Yarn Berry、pnpm、Bun 在发布时自动改写，且 ",[1095,1723,1724],{},"pnpm -r publish"," 与 ",[1095,1727,1728],{},"yarn workspaces foreach"," 还支持批量发布所有改动过的工作区。Bun 的发布流程仍比 pnpm 简陋，多数团队仍需配合 changesets 使用。",[1091,1731,1732,1735,1736,1739,1740,1204,1743,1746],{},[1109,1733,1734],{},"插件与扩展能力。"," monorepo 一旦上规模，常需要约束工作区之间的依赖方向、统一版本策略、与 TypeScript project references 同步。npm 插件生态几乎为零，全靠外部工具（Nx、Turbo、Lerna、changesets）。Yarn Classic 插件少，且已进入维护模式。",[1109,1737,1738],{},"Yarn Berry 插件生态最强","：官方与社区插件覆盖依赖约束、版本策略、工作区工具、TS project references 同步等，还能写自定义插件拦截安装与发布生命周期。pnpm 核心能力内置（filter、拓扑排序、catalog、",[1095,1741,1742],{},"overrides",[1095,1744,1745],{},"patchedDependencies","），插件生态不如 Berry 但日常够用。Bun 插件系统仍在发展中，monorepo 能力主要靠内置。",[1091,1748,1749,1752,1753,1755,1756,1759,1760,1762,1763,1766,1767,1770,1771,1773,1774,1777],{},[1109,1750,1751],{},"依赖版本统一。"," monorepo 里多个工作区依赖同一个包的不同版本、希望强制统一时：npm 靠 ",[1095,1754,1742],{},"（8.3+）、Yarn 靠 ",[1095,1757,1758],{},"resolutions","、Bun 兼容 npm 的 ",[1095,1761,1742],{},"。pnpm 在 ",[1095,1764,1765],{},"pnpm.overrides"," 之上，9.0 起加了 ",[1095,1768,1769],{},"catalogs","——在 ",[1095,1772,1608],{}," 里定义一份版本目录，各工作区用 ",[1095,1775,1776],{},"catalog:default"," 引用，改一处全 monorepo 生效。这是 pnpm 在版本治理上独有的一项。",[1091,1779,1780],{},"一句话总结这一节：npm 的 workspace 是\"能跑\"级别，pnpm 是\"默认就对\"级别，Yarn Berry 是\"配好之后最强\"级别，Bun 是\"快速追上中\"级别。",[1103,1782,1783],{"id":1783},"生产可靠性",[1091,1785,1786,1787,1789],{},"包管理器的\"生产可靠性\"大致是：同一份 ",[1095,1788,1192],{}," 与 lockfile 在 CI 里和在笔记本上是否产出同一份安装；以及防呆机制。下面几件事按咬人的频率排序。",[1091,1791,1792,1795,1796,1798],{},[1109,1793,1794],{},"严格模式的依赖解析。"," 幽灵依赖 import 了从未声明过的东西——是 JavaScript 里\"我机器上能跑、CI\u002F预发\u002F生产挂了\"类事故最常见的成因。npm 与 Bun 的拍平提升允许它；pnpm 默认阻止它；Yarn PnP 默认阻止它；Yarn 用 ",[1095,1797,1409],{}," 时允许它。严格依赖解析是下次起新项目就该顺手拿下的免费升级。",[1091,1800,1801,1804,1805,1808,1809,1811],{},[1109,1802,1803],{},"确定性安装。"," 给定同一份 lockfile，四款工具的安装都是确定性的。问题是没 lockfile 时会发生什么。npm 与 Yarn Classic 历史上允许未锁定的安装漂移；现代 Yarn 在 lockfile 不同步且未给显式标志时会拒绝安装；pnpm 有 ",[1095,1806,1807],{},"--frozen-lockfile","；Bun 有 ",[1095,1810,1807],{},"。CI 里会给用的工具加上 frozen-lockfile 标志，否则错位的 lockfile 会在 CI 里默默重写自己，导致未经评审的版本发布。",[1091,1813,1814,1817,1818,1820],{},[1109,1815,1816],{},"Postinstall 脚本。"," 原生模块（node-gyp 构建、sharp 的预编译二进制、Prisma 的引擎下载）都通过 postinstall 脚本运行。这类东西在四款工具上通常都没问题，但 pnpm 的严格布局与 Yarn PnP 各有边角——某个 postinstall 脚本假定了实际并不成立的 ",[1095,1819,1115],{}," 形状。",[1103,1822,1823],{"id":1823},"选型",[1091,1825,1826],{},"多数场景下我认为还是pnpm占优，目前pnpm在字节内部多个团队也都有实践，主要是eden的monorepo底层依赖管理工具支持yarn workspace和pnpm的切换，随着eden的支持，可以见到pnpm在字节内部的实践范围会越来越大。毕竟pnpm是真正的解决node_modules的依赖困境，主要通过软链接和硬链接的结合使用，最终达到节省磁盘空间，安装速度快，严格高效等优点。",[1103,1828,1829],{"id":1829},"参考",[1091,1831,1832,1833],{},"NPM 中的 phatom 与 doppelgangers 问题：",[1834,1835,1836],"a",{"href":1836,"rel":1837},"https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F353208988",[1838],"nofollow",[1091,1840,1841,1842],{},"node_modules困境：",[1834,1843,1844],{"href":1844,"rel":1845},"https:\u002F\u002Fzhuanlan.zhihu.com\u002Fp\u002F137535779",[1838],[1091,1847,1848,1849],{},"扁平的node_modules并不是唯一选项：",[1834,1850,1851],{"href":1851,"rel":1852},"https:\u002F\u002Fpnpm.io\u002Fzh\u002Fblog\u002F2020\u002F10\u002F17\u002Fnode-modules-configuration-options-with-pnpm",[1838],{"title":1513,"searchDepth":1854,"depth":1854,"links":1855},4,[1856,1858,1865,1866,1867,1868,1869,1870,1871],{"id":1105,"depth":1857,"text":1105},2,{"id":1174,"depth":1857,"text":1175,"children":1859},[1860,1862,1863,1864],{"id":1182,"depth":1861,"text":1183},3,{"id":1262,"depth":1861,"text":1263},{"id":1360,"depth":1861,"text":1361},{"id":1413,"depth":1861,"text":1414},{"id":1442,"depth":1857,"text":1442},{"id":1461,"depth":1857,"text":1461},{"id":1493,"depth":1857,"text":1494},{"id":1588,"depth":1857,"text":1589},{"id":1783,"depth":1857,"text":1783},{"id":1823,"depth":1857,"text":1823},{"id":1829,"depth":1857,"text":1829},"https:\u002F\u002Fpica.zhimg.com\u002F70\u002Fv2-a6ca782e51cff8b16da130b1c43fe744_1440w.avis?source=172ae18b&biz_tag=Post","前端关于 npm、pnpm、Yarn 与 Bun 的选型，主要得看它们在生产环境的差异。好的包管理器不该让幽灵依赖爆掉，不该因为 workspace: 协议悄悄往 registry 发一个坏掉的包，lockfile 也不该团队成员谁都看不懂。","md",true,{"uuid":1877,"slots":1878},"1fa522e0-c7a6-11f0-beab-eb8961f00b4e",{},{"title":1085,"description":1873},"posts\u002F2025\u002F2025-11-22-关于前端包管理器：npm,pnpm,yarn和bun",[135],"GP4MRHM5WbIGTV32GJeBKVZGAJ1zRqEZgh7c_oXhyF8",1790443287752]