基于yarn workspace创建monorepo项目

223 阅读3分钟

Monorepo是一种项目管理方式,将多个项目的代码集中到一个仓库中,能更好的处理发版的依赖关系、提高代码的复用。 yarn/npm 的workspaces本身就是支持monorepo的,对于重复的依赖,monorepo会将依赖提升到根目录,避免重复安装,在代码开发阶段monorepo的模块之间可以实时引用代码,提升开发效率。

yarn workspace 的常用命令

 yarn workspaces info // 查看monorepo的配置信息, 其中包含模块之间的依赖关系
 yarn add package_name -W // 安装依赖到monorepo的根目录
 yarn remove package_name -W // 移除根目录以依赖

 yarn workspace package_name add package_name // 安装依赖到指定模块
 yarn workspace package_name build // 编译指定模块
 yarn workspace package_name test // 运行指定模块的测试用例

从上面的命令可以看出yarn workspace的好处是不用切换项目的目录,在根目录就能执行子项目的命令

 yarn workspace pages add package_name // 安装依赖到pages模块
 yarn workspace pages statr // 运行项目

当然你仍然可以切换到子项目的目录执行命令,如:

 cd packages/pages
 yarn add package_name

基于yarn workspace 的monorepo

项目结构如下:

|--node_modules
|--packages
|  |--components
|  |  |--node_modules
|  |  |--public
|  |  |  |--index.html
|  |  |--src    
|  |  |  |--index.jsx
|  |  |  |--button.jsx
|  |  |  |--input.jsx
|  |  |--package.json
|  |  |--webpack.config.js
|  |--pages
|  |  |--node_modules
|  |  |--public
|  |  |  |--index.html
|  |  |--src    
|  |  |  |--index.jsx
|  |  |  |--app.jsx
|  |  |--package.json
|  |  |--webpack.config.js
|--package.json
|--.gitignore
|--README.md

一、 初始化

yarn init -y 创建package.json文件

{
  "name": "demo",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "private": true,
  "workspaces": ["packages/*"]
}

二、创建子模块

2.1.创建目录

创建packages/pages、packages/components模块,它都是react项目,pages是业务页面,components是公共组件库,pages依赖components。这里的react项目是手动创建的,也可以通过cra去创建。

2.2 依赖安装

由于两个模块都是react项目,它们的依赖也完全相同,所以这里直接安装到monorepo的根目录共享依赖。

 yarn add react react-dom webpack weback-dev-server @babel/core @babel/preset-env @babel/preset-react
 babel-loader --dev -W

之后在两个模块的目录下分别安装依赖:

 yarn add react react-dom webpack webpack-cli webpack-dev-server @babel/core @babel/preset-env @babel/preset-react babel-loader --dev

2.3 配置webpack 处理jsx文件

pages和components都需要webpack配置,这里以pages为例,配置webpack.config.js文件。

// pages/webpack.config.js
const path = require('path');
module.exports = {
     entry: './src/index.jsx',
     output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
     },

     module: {
        rules: [
            {
                test: /\.jsx$/,
                use: {
                   loader: 'babel-loader',
                   exclude: /node_modules/,
                   options: {
                    presets: ["@babel/preset-env", "@babel/preset-react"]   
                   }
                },
            }
        ]
     },
     resolve: {
        extensions: ['.jsx', '.js']
     },
     mode: 'development',
     devServer: {
        static: {
            directory: path.join(__dirname, 'public'),
            watch: true
        },
        open: true,
        port: 3000,
        hot: true
     }
}

三、components模块的代码

 // components/src/button.jsx
import React, { Component } from 'react'

export default class Button extends Component {
  render() {
    return (
      <div>{this.props.children}</div>
    )
  }
}

// components/src/index.jsx
export { default as Button } from "./Button";
export { default as Input } from "./Input";

四、在pages模块中使用components模块的组件

yarn workspaces pages add components

实际上是通过npm link的方式将两个模块关联起来,这样就可以直接在pages模块中使用components模块的组件,components模块的更新也会同步到pages模块中。

在pages模块中使用components模块的组件

// pages/src/app.jsx
import React, { Component } from 'react'
import  { Button}  from 'components/src/index'

export default class App extends Component {
  render() {
    return (
      <Button>add</Button>
    )
  }
}

yarn workspace pages start运行项目

五、npm发版

cd packages/components
npm version patch // 发布一个小版本
npm publish // 发布到npm仓库

yarn workspace的发版和npm的发版基本一致,为了简化发版流程,可以使用lerna管理monorepo,lerna可以自动化执行版本的发布和自动化生成CHANGELOG。