专栏首页Super 前端monorepo--依赖

monorepo--依赖

19年,团队沉淀了组件库、图表库、工具库等基础建设相关内容。上述的内容均为独立工程维护,起初我们采用 Git Subtree + npm install <folder> 来关联各个项目,带来了开发、调试的便利,同时也带了一些复杂性。

11月份,整个底层稳定性显著提高,宿主项目中调试等已不是主要问题;我们的新成员 fusion-utils 诞生,由于 fusion-charts 和 fusion-components 同时需要依赖 fusion-utils,使得依赖冗余&冲突,以及多个独立仓库提交繁琐等问题凸显出来。因此,我们再出发,选择了 monorepo

选型 monorepo 后,并不是无脑的按照其指导来全部切换,为了更平稳过度,我们规划了以下几步:

  • 第一步:采用 yarn workspace 来解决依赖问题(npm install <folder> 就此落幕)
  • 第二步:深度利用 peerDependencies 等,来处理依赖版本问题
  • 第三步:结合 package.json 中 bin字段,利用 yarn link ,创建 node 交互式命令行。使用 Commander 开发了大量 CLI,来简化提交、构建等问题
  • 第四步:多仓合一仓(已论证…)

monorepo

Monorepo is a unified source code repository used by an organisation to host as much of its code as possible.

Monorepo 它是一种管理 organisation 代码的方式,在这种方式下会摒弃原先一个 module 一个 repo 的方式,取而代之的是把所有的 modules 都放在一个 repo 内来管理。-- 这是宗旨,目前团队已尝试,但是否全线切换,仍待考量(因为 依赖问题 已解决,提交复杂性也已通过 CLI 统一)

目前诸如 Babel、React、Angular、Ember、Meteor、Jest 等等都采用了 Monorepo 方式来进行源码的管理。

monorepo 最终目标:将所有相关 module 都放到一个 repo 里,每个 module 独立发布,issue 和 PR 都集中到该 repo 中。不需要手动去维护每个包的依赖关系,当发布时,会自动更新相关包的版本号,并自动发布。

优点:

  • 单一(统一)的校验、构建、测试和发布流程
  • 模块之间的修改、测试更便捷
  • 维护统一的 Issues 地址
  • 更容易设置开发环境

缺点:

  • 代码库体量更大
  • 不能直接从 Github 安装模块 https://github.com/npm/npm/issues/2974
  • monorepo 会产生大量的 commit、branch、tag、git 追踪的文件也会增多。从而导致:
    • commit 增多:git log git blame 变慢
    • refs 增多:git clone git branch git push 变慢
    • tracked 文件增多:git status git commit 变慢

google、Facebook 花费了大量时间在 monorepo 上。但,也有反对者,强力建议不要使用 monorepo,https://medium.com/@mattklein123/monorepos-please-dont-e9a279be011b

yarn workspace

只需运行一次 yarn install 就可以一次安装所有软件包,其具有 hoist 提升功能。

  • 依赖关系可以链接在一起,这意味着可以相互依赖,同时始终使用最新的可用代码。比 yarn link 更好的机制,因为它只影响工作区树而不是整个系统(yarn link 会在全局/usr/local/bin 中增加相关记录,[见下述](###yarn link)
  • 所有的项目依赖项将一起安装,从而为 Yarn 提供了更大的自由度来更好地对其进行优化 – hoist
{
  "private": true,
  "workspaces": [
    "src/charts",
    "src/components",
    "src/fusion-utils"
  ]
}

注意:private:true 是必需的!以确保意外地暴露它们。

这里,src/chartssrc/componentssrc/fusion-utils 都是独立的工程,通过 Git Subtree 来关联这些项目,然后每个项目中都有独立的 package.json 文件,在宿主项目中使用 yarn install 统一安装即可。

{
  "name": "fusion-charts",
  "version": "1.2.0",
  "private": true,
  "description": "基于 Vue 和 ECharts 封装的图表组件",
  "main": "./index.js",
  "module": "./index.js",
  "dependencies": {
    "echarts": "^4.2.0-rc.2"
  },
  "devDependencies": {
    "vue-template-compiler": "^2.6.10"
  },
  "peerDependencies": {
    "fusion-utils": "^1.0.0",
    "vue": "^2.6.10"
  }
}

安装完成,具有类似的文件层次结构:

/package.json
/yarn.lock

/node_modules
/node_modules/echarts
/node_modules/vue
/node_modules/vue-template-compiler
/node_modules/fusion-charts -> src/charts
/node_modules/fusion-components -> src/components
/node_modules/fusion-utils -> src/fusion-utils

/src/charts/package.json
/src/components/package.json
/src/fusion-utils/package.json

echarts、vue 等均安装到了根目录下。代码中对于 fusion-charts 等引用要使用 /workspace-a/package.json#name 字段(上述,name 字段为 fusion-charts),而不是文件夹名称 charts。import {...} from 'fusion-charts'

hoist

独立项目

为了减少冗余,大多数程序包管理器采用某种提升方案将所有相关模块尽可能地提取并展平到一个集中位置。依赖关系树可能像这样:

能够消除重复的 A@1.0B@1.0 ,同时保留版本差异(B@2.0)。通过从项目根目录遍历 “node_modules” 树,大多数模块 crawlers/loaders/bundlers 可以非常有效地定位模块。

monorepo 项目

通过将子模块提升到其父项目的node_modules:monorepo/node_modules来在子项目/程序包之间共享模块。解决了相互依赖时的冗余度(如,fusion-chartsfusion-components 都要引用 fusion-utils)。

依赖丢失?

至此,可以从项目的根 node_modules 访问所有模块,但我们通常会在其本地项目中构建每个程序包,这些模块在其自己的 node_modules 下可能不可见。

  • 在项目根目录 “monorepo” 中找不到模块 “B@2.0”(无法遵循符号链接 – symlink)
  • “package-1” 中找不到模块 A@1.0(不知道上面 “monorepo” 中的模块树)

为了使这个 monorepo 项目能够从任何地方可靠地找到任何模块,它需要遍历每个 “node_modules” 树:monorepo/nodemodulesmonorepo/packages/package-1/node_modules

nohoist

禁止将选定的模块提升到项目根目录

"workspaces": {
  "packages": ["packages/*"],
  "nohoist": ["**/react-native", "**/react-native/**"]
}

创建一个 nodejs 命令行包

cli.js

#!/usr/bin/env node

告诉*nix系统,我们的 JavaScript 文件的解释器应该是 /usr/bin/env节点

现在我们可以在 Linux 或 Mac OS X 上以 ./cli.js 或在 Windows 中使用 node cli.js 来运行它

package.json bin 是一个让 Yarn 在包安装时给包创建 cli 命令(二进制)的映射表。

"bin": {
	"fusion-smartV-build-cli": "./bin/cli.js"
}

yarn/npm link 命令允许我们在本地 “symlink a package folder”,它将在本地安装 package.json的 bin 字段中列出的任何命令。根目录下直接执行 yarn link 即可。

yarn link 一个包可以链接到另一个项目

  • 在你想连接的包里,运行 yarn link
  • 使用 yarn link [package] 来链接另一个你想在当前项目里使用的本地包
$ cd project1
$ yarn link

$ cd project2
$ yarn link project1

这会创建一个符号链接 project2/node_modules/project1 连接到你本地的project1 项目副本

peerDependencies

peerDependencies的目的是提示宿主环境去安装满足插件peerDependencies所指定依赖的包,然后在插件import或者require所依赖的包的时候,永远都是引用宿主环境统一安装的npm包,最终解决插件与所依赖包不一致的问题。

"peerDependencies": {
	"fusion-utils": "^1.0.0",
  "vue": "^2.6.10"
}

它要求宿主环境安装 fusion-utils^@1.0.0vue@2.6.10 的版本。组件中引入的 fusion-utils 和 vue 包其实都是宿主环境提供的依赖包。

参考地址

  • https://juejin.im/entry/586f00bc128fe100580a6f78
  • https://www.toptal.com/front-end/guide-to-monorepos
  • https://yarn.bootcss.com/docs/workspaces/
  • https://medium.com/netscape/a-guide-to-create-a-nodejs-command-line-package-c2166ad0452e

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • vue-router详解及实例

    用户进行了交互操作,现在要对页面内容进行变更,可以通过javascript进行动态替换DOM,但是其不便于分享、收藏,对于搜索引擎和用户来说都是不友好的!

    奋飛
  • 深入理解ES6--解构

    通过在数组中的位置进行选取,且可以将其存储在任意变量中,未“显式声明”的元素都会被直接被忽略。

    奋飛
  • 【实例】调整区域大小&动态隐藏区域

    实例参照地址:https://jsfiddle.net/381510688/fb6Lz9rm/

    奋飛
  • POJ--3468 A Simple Problem with Integers(线段树)

    A Simple Problem with Integers Time Limit: 5000MS Memory Limit: 131072K ...

    ShenduCC
  • HDOJ 1009(贪心)

    Problem Description FatMouse prepared M pounds of cat food, ready to trade with...

    dejavu1zz
  • 设计模式 - 迭代器模式 - JavaScript

    迭代器模式是指提供一种方法顺序访问一个集合对象的各个元素,使用者不需要了解集合对象的底层实现。

    心谭博客
  • 成为DBA的10条规则

    原文地址 https://datatechnologytoday.wordpress.com/2018/10/02/10-rules-for-succeedin...

    二狗不要跑
  • 学习Python一年,基础忘记了,看看面试题回忆回议,Python面试题No3

    git,svn两个都要说到,github,码云也要提及,面试官想要的就是版本管理工具,你只要选择一个你熟悉的,疯狂的说一通就可以了,最好说一下自己以前做过哪些开...

    梦想橡皮擦
  • DBA生存警示:业务高峰误操作案例及建议

    编辑手记:对于资深的老DBA们,他们在漫长的职业生涯中养成了很多稀奇古怪的守则,以在复杂多变的环境中“幸存”,这源于无数血泪的教训,我曾经在《数据安全警示录》一...

    数据和云
  • 爬虫 | 五八字体反爬

    https://sz.58.com/ershouche/pn2/?PGTID=0d100000-0000-4e81-5801-e3cfbaae2802&Clic...

    用户6825444

扫码关注云+社区

领取腾讯云代金券