跨包代码如何调试?带你了解npm link和monorepo

随着前端项目越来越大,我们往往会将一些逻辑、组件或资源从项目中抽离出来,形成单独的npm包。这样,这些内容就可以非常容易地在其他项目中进行复用。然而,当我们需要联调的时候,问题就变得复杂起来,我们需要让依赖包能够响应被依赖的包的变更。这时候,基于文件链接的npm link就极大地方便了我们调试。

文件链接

文件链接是类Unix操作系统中的概念,又分为软链和硬链。软链又被称为符号链接,其是一个单独的特定类型的文件,存储的内容只是一个路径的字符串,如./tsconfig.json/var/tmp这种,和Windows中的快捷方式非常类似。而硬链则是在文件目录中不同的索引节点指向相同的文件,也就是说,硬链和被链接的文件是完全等价的。

下面执行ls -il的结果可以很好地展示软链和硬链的原理上的区别:

$ ls -il
total 0
8607720711 -rw-r--r--  2 root   staff   0  2 23 23:06 a.txt
8607720711 -rw-r--r--  2 root   staff   0  2 23 23:06 b.txt
8607720736 lrwxr-xr-x  2 root   staff   5  2 23 23:07 c.txt -> a.txt

在这个文件夹中,a.txt是原始文件,b.txt是a.txt的一个硬链,可以看到在ls的结果中两个文件除了文件名,其他信息,包括inode号(文件系统中的唯一序号,上图输出中的第一列)是完全一致的。而c.txt则是a.txt的一个硬链,其文件id与a.txt不同。更进一步,我们可以看到a.txt和b.txt的引用计数(输出中的第3列)是2,而c.txt的引用计数是1,这是因为硬链会增加inode上的引用计数,当引用计数归零时,就说明一个文件在文件系统中已经没有任何索引了,操作系统可以删除该文件的物理存储,而软链和原始文件则是不同文件,因此引用计数就重新从1开始计数了。

由于软链和硬链上面原理的不同,带来了它们使用上的区别:

  • 软链可以链接到文件与文件夹,甚至可以创建一个指向软链的软链,而硬链只支持链接到具体的文件(在软链上创建硬链会直接链接到软链指向的具体文件)
  • 软链的目标可以是不存在的,而硬链则必须是真实存在的文件
  • 软链可以跨磁盘,而硬链则不行
  • 删除软链对原始文件没有影响,删除硬链则会减少原始文件的引用计数
  • 移动原始文件或链接文件,软链可能会失效,而硬链则不会

调试跨包代码

要调试跨包代码,首先需要区分两个大场景

  • 跨包代码在同一个仓库
    • 在这种情况下,项目结构一般就是monorepo的形式,以lerna为例,在lerna bootstrap时,就会链接任何交叉依赖项
  • 跨包代码在不同仓库
    • 在这种情况下,项目的依赖默认会从npm下载,因此需要手工通过npm link进行链接

举例,应用引用一个叫@test的包,首先需要

在@test包中执行npm link ——— 在npm的全局地址(npm —global的缓存处)用软链保存了@test包

在应用中执行npm link @test ——— 在应用的node_modules中将@test替换成软链地址

而用这种方式,可能会碰到几种场景

  1. @test包又引用了@child包,而在实际的代码修改中,你想要修改的是@child的代码
    1. 先在@test包对@child进行一次link
  2. @test包引用了@types/react,并且万恶的写在了dependencies中,你的应用也引入了@types/react
    1. 这个背景是项目中不能存在不同实例的@types/react,如果是工具包可以写在devDependencies或者peerDependencies中
    2. 而如果实在是这种场景下,@test使用的可能是自己node_modules中的@types/react,而你的应用用的是另一个,可以靠webpack的alias或者rollup的global来解决,指定@test的react依赖包位置

ps:如果是产物引用的方式的话,需要在webpack中增加watch,监控代码生成产物

在包管理工具中软、硬链的应用

npm、yarn

npm和yarn都是通过软链的形式来支持workspace的。对于workspace中匹配的包,npm和yarn会在顶层的node_modules里创建同名的软链指向workspace中的文件夹,从而在依赖包中可以引用。

pnpm

pnpm结合使用了硬链和软链:

  • 在全局会有一个.pnpm-store文件夹存储所有下载过的依赖的文件
  • 在每个项目的node_modules文件夹中,pnpm会创建一个.pnpm文件夹,存储项目所依赖的所有依赖包,而依赖包中的文件则是通过硬链的方式指向.pnpm-store文件夹中的文件,从而实现所有包仅需下载一次
  • 在项目的node_modules中则只创建项目直接依赖的包的软链,指向.pnpm文件夹中对应的包的具体版本,.pnpm文件夹中包的依赖也通过类似的方式用软链的方式处理

整体的逻辑如下图:
pnpm原理

  • 在项目的package.json中,我们定义了两个dependency: rimraf与glob
  • pnpm为这两个包在node_modules里分别创建了软链,指向.pnpm文件夹中对应的包
  • 由于rimraf本身也依赖了glob,因此pnpm在rimraf@3.0.2文件夹的node_modules里也创建了glob的软链
  • rimraf与glob文件夹中的具体文件则硬链到.pnpm-store文件夹中,从而实现全局缓存
#前端开发##笔试题目##面经##笔经##前端##学习路径##前端工程师#
全部评论
8607720736 lrwxr-xr-x  2 root   staff   5  2 23 23:07 c.txt -> a.txt    请问这里是说 c.txt 是 a.txt   的一个软连接吗,而且第三列是1
点赞 回复 分享
发布于 2022-04-02 13:13
楼主好厉害,感谢分享啊
点赞 回复 分享
发布于 2022-02-28 17:12

相关推荐

如题,他是要劝退我了吗
椛鸣:根据你的时间 来给你安排任务 如果你时间长 可能会参与到一些长期的项目 时间短 那就只能做点零工
点赞 评论 收藏
分享
野猪不是猪🐗:我assume that你must技术aspect是solid的,temperament也挺good的,however面试不太serious,generally会feel style上不够sharp
点赞 评论 收藏
分享
评论
5
7
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务