大家好,今天从计算机文件系统的软硬链接(hard-link symbolic-link
)入手,了解前端的包管理工具。为什么从这个角度了解呢?了解软硬链接有什么用呢,可以带着下边的几个问题思考一下
- npm/yarn link 是干嘛的
- pnpm 为什么是 performant(高性能的) npm
文件系统:Window2000及以上采用NTFS和Linux7及以上采用的XFS
一、概念和实操验证
硬链接(hard link,也称链接)就是一个文件的一个或多个文件名。再说白点,所谓链接无非是把文件名和计算机文件系统使用的节点号链接起来。因此我们可以用多个文件名与同一个文件进行链接,这些文件名可以在同一目录或不同目录。
软链接文件有类似于Windows的快捷方式。它实际上是一个特殊的文件。在符号连接中,文件实际上是一个文本文件,其中包含的有另一文件的位置信息。
1. Linux 下的软硬链接实操
以下命令是我在Linux 虚拟机中的操作
- 第一步新建testfile文件;
- 第二步给文件输入内容'有朋自远方来';
- 第三步通过ln 在同文件路径下建立硬链接文件hardtestfile;
- 第四步修改被硬链接的文件,加上'虽远必诛';
- 第五步查看源文件testfile 内容和被硬链接的文件同步被修改了\
[wyq@localhost f]$ touch testfile //新建testfile文件
[wyq@localhost f]$ echo '有朋自远方来'>testfile
[wyq@localhost f]$ cat testfile有朋自远方来
[wyq@localhost f]$ ln testfile hardtestfile
[wyq@localhost f]$ lshardtestfile testfile
[wyq@localhost f]$ echo '虽远必诛'>>hardtestfile
[wyq@localhost f]$ cat testfile // testfile内容变化
有朋自远方来 虽远必诛
2. Linux 下的软链接
在上边已经建立testfile文件的基础上
- 我们执行ln -s testfile softtestfile // ln -s 建立testfile的软链接
- 修改软链接的文件内容
- echo '不亦乐乎'>>softtestfile
[wyq@localhost f]$ ln -s testfile softtestfile //新建testfile文件
[wyq@localhost f]$ cat softtestfile有朋自远方来 虽远必诛
[wyq@localhost f]$ lshardtestfile testfile
[wyq@localhost f]$ echo '不亦乐乎'>>softtestfile
[wyq@localhost f]$ cat softtestfile // testfile内容变化
有朋自远方来 虽远必诛 不亦乐乎
我们在通过 ls -li
[wyq@localhost f]$ ls -litotal 819446343 -rw-rw-r--. 2 wyq wyq 45 Jan 17 19:37 hardtestfile
19446346 lrwxrwxrwx. 1 wyq wyq 8 Jan 17 19:27 softtestfile -> testfile
19446343 -rw-rw-r--. 2 wyq wyq 45 Jan 17 19:37 testfile
发现不论是源文件还是软、硬链接文件,一个发生修改,其他数据也都会发生改变
硬链接的特点
- 不论是修改源文件(
testfile
),还是修改硬链接文件(hardtestfile
),另一个文件的数据都会改变 - 不论是删除源文件,还是删除硬链接文件,只要还有一个文件存在,这个文件(inode 号是
19446343
的文件)都可以被访问,硬链接不会建立新的 inode 信息,也不会更改 inode 的总数。 - 硬链接不能跨文件系统(分区)建立,因为在不同的文件系统中,inode 号是重新计算的。
- 硬链接不能链接目录,因为如果给目录建立硬链接,那么不仅目录本身需要重新建立,目录下所有的子文件,包括子目录中的所有子文件都需要建立硬链接,这对当前的 Linux 来讲过于复杂。
不同的是软链接会真正建立自己的 inode 索引和 block,所以软链接和源文件的 inode 号(19446346
)是不一致的,而且在软链接的 block 中,写的不是真正的数据,而仅仅是源文件的文件名及 inode 号,所以软链接和硬链接在原理上最主要的不同在于:
硬链接不会建立自己的 inode 索引和 block(数据块),而是直接指向源文件的 inode 信息和 block,所以硬链接和源文件的 inode 号是一致的;而软链接会真正建立自己的 inode 索引和block,所以软链接和源文件的 inode 号是不一致的,而且在软链接的 block 中,写的不是真正的数据,而仅仅是源文件的文件名及 inode 号
3. Window下的软、硬链接
可使用mklink,具体指令就不再赘述了,可自行搜索
不过window系统下有三个链接概念hard-link、Junction、symbolic-link
,可查阅文末的参考文档1
二、NPM/Yarn Link
我们了解hard link
和Symbolic link
之后,再来看看npm link是干什么的,npm link
是npm 的一个命令,用来建立文件的软链接(symbolic-link
)
我们用一个场景来实操一下,本质其实就是关联两个文件夹,比如调试npm包或者是写webpack loader这些场景等,都可以使用npm link 做文件夹的关联
步骤如下
- 在C盘新建一个文件夹 link(可以当作看作是npm包的工程),npm init 初始化package.json(
"name": 'testnpmlink')
- 进入到link目录下 执行 npm link,就会生成一个全局的软链接
- 在项目工程(要加载使用link工程)目录,执行 npm link testnpmlink
步骤1、2-全局的软链接
步骤3的结果-项目中的安装
通过截图可以看到在node.js
的安装目录和项目中的node_modules
都可以修改调试。理解了上边的软链接的概念和特点,就清楚npm link
做了哪些事
三、Why Pnpm
Pnpm 为什么是 performant(高性能的) npm
pnpm
内部使用基于内容寻址的文件系统来存储磁盘上所有的文件,这个文件系统出色的地方在于:
不会重复安装同一个包。用 npm/yarn 的时候,如果 100 个项目都依赖 lodash,那么 lodash 很可能就被安装了 100 次,磁盘中就有 100 个地方写入了这部分代码。但在使用 pnpm 只会安装一次,磁盘中只有一个地方写入,后面再次使用都会直接使用 hardlink。
即使一个包的不同版本,pnpm 也会极大程度地复用之前版本的代码。举个例子,比如 lodash 有 100 个文件,更新版本之后多了一个文件,那么磁盘当中并不会重新写入 101 个文件,而是保留原来的 100 个文件的 hardlink,仅仅写入那一个新增的文件。
神三元
总结来说就是安装速度很快,节省磁盘空间,还有安全性等,具体可参考 这篇文章
本文还是主要介绍pnpm
的连接方式,上边也提到了pnpm
只会安装一次,后边的使用都是通过hardlink
的方式,看如下截图
总结上边的两个截图我们可以总结来说是包含两方面的信息
1. CAS(Content-addressable store)
内容寻址存储,它是一种存储信息的方式,根据内容而不是位置进行检索信息的存储方式。
2. Virtual store
虚拟存储,指向存储的链接的目录,所有直接和间接依赖项都链接到此目录中,项目当中的.pnpm目录。
我们安装express之后,提示该packages已经从内容寻址的储存地址被硬链接到一个虚拟的储存位置
下边分别列出来了Content-addressable store
是在 我的这个目录下
C:/Users/wyq/.pnpm-store/v3
Virtual store 是在实际项目的node_modules/.pnpm下
下边截图是node_modules
通过符号链接显示的布局,node_modules
中每个包的每个文件都是来自内容可寻址存储的硬链接
这里稍微提一下,pnpm文档也提到的一个点,就是在项目中使用pnpm遇到问题时,官方其中一个不太推荐的解决方案是使用shamefully-hoist
选项。这会创建一个扁平的node_modules
结构,跟npm/yarn类似的hoist机制,hoist机制的问题可以自行了解下。
这种做法 is shamefully ,这个词挺有意思,闲扯一下:我个人理解有两层意思吧,一层是pnpm官方的歉意, pnpm跟npm/yarn不一样的思路,在遇到实在解决不了的问题,实在抱歉;另一层意思是我能非扁平化解决依赖问题,你再使用hoist
,你就太"无耻"了。
再说下pnpm跟硬链接有关系的一个问题,就是由于硬链接的特点,不能跨多个驱动器或文件系统工作,分两种情况:
1. 存储路径已指定
pnpm config set store-dir /path/to/.pnpm-store
如果我们在磁盘C上执行 pnpm install,则 pnpm 存储必须位于磁盘 C。如果 pnpm 存储位于磁盘D,则所有需要的包将被直接复制到项目位置而不是链接。这个严重的抑制了 pnpm 的存储和性能优势。
2. 存储路径未指定
则会创建多个存储(每个驱动器或文件系统一个)。如果在磁盘C上pnpm install
,则存储将在C的文件系统根目录下的 .pnpm-store
下被创建。如果在磁盘D上安装运行,将会在D上的 .pnpm-store
处创建一个独立的存储。项目仍将保持 pnpm 的优势,但每个驱动器可能有冗余包。
最后重要提醒
get hands dirty!
get hands dirty!
get hands dirty!
参考文档
1. https://docs.microsoft.com/en-us/windows/win32/fileio/hard-links-and-junctions2. https://docs.microsoft.com/en-us/windows/win32/fileio/creating-symbolic-links3. https://pnpm.io/