实习生在开源项目的求生经历:认识项目

本文将站在一个实习生的角度,分享笔者在 LogicFlow 中的实习经验和对于所遇问题的一些分析解决思路。本篇文章并非把实习总结一条条的罗列出来,而是把实习的经历和技术文章结合到一块儿,讲述实习生是如何在开源项目中快速成长的,与技术文章相比,阅读起来相对轻松。node

前言

往前数算刚好一全年,当时大三的我刚刚加入春招大军,正在一场场面试中因被面试官拒绝而怀疑自我,又在一次次被拒后的自我反思中重拾自信,过了两个多月才在春招的末尾找到了第一份实习工做,随即跨越半个中国来到深圳的一家银行类公司,跟着师兄作了一些零零散散的小项目。在第一份实习中,最主要的收获仅仅是感觉到了职场与校园之间的一些差别,而在技术上,因为受到项目体量的限制,成长速度较为缓慢。第一份实习接近尾声时恰逢秋招伊始,或许是受益于第一段实习的经历,个人简历和面试都得到了不错的反馈,最终从手里的 offer 中选择了滴滴,校招也就此顺利收官。git

与滴滴签约后,想着早些熟悉一下之后的同事和项目,我提早来到北京实习。入职第一天,中午吃饭时从师兄口中得知,团队正在作一个开源项目(LogicFlow),我实习阶段的主要工做就是协助他们作一些开发。程序员

听到开源项目的时候,个人心里一颤,在潜意识里,开源项目开发难度大,且对开发者的技术水平要求高,自觉有些难以胜任,同时又以为异常幸运,历来没有想到能够在实习阶段遇上“造火箭”,将来会有大量的机遇推进我去学习新的知识,就这样带着一全年中最大的幸运开始了本身的实习生活。github

启动项目

刚入职时,组内大佬们就已经完成了 LogicFlow 的核心功能以及对应文档,此时的 LogicFlow 还未开源,个人起步工做是为文档添加可操做的示例。面试

初次见面

拿到内部仓库的访问权限后,我把代码下载到本地,粗略浏览了一下项目目录的大体结构:shell

LogicFlow
├── docs		    // 文档
├── examples		    // 示例
├── packages
│   ├── core		    // 核心包
│   │   ├── src
│   │   └── package.json
│   ├── extension	    // 拓展包
│   │   ├── src
│   │   └── package.json
│   └── site		    // 调试示例
│       ├── src
│       └── package.json
├── .commitlintrc.js
├── babel.config.js
├── eslintrc.js
├── .gitignore
├── lerna.json
├── package.json
├── developer.md
├── README.md
└── yarn.lock
复制代码

看完目录后我获取到了如下主要信息:npm

  • LogicFlow 的文档在 docs 目录下
  • 我要开发的文档示例在 examples 目录下
  • packages 目录中包含了 LogicFlow 的核心包、拓展包、示例站点
  • 一个历来没有见过的文件 - lerna.json
  • 开发者上手指引 - developer.md
  • LogicFlow 项目介绍 - README.md

产生疑惑

看完目录后肯定了下一步要作的事情:跟着 developer.md 为项目安装依赖。json

从 developer.md 中能够得知安装依赖的命令:bootstrap

npm run bootstrap
复制代码

执行完成以后,LogicFlow 的根目录和 examples、core、extension、site 目录下都多了一个 node_modules 文件夹,此时我产生了几个疑问:bash

  • 为何 LogicFlow 要在一个项目中存放多个项目?
  • npm run bootstrap 命令是如何作到同时为多个项目安装依赖的?

好奇心驱使我查看了 npm run bootstrap 中的原始命令,在根目录的 package.json 中,bootstrap 是这样定义的:

{
  "scripts": {
    "bootstrap": "yarn && lerna bootstrap",
  }
}
复制代码

很明显,npm run bootstrap 先经过 yarn 根据根目录的 package.json 为项目安装了全局依赖,而后经过 lerna bootstrap 为 examples、core、extension 和 site 各自安装了依赖。

分析盲点(monorepo 与 lerna)

到目前为止,我能够肯定 LogicFlow 使用 lerna 这个东西对项目进行了工程化管理,lerna 支持了一个项目中能够存放多个子项目,同时 lerna 对我来讲是一个新的知识盲点。

