zoukankan      html  css  js  c++  java
  • Monorepo——大型前端项目的代码管理方式

    1.Monorepo

    Monorepo 是管理项目代码的一个方式,指在一个项目仓库 (repo) 中管理多个模块/包 (package),不同于常见的每个模块建一个 repo。

    目前有不少大型开源项目采用了这种方式,如 Babel

    How is the repo structured?
    The Babel repo is managed as a monorepo that is composed of many npm packages.

    还有 create-react-appreact-router 等。可以看到这些项目的第一级目录的内容以脚手架为主,主要内容都在 packages 目录中、分多个 package 进行管理。

    ├── packages
    |   ├── pkg1
    |   |   ├── package.json
    |   ├── pkg2
    |   |   ├── package.json
    ├── package.json

    monorepo 最主要的好处是统一的工作流和Code Sharing:

    比如我想看一个 pacakge 的代码、了解某段逻辑,不需要找它的 repo,直接就在当前 repo;当某个需求要修改多个 pacakge 时,不需要分别到各自的 repo 进行修改、测试、发版或者 npm link,直接在当前 repo 修改,统一测试、统一发版。只要搭建一套脚手架,就能管理(构建、测试、发布)多个 package。

    不好的方面则主要是 repo 的体积较大:

    特别是,因为各个 package 理论上都是独立的,所以每个 package 都维护着自己的 dependencies,而很大的可能性,package 之间有不少相同的依赖,而这就可能使install时出现重复安装,使本来就很大的 node_modues 继续膨胀(我称这为「依赖爆炸」...)。

    基于对以上的理解,我认为当项目到一定的复杂度,需要且可以划分模块、但模块间联系紧密的,比较适合用 monorepo 组织代码。

    目前最常见的 monorepo 解决方案是 Lerna 和 yarn 的 workspaces 特性。其中,lerna 是一个独立的包,其官网的介绍是:

    a tool that optimizes the workflow around managing multi-package repositories with git and npm.

    上面提到的 Babel, create-react-app 等都是用 lerna 进行管理的。在项目 repo 中以lerna.json声明 packages 后,lerna 为项目提供了统一的 repo 依赖安装 (lerna bootstrap),统一的执行 package scripts (lerna run),统一的 npm 发版 (lerna publish) 等特性。对于「依赖爆炸」的问题,lerna 在安装依赖时提供了--hoist选项,相同的依赖,会「提升」到 repo 根目录下安装,但lerna 直接以字符串对比 dependency 的版本号,完全相同才提升,semver 约定在这并不起作用。

    而使用 yarn 作为包管理器的同学,可以在 package.json 中以 workspaces 字段声明 packages,yarn 就会以 monorepo 的方式管理 packages。相比 lerna,yarn 突出的是对依赖的管理,包括 packages 的相互依赖、packages 对第三方的依赖,yarn 会以 semver 约定来分析 dependencies 的版本,安装依赖时更快、占用体积更小;但欠缺了「统一工作流」方面的实现。

    lerna 和 yarn-workspace 并不是只能选其一,大多 monorepo 即会使用 lerna 又会在 package.json 声明 workspaces。这样的话,无论你的包管理器是 npm 还是 yarn,都能发挥 monorepo 的优势;要是包管理是 yarn ,lerna 就会把依赖安装交给 yarn 处理。

    2.简单示例:

    首先全局安装lerna: 

    npm i -g lerna

    创建一个项目文件夹并生成.git文件:

    git init lerna

    初始化lerna: 

    lerna init

    在生成的packages文件夹中添加两个包:

    mkdir module1、mkdir module2

    在module1中创建package.json: 

    npm init -y

    在module1中新建index.js文件:

    require("module2")

    在module1中修改package.json:

    "dependencies": {
     "module2": "^1.0.0"
    }

    为packages目录下所有包安装它们的依赖,为内部互相依赖的package建立symlink,对所有的package执行npm prepublish:

    lerna bootstrap

    使用lerna将公共的依赖下载到外部,非公共的依赖下载到包本身的配置("hoist": true),然后执行lerna bootstrap:

    //lerna.json
    "packages": [
        "packages/*"
      ],
      "command": {
        "bootstrap": {
          "hoist": true
        }
      },

    上述的--hoist选项设置为true时,相同的依赖,会「提升」到 repo 根目录下安装,但lerna 直接以字符串对比 dependency 的版本号,完全相同才提升,semver 约定在这并不起作用。为解决这个问题可以使用yarn的workspaces 字段,在 package.json 中以 workspaces 字段声明 packages,yarn 就会以 monorepo 的方式管理 packages。相比 lerna,yarn 突出的是对依赖的管理,包括 packages 的相互依赖、packages 对第三方的依赖,yarn 会以 semver 约定来分析 dependencies 的版本,安装依赖时更快、占用体积更小;

    在项目根目录下的package.json中添加:

    {
      "private": true,
      "workspaces": ["module1", "module2"]
    }
    或:
    {
      "private": true,
      "workspaces": ["packages/*"]
    }

    在项目根目录下的lerna.json中添加以下配置,并注释hoist选项:

    {
      "packages": [
        "packages/*"
      ],
      "useWorkspaces": true,
      "npmClient": "yarn",
      // "command": {
      //   "bootstrap": {
      //     "hoist": true
      //   }
      // },
    }

    lerna和yarn workspace的区别:

    hoist: 提取公共的依赖到根目录的node_moduels,可以自定义指定。其余依赖安装的package/node_modeles中,可执行文件必须安装在package/node_modeles。

    workspaces: 所有依赖全部在跟目录的node_moduels,除了可执行文件

    3.Lerna 命令

    lerna create <name> [loc]

    创建一个包,name包名,loc 位置可选

    Examples

    # 根目录的package.json 
     "workspaces": [
        "packages/*",
        "packages/@gp0320/*"
      ],
      
    # 创建一个包gpnote默认放在 workspaces[0]所指位置
    lerna create gpnote 
    
    # 创建一个包gpnote指定放在 packages/@gp0320文件夹下,注意必须在workspaces先写入packages/@gp0320,看上面
    lerna create gpnote packages/@gp0320

     

    lerna add <package>[@version] [--dev] [--exact]

    增加本地或者远程package做为当前项目packages里面的依赖
    • --dev devDependencies 替代 dependencies
    • --exact 安装准确版本,就是安装的包版本前面不带^, Eg: "^2.20.0" ➜ "2.20.0"

    Examples

    # Adds the module1 package to the packages in the 'prefix-' prefixed folders
    lerna add module1 packages/prefix-*
    
    # Install module1 to module2
    lerna add module1 --scope=module2
    
    # Install module1 to module2 in devDependencies
    lerna add module1 --scope=module2 --dev
    
    # Install module1 in all modules except module1
    lerna add module1
    
    # Install babel-core in all modules
    lerna add babel-core

    lerna bootstrap

    默认是npm i,因为我们指定过yarn,so,run yarn install,会把所有包的依赖安装到根node_modules.

    lerna list

    列出所有的包,如果与你文夹里面的不符,进入那个包运行yarn init -y解决

    lerna import <path-to-external-repository>

    导入本地已经存在的包

    lerna run

    lerna run < script > -- [..args] # 运行所有包里面的有这个script的命令
    $ lerna run --scope my-component test

    lerna exec

    运行任意命令在每个包

    $ lerna exec -- < command > [..args] # runs the command in all packages
    $ lerna exec -- rm -rf ./node_modules
    $ lerna exec -- protractor conf.js
    lerna exec --scope my-component -- ls -la

    lerna link

    项目包建立软链,类似npm link

    lerna clean

    删除所有包的node_modules目录

    lerna changed

    列出下次发版lerna publish 要更新的包。

    原理:
    需要先git add,git commit 提交。
    然后内部会运行git diff --name-only v版本号 ,搜集改动的包,就是下次要发布的。并不是网上人说的所有包都是同一个版全发布。

    ➜  lerna-repo git:(master) ✗ lerna changed                                     
    info cli using local version of lerna
    lerna notice cli v3.14.1
    lerna info Looking for changed packages since v0.1.4
    daybyday #只改过这一个 那下次publish将只上传这一个
    lerna success found 1 package ready to publish

    lerna publish

    会打tag,上传git,上传npm。
    如果你的包名是带scope的例如:"name": "@gp0320/gpwebpack",
    那需要在packages.json添加

    "publishConfig": {
        "access": "public"
      },
    lerna publish 
    lerna info current version 0.1.4
    #这句意思是查找从v0.1.4到现在改动过的包
    lerna info Looking for changed packages since v0.1.4 
    
    ? Select a new version (currently 0.1.4) Patch (0.1.5)
    
    Changes:
     - daybyday: 0.1.3 => 0.1.5 #只改动过一个
    
    ...
    
    Successfully published:
     - daybyday@0.1.5
    lerna success published 1 package

    参考:https://segmentfault.com/a/1190000019350611

    参考:https://segmentfault.com/a/1190000019309820?utm_source=tag-newest

    参考:https://blog.csdn.net/kalinux/article/details/116462002

    参考:https://zhuanlan.zhihu.com/p/350329753

    实现Monorepo的4种方式:https://blog.csdn.net/dfsgwe1231/article/details/105996358

    具体的使用方法移步 Lerna 官网:https://lerna.js.org

    yarn 官网对 workspace的详细说明:Workspaces | Yarn

  • 相关阅读:
    python https请求报错:SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED]
    python打包为exe文件
    文件自定义扫描工具
    pandas 的常用方法
    cisco应用
    Cisco 模拟配置
    python 识别图片上的数字
    OpenSSL
    OpenSSL
    OpenSSL
  • 原文地址:https://www.cnblogs.com/vickylinj/p/15572188.html
Copyright © 2011-2022 走看看