软链接&硬链接在前端中的应用

前置知识

先了解一个操作系统中重要的概念 inode(index nodes)。

inode

inode 是一种文件数据结构,用于存储有关除名称和数据之外的任何 Linux 文件的信息。

inode更详细介绍可参考:www.ruanyifeng.com/blog/2011/1…

  • 扇区(sector)

    • 硬盘的最小存储单位叫「扇区」(sector,每个扇区存储512bit)
    • 操作系统一次读一个扇区效率太低,所以会一次读多个扇区
  • 块(block)

    • 多个扇区组成一个块,最常见的大小为4kb(即由8个扇区组成)
    • 块是文件存取的最小单元,文件数据都存储在块中
  • 索引节点(inode)

    • 存储文件元信息,比如文件的创建者、文件的创建日期、文件大小等
    • Unix/Linux系统内部不使用文件名,而使用inode号码来识别文件。对于系统来说,文件名只是inode号码便于识别的别称或者绰号。
    • 打开文件时,系统首先找到文件的inode号码,然后通过inode号码获取inode信息。然后根据inode中的文件数据所在block读取数据。

node方法获取文件信息

const fs = require('fs')

const fileInfo = fs.statSync('./src/logo.svg')
console.log('文件信息:', fileInfo)
复制代码

输出信息:

文件信息: Stats {
  dev: 16777220,
  mode: 33188,
  nlink: 1,
  uid: 501,
  gid: 20,
  rdev: 0,
  blksize: 4096,
  ino: 114457648, // inode的唯一标识码
  size: 2632,
  blocks: 8,
  atimeMs: 1639292681311.895,
  mtimeMs: 1638968536815.0586,
  ctimeMs: 1638968536815.1282,
  birthtimeMs: 1638968536814.7827,
  atime: 2021-12-12T07:04:41.312Z,
  mtime: 2021-12-08T13:02:16.815Z,
  ctime: 2021-12-08T13:02:16.815Z,
  birthtime: 2021-12-08T13:02:16.815Z
}
复制代码

linux命令获取inode

通过ls -i列出目录中文件以及所有inode标识码:

软链接(符号链接)与硬链接

不论是硬链接或软链接都不会将原本的源文件复制一份,只会占用非常少量的磁盘空间。

硬链接

  • 多个文件可以指向源文件同一inode。
  • 删除其中一个文件不影响对另一个文件的访问,文件内容的修改会同步到所有文件。
  • 只能给文件创建硬链接,不能给目录创建。
  • 适用场景:用于镜像数据文件,防止误删。
  • 源文件和硬链接的关系:

创建硬链接

通过linux命令创建一个硬链接logoHard.svg指向源文件logo.svg

logo.svg是已经存在的,logoHard.svg命令运行后会自动创建。

查看硬链接信息

  • 查看源文件和硬链接文件的inode信息:
const fs = require('fs')

const fileInfo = fs.statSync('./src/logo.svg')
console.log('logo文件信息:', fileInfo)

const fileInfoHardLink = fs.statSync('./src/logoHard.svg')
console.log('logo硬链接文件信息:', fileInfoHardLink)
复制代码
  • 输出信息:
    • 两个文件的ino信息相同,其他信息也完全相同。
    • 其中nlink表示链接数,每多一个文件指向改inode,nlink就会+1。当值减到 0,系统就自动回收 inode 及其对应的 block 区域。
$ node link.js
logo文件信息: Stats {
  dev: 16777220,
  mode: 33188,
  nlink: 2,
  uid: 501,
  gid: 20,
  rdev: 0,
  blksize: 4096,
  ino: 114457648,
  size: 2632,
  blocks: 8,
  atimeMs: 1639295580440.4216,
  mtimeMs: 1638968536815.0586,
  ctimeMs: 1639295575616.7522,
  birthtimeMs: 1638968536814.7827,
  atime: 2021-12-12T07:53:00.440Z,
  mtime: 2021-12-08T13:02:16.815Z,
  ctime: 2021-12-12T07:52:55.617Z,
  birthtime: 2021-12-08T13:02:16.815Z
}
logo硬链接文件信息: Stats {
  dev: 16777220,
  mode: 33188,
  nlink: 2,
  uid: 501,
  gid: 20,
  rdev: 0,
  blksize: 4096,
  ino: 114457648,
  size: 2632,
  blocks: 8,
  atimeMs: 1639295580440.4216,
  mtimeMs: 1638968536815.0586,
  ctimeMs: 1639295575616.7522,
  birthtimeMs: 1638968536814.7827,
  atime: 2021-12-12T07:53:00.440Z,
  mtime: 2021-12-08T13:02:16.815Z,
  ctime: 2021-12-12T07:52:55.617Z,
  birthtime: 2021-12-08T13:02:16.815Z
}
复制代码

软链接

  • 也叫符号链接。
  • 软链接是一个链接文件,指向源文件的地址。类似索引或者指针。
  • 修改源文件内容,软链接内容也会改变。当删除源文件时,访问软链接会报错No such file or directory
  • 源文件与软链接的关系:

20211230-162327.png

创建软链接

linux命令创建

通过linux命令创建一个软链接logoSoft.svg指向logo.svg

node方法创建

通过node提供的fs.symlinkSync(target, path[, type])方法创建,其中target表示源文件,path表示软链接。

const fs = require('fs');
fs.symlinkSync('./src/logo.svg', './logoSoftV2.svg')
复制代码

查看软链接信息

linux命令查看

通过ls -il命令可查看软链接的指向信息 & inode信息。

node方法查看

通过node提供的fs.lstatSync(path[, options])方法查看。其中path表示软链接的路径。

const fs = require('fs');

const fileInfo = fs.statSync('./src/logo.svg')
console.log('logo源文件信息:', fileInfo)

const logoSoftInfo = fs.lstatSync('./src/logoSoft.svg');
console.log('logo软链接文件信息:', logoSoftInfo)
复制代码

输出信息:

  • 软链接和源文件是不同的inode,其他信息也不同
  • 软链接文件只是存储了一个链接信息,所以文件size很小
$ node readSoftLink.js
logo源文件信息: Stats {
  dev: 16777220,
  mode: 33188,
  nlink: 3,
  uid: 501,
  gid: 20,
  rdev: 0,
  blksize: 4096,
  ino: 114457648,
  size: 2632,
  blocks: 8,
  atimeMs: 1639299883906.419,
  mtimeMs: 1639296458198,
  ctimeMs: 1639296496378.1218,
  birthtimeMs: 1638968536814.7827,
  atime: 2021-12-12T09:04:43.906Z,
  mtime: 2021-12-12T08:07:38.198Z,
  ctime: 2021-12-12T08:08:16.378Z,
  birthtime: 2021-12-08T13:02:16.815Z
}
logo软链接文件信息: Stats {
  dev: 16777220,
  mode: 41453,
  nlink: 1,
  uid: 501,
  gid: 20,
  rdev: 0,
  blksize: 4096,
  ino: 114734506,
  size: 14,
  blocks: 0,
  atimeMs: 1639296562078.7632,
  mtimeMs: 1639296562078.7632,
  ctimeMs: 1639296562078.7632,
  birthtimeMs: 1639296562078.7632,
  atime: 2021-12-12T08:09:22.079Z,
  mtime: 2021-12-12T08:09:22.079Z,
  ctime: 2021-12-12T08:09:22.079Z,
  birthtime: 2021-12-12T08:09:22.079Z
}
复制代码

软链接与硬链接的区别

软链接和硬链接通过不同的方式来减少磁盘空间,那他们之间有啥区别,在具体的使用场景下我们应该怎么选择使用哪一种链接方式呢:

软链接 硬链接
inode 软链接与源文件拥有不同的inode,是两个不同的文件 硬链接和源文件拥有同一个inode,它们其实互为硬链接
文件属性 链接文件 与源文件类型相同
跨文件系统建立 支持 不支持
链接数目 (也就是文件信息中的nlink) nlink不会随着软链接数目增加 每增加一个两链接nlink也会加1
删除源文件 软链接无法正常访问 硬链接文件可正常访问
应用 1. pnpm中解决幻影依赖的场景1. 快捷方式(windows中创建桌面快捷方式) 文件备份防误删

软链接 & 硬链接在前端中的应用

yarn link

官方文档:yarnpkg.top/Clilink.htm…

在开发阶段的项目包,可以被其他项目所依赖。因为还在开发阶段,项目包并没有被发布,通常用yarn link这种方式在其他项目中来测试新功能或者debug。

使用方式

场景举例:当我们在业务仓库A(如:ep_student_client)中开发需求时,仓库A依赖了我们业务自定义的sdk。这个sdk是在仓库B(如:packasges仓库中开发的@byted-ep/slardar)中开发的。

开发调试存在的问题:在开发调试时,我们如果不借助yarn link,只能通过编译发布sdk,然后在A仓库中安装sdk来验证。这样开发效率很低。

使用 yarn link:

  • 在仓库B中运行yarn link
  • 在仓库A中运行yarn link [package...]

通过以上命令我们可以实现仓库B中的修改可以同步到仓库A中。

软链接使用

yarn link源码地址:github.com/yarnpkg/yar…

使用@byted-scope-test/demo-cli这个sdk来说明yarn link原理。

  1. 通过args是否有传入参数来判断是运行的yarn link还是yarn link [package...]
  1. yarn link

    1. 创建软链接文件夹,文件路径为/Users/yangxuelian/.config/yarn/link/@byted-scope-test/。所有link都是管理在/Users/yangxuelian/.config/yarn/link目录下的。

    2. 创建软链:

      1. src为当前目录路径/Users/yangxuelian/Documents/practice/lerna-repo/packages/cli
      2. dest为/Users/yangxuelian/.config/yarn/link/@byted-scope-test/demo-cli
  1. yarn link @byted-scope-test/demo-cli

    1. 创建软链接文件夹。

    2. 创建软链

      1. src为yarn link中创建的软链/Users/yangxuelian/.config/yarn/link/@byted-scope-test/
      2. dest为当前想要建立软链接的文件目录:/Users/yangxuelian/Documents/ep/ep_student_client/node_modules/@byted-scope-test/demo-cli

export async function run(config: Config, reporter: Reporter, flags: Object, args: Array<string>): Promise<void> {
  if (args.length) {
    for (const name of args) {
      const src = path.join(config.linkFolder, name);
      if (await fs.exists(src)) {
        const folder = await getRegistryFolder(config, name);
        const dest = path.join(folder, name);
        await fs.unlink(dest);
        await fs.mkdirp(path.dirname(dest));
        await fs.symlink(src, dest);
        reporter.success(reporter.lang('linkUsing', name));
      } else {
        throw new MessageError(reporter.lang('linkMissing', name));
      }
    }
  } else {
    // 创建当前包的软链
    const manifest = await config.readRootManifest();
    const linkLoc = path.join(config.linkFolder, name);

    if (await fs.exists(linkLoc)) {
      reporter.warn(reporter.lang('linkCollision', name));
    } else {
      // 1. 创建软链接文件;2.将软链接指向当前目录
      await fs.mkdirp(path.dirname(linkLoc));
      await fs.symlink(config.cwd, linkLoc);
      // 如果是命令行单独对bin部分做处理
      if (manifest.bin) {
        const globalBinFolder = await getGlobalBinFolder(config, flags);
        for (const binName in manifest.bin) {
          // 省略获取bin的src和dest部分代码。。。。
          if (await fs.exists(binDestLoc)) {
            reporter.warn(reporter.lang('binLinkCollision', binName));
          } else {
            if (process.platform === 'win32') {
              await cmdShim(binSrcLoc, binDestLoc, {createPwshFile: false});
            } else {
              await fs.symlink(binSrcLoc, binDestLoc);
            }
          }
        }
      }
    }
  }
}
复制代码

pnpm

使用方式

当我们运行pnpm install进行node_modules安装的时候,会使用软链接 & 硬链接的方式来节省磁盘空间 & 提升安装效率。

软链接使用

通过执行pnpm install,安装的node_modules中文件会被分为两部分:.pnpm目录 & 其他。

  • .pnpm目录:存放了所有实际安装的包
  • 其他文件:package.json中声明的包,但是只是生成一个软链接。实际指向.pnpm中安装的包。

硬链接使用

pnpm 有个根目录,一般都是保存在 user/.pnpm-store 下,pnpm 通过硬链接的方式保证了相同的包不会被重复下载,比如说我们已经在 repoA 中下载过一次 [email protected] 版本,那我们后续在 repoB 中安装 [email protected] 的时候是会被复用的,具体就是 repoA 中的 express 中的文件和 repoB 中的 express 中的文件指向的是同一个 inode。

参考文档

❤️ 谢谢支持

以上便是本次分享的全部内容,希望对你有所帮助^_^

喜欢的话别忘了 分享、点赞、收藏 三连哦~。

欢迎关注公众号 ELab团队 收货大厂一手好文章~

我们来自字节跳动,是旗下大力教育前端部门,负责字节跳动教育全线产品前端开发工作。

我们围绕产品品质提升、开发效率、创意与前沿技术等方向沉淀与传播专业知识及案例,为业界贡献经验价值。包括但不限于性能监控、组件库、多端技术、Serverless、可视化搭建、音视频、人工智能、产品设计与营销等内容。

字节跳动校/社招内推码: BQEQ9TY

投递链接: jobs.toutiao.com/s/8SudHqT

猜你喜欢

转载自juejin.im/post/7047429181021356062