带着疑惑,找到了 lerna 的代码仓库,在官方 README.md 中,我快速捕捉了如下关键信息:

  • lerna 是一个 JavaScript 项目的管理工具,这些项目内部能够包含多个包
  • 这种一个项目包含多个包的形式被称为 multi-package repository(或者 monorepo)

同时找到了 lerna 的核心命令 lerna bootstrap,它主要完成了如下内容:

  • 在项目的每一个包的目录下执行 npm install,为其安装外部依赖
  • 为相互依赖的两个包之间创建软链(symlink)

软链关系以下:

/node_modules
/examples
    └── node_modules
        ├── @logicflow/core --> /packages/core // 软链
	└── @logicflow/extension --> /packages/extension // 软链
/packages
    ├── core
    │   └── node_modules
    ├── extension
    │   └── node_modules
    │       └── @logicflow/core --> /packages/core // 软链
    └── site
        └── node_modules
            ├── @logicflow/core --> /packages/core // 软链
            └── @logicflow/extension -> /packages/extension // 软链
复制代码

如今就能够解释以前的几个疑问了:

  • npm run bootstrap 命令是如何作到同时为多个项目安装依赖的?
    • npm run bootstrap 在每一个包目录下执行了 npm install
  • 为何 LogicFlow 要在一个项目中存放多个项目?
    • extension 依赖于 core,而 examples 和 site 依赖于 extension + core,将它们放到一个项目中能够实现同时开发、同时发版,且 lerna 的 symlink 能够保证各个包直接依赖于其余包在本地的最新代码
  • 根目录下那个历来没有见过的文件 lerna.json 是干啥的?
    • 是 lerna 的配置文件,能够告诉 lerna 项目中存在哪些 packages

成功启动

已经知道,各个包之间能够直接引用本地的最新代码进行开发,如今咱们应该在启动项目以前,先把各个包构建一下以便其余包引用,如 developer.md 中的构建命令:

# 先构建类型
# LogicFlow 使用 TS 进行开发,不一样的包之间存在类型依赖
npm run build:types
 # 构建源码
npm run build
复制代码

在根目录执行完上面两个命令以后,就能够进入 examples 目录进行开发调试了:

cd examples
npm run start
复制代码

工具优化

到了 LogicFlow 开源时,项目的多包管理形式发生了新的变化。在 LogicFlow 正式发布的过程当中,师兄添加了一个 yarn workspaces 的功能,这一样又触及到了个人知识盲区。

新的疑问(workspaces)

有了 yarn workspaces 以后,package.json 发生了变动:

{
  "workspaces": ["packages/*", "examples"],
  "scripts": {
    "bootstrap": "yarn",
  }
} 
复制代码

在 package.json 中新增了一个 workspaces 的字段,而 npm run bootstrap 命令只经过执行 yarn 来安装依赖。安装依赖以后,与添加 yarn workspace 以前相比,惟一不一样的点来自于各个包的 node_modules 内部,即各个包中若是有相同的依赖,那么这些依赖会被提取到根目录下的 node_modules 中,而整个项目在本地的体积就会大大缩小。

例如,在 core、extension、examples、site 这四个包中都使用了 @babel/core 这个工具,那么 @babel/core 就会被提取到根目录下的 node_modules 中,而那四个包的 node_modules 目录就缩减了下来。

/node_modules
    ├── @babel/core
    ├── @logicflow/core --> /packages/core // 软链
    └── @logicflow/extension --> /packages/extension // 软链
/examples
    └── node_modules
/packages
    ├── core
    │   └── node_modules
    ├── extension
    │   └── node_modules
    └── site
        └── node_modules
复制代码

与添加 yarn workspaces 以前相比,最重要的一点是软链(symlink)的功能保存了下来,到此为止,这个 yarn workspaces 又让我产生了新的疑惑:

  • 既然 yarn workspaces 和 lerna 均可觉得各个包安装依赖并添加软链,那么这二者的区别是什么?
  • 为何有了 yarn workspaces 以后,仍然要保留 lerna?

带着这几个疑问,又开始了新的探索。

workspaces 与 lerna 的对比

一样的,我找到了 yarn 对 workspaces 的介绍,文档中表示,yarn workspaces 可以完成如下任务:

  • 能够为本地相互依赖的包之间建立软链(symlink)
  • 将各个包之间的依赖安装到一块儿,放到根目录下
  • 各个包使用同一个 yarn.lock 文件,以减小冲突,方便查看

也就是说,yarn workspaces 一样也是一个 monorepo 的管理工具,可是它所提供的功能仅仅与 lerna 提供的底层功能同样,为本地相互依赖的包创建软链,并在此基础上进行优化,将各个包的共同依赖抽离出来到根目录。

yarn workspaces 除了提供 lerna 的底层功能之外,其余高级功能均不支持,这就是为何有了 yarn workspaces 以后,LogicFlow 仍然保留 lerna 的缘由:

  • 使用 yarn workspaces 来代替 lerna bootstrap 安装依赖
  • 仍然使用 lerna 所提供的各类高级功能,例如:
    • 使用 lerna version 为各个包的变动代码添加 tag
    • 使用 lerna publish 来同时发布多个包

因此,针对于 monorepo 的形式,咱们能够同时使用 workspaces 和 lerna 来进行项目管理。另外,在 npm 7.x 版本中也已经支持了 workspaces 功能。

思考总结

从刚加入 LogicFlow 时,项目就已经开始使用 monorepo 的管理形式了,那么就会有对应的疑问,LogicFlow 为何要用 monorepo 呢,它的优势在哪里,它有没有什么缺点呢?

monorepo 的优势

以 LogicFlow 为例,我总结了 monorepo 为项目带来的一些优势:

便于代码引用

monorepo 能够保证 extension 直接引用到本地 core 的最新代码,若是是 multirepo 的形式,须要先通过 core 的发包,而后在 extension 中下载使用。img

monorepo 能够在开发过程当中省去对 core 包的频繁发版与安装。

便于依赖管理

monorepo 能够一次为多个包安装依赖,若是是 multirepo,须要为每一个项目都安装一次依赖。img

使用前文中的 workspaces,还能够将相同的依赖从各个包中抽离出来,减小整个项目在本地的体积。

便于快速发版

在 LogicFlow 中,extension 依赖于 core 的代码,当 core 发生变动后,extension 经常也要随之改变,为了保证这两个包之间的依赖关系清晰稳定,LogicFlow 始终保持二者的版本号一致,这就须要同步地快速发版,monorepo 能够保证同时对多个包进行操做。

img

monorepo 的缺点

实际上,monorepo 的优势基本是由各类工具在 monorepo 的基础上带来的,其 monorepo 自己也存在缺点。

管理成本

在 monorepo 中进行多个包的开发、联调、上线时,仍然须要执行屡次打包或上线的命令,例如,当 LogicFlow 的 core 和 extension 发生变更后,对应的 examples 也会变化,这就须要先分别对 core 和 extension 进行打包,而后供 examples 的开发使用,因此 monorepo 的每个动做包含了多个项目的步骤,使得管理成本上升。

解决方案:引入 lerna 来管理 monorepo,能够实现对多个包的同时构建或上线等。

项目体积变大

当全部的包都放到一个项目里,安装依赖以后整个项目的体积会变大,多个类似的 node_modules 会使得项目体积飙升。

解决方案:使用 workspaces 将不一样包中相同的依赖安装到根目录下,以便依赖的复用。

commits 易混乱

在一个 git repository 中修改多个包的代码,会致使多个包的 commits 掺杂在一块儿,在查看 commit log 的过程当中会增长分析成本。

解决方案:规范 commit 格式,在 commit 中注明改动所属的包,例如:fix(core): something。

访问权限控制缺失

多个包放到同一个仓库中意味着它们的访问权限必须是一致的,对于 LogicFlow 这种开源项目并无影响,但当某些项目拥有不一样访问权限的包时,monorepo 并不适用。

梦的开始

每一个程序员都会懂得,职业生涯中可以有一个好的项目做为磨练本身的机会是很是可贵的,开源项目脱离了业务需求中逻辑的重复性和复杂性,保留了对行业内各类前沿技术的应用,是让开发者快速成长的好机会。可以有机会加入 LogicFlow,对我这个实习生而言是很是幸运的,如同作梦同样,在 LogicFlow 中所遇到的每个疑问和难题,都像是有位老师在指引我前行。

最后

本篇文章以一个实习生的视角,讲述了在第一次面对开源项目 LogicFlow 时所遇到的“新概念” - monorepo,并分享了笔者对于 monorepo 学习和理解,monorepo 自己瑕不掩瑜,是开源项目能够考虑使用的一种包的管理形式,在后面的实习阶段,笔者还会将其余所遇到的有意思的新挑战分享给你们。

更多阅读资料:

相关文章
相关标签/搜索