阅读 107

从0到0.5创建一个monorepos项目

Monorepos介绍

Monorepos简单点来说就是一个项目下有好几个独立的子模块,这些子模块可以单独运行、打包、发布,但是这些子模块又在同一个git仓库下管理,而且这些子模块可能存在相互的依赖关系。

用这种方式管理项目的好处就是既方便统一管理这些子模块,又可以最大程度上降低模块间的耦合。

目前很多大型项目都采用的这种管理方式,比如Babel、React、Vue等等。

去github上看一下create-react-app的目录结构

可以看到packages文件夹下面有很多个子模块,如create-react-appreact-dev-utilsreact-scripts...,这些模块全都可以独立的发布到npm

同时它们又在同一个git仓库里维护,所以create-react-app是一个典型的采用monorepos方式管理的项目。

创建前的准备

创建一个monorepos项目需要用到lerna这个多包管理工具,以及Yarnwrokspaces 特性,所以先要把这两个工具全局安装一下,安装方式这里就不多做介绍了。

然后创建一个项目文件夹,名字随意,我这里取名叫lerna-test

开始创建

初始化

lerna-test下执行如下命令

lerna init
复制代码

成功之后目录结构如下

修改配置

  • 修改lerna.json

    添加npmClient配置如下

    {
      "packages": ["packages/*"],
      "npmClient": "yarn",
      "version": "0.0.0"
    }
    复制代码
  • 修改package.json

    添加workspaces配置如下

    {
      "name": "root",
      "private": true,
      "workspaces": [
        "packages/*"
      ],
      "devDependencies": {
        "lerna": "^3.20.2"
      }
    }
    复制代码

初始化子模块

为了方便说明,我这里用lerna初始化了一个普通项目,以及用create-react-app初始化了一个react项目

首先进入到/packages文件夹下,分别创建如下两个子项目

  • 创建普通项目

    lerna create aaawu
    复制代码

    aaawu这个项目名字可以随意取

  • 创建react项目

    yarn create react-app test-react
    复制代码

    test-react这个项目名字可以随意取

两个子项目都创建好,目录结构应该是这样的

添加依赖关系

正常的Monorepos项目里,各模块之间很有可能存在相互的依赖关系,接下来我们假设test-ract需要依赖aaawu

打开/packages/test-react/package.json,在dependencies下手动添加一行依赖

"aaawu": "^0.1.0"
复制代码

0.1.0这个版本号对应了/packages/aaawu/package.jsonversion

此时命令行执行如下命令,查看各个模块的依赖关系

yarn workspaces info
复制代码

结果如下

yarn workspaces v1.12.1
{
  "aaawu": {
    "location": "packages/aaawu",
    "workspaceDependencies": [],
    "mismatchedWorkspaceDependencies": []
  },
  "test-react": {
    "location": "packages/test-react",
    "workspaceDependencies": [
      "aaawu"
    ],
    "mismatchedWorkspaceDependencies": []
  }
}
复制代码

已经可以看到依赖添加成功了。

建立关联

现在test-reactaaawu的依赖关系有了,怎么才能让test-react项目里能importaaawu里的代码呢?

和普通的yarn add ×××不同,本地开发的时候我们需要的是引用本地的aaawu,而不是线上npm里的项目。

这个时候yarnworkspaces就起到作用了,它会自动管理/package.jsonworkspaces字段指定包下的所有依赖。由于我们之前配置的是

"workspaces": [
    "packages/*"
  ]
复制代码

所以,packages文件下所有子包的依赖关系,它都直接解决了。

命令行执行

yarn
复制代码

test-reactaaawu需要的所有依赖包都被统一安装到根目录下的node_modules里(除了因版本冲突无法统一的包)。因版本冲突无法统一的包,还是会安装在各个模块下的node_modules里。

此时查看根目录下node_modules

看到aaawu和其他的不太一样,旁边有个箭头。这个确实和从线上下载下来的包不同,它其实指向的是本地/packages/aaawu,它是一个镜像,也就是说/packages/aaawu里的代码改动这边也会一起改变。

运行项目

依赖都安装完了,接下来就可以直接运行项目了

切换到/packages/test-ract目录下,直接

npm start
复制代码

test-react会正常跑起来

修改代码

我们想要在test-react项目里试一下能不能importaaawu中的方法。直接在原有代码的基础上做最小的改动,小试牛刀一下

  • 修改/packages/aaawu/lib/aaawu.js
"use strict";

module.exports = aaawu;

function aaawu() {
  // TODO
  return "this is aaawu";
}

复制代码
  • 修改/packages/test-react/src/App.js
import React from "react";
import logo from "./logo.svg";
import A from "aaawu";
import "./App.css";

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          {A()}
        </a>
      </header>
    </div>
  );
}
export default App;
复制代码

此时不用刷新页面,便能看到页面上已经正确显示出内容了

以上,子模块存在相互依赖关系的monorepos项目,创建成功,并能成功运行