Skip to content

bbcvc/monorepo

构建monorepo最佳实践

Important

技术栈:pnpm rollup vite umi react18 typescript

预览

infra:https://infra.monorepo.cl96.top/
image

web:https://web.monorepo.cl96.top/ image

一、从零开始构建 monorepo

1. 初始化项目文件夹

// 项目根目录 mkdir monorepo cd monorepo pnpm init -y

2. 创建 pnpm-workspace.yaml

写入以下内容,文件名为 pnpm-workspace.yaml
pnpm-workspace.yaml文件中的 packages 都会被作为 monorepo 的子模块

packages: - 'packages/**' - 'apps/*'

packages下用来存放包

apps下用来存放程序应用

3. 创建子文件夹

mkdir packages mkdir apps

二、配置项目的格式化工具

1. prettier

prettier是什么?
一个代码格式化工具,可以格式化任何代码,使代码更加美观,更加易于阅读,更加易于维护

i. 安装

pnpm add -D prettier --workspace-root

ii. 配置

{ "singleQuote": true, "jsxSingleQuote": true, "semi": false, "useTabs": false, "tabWidth": 2, "bracketSpacing": true, "jsxBracketSameLine": false, "arrowParens": "always" }

iii. 在 CI/CD 中自动格式化

pnpm add -D lint-staged --workspace-root

.lintstagedrc.json

{ "**/*.{js,ts,jsx,tsx}": ["prettier --write"] }

2. Editorconfig

Editorconfig for VS Code 是什么? 一个用于编辑器的配置文件,可以自动格式化代码,使代码更加美观,更加易于阅读,更加易于维护。

这是一个 VS Code 的插件,可以通过扩张商店安装

配置如下:

root = true [*] charset=utf-8 end_of_line=lf insert_final_newline=true indent_style=space indent_size=2 max_line_length = 100 [*.{yml,yaml,json}] indent_style = space indent_size = 2 [*.md] trim_trailing_whitespace = false [Makefile] indent_style = tab 

三、在 packages 中创建一些模块

1. icons

icons 这个包用作项目的图标,因为一些 icon 库并不能完全满足产品或设计上的需求 使用 @svgr/cli 把 svg 转换成 tsx 文件,https://github.com/gregberge/svgr

svgr.config.js

module.exports = { // 将 SVG 转换为适合图标的 React 组件 icon: true, // 替换 SVG 中的颜色值 replaceAttrValues: { '#063855': 'currentColor', // 将 #063855 替换为 currentColor }, // 生成 TypeScript 组件 typescript: true, // 自定义模板 template: (variables, { tpl }) => { return tpl`  import React from 'react';  import { SVGProps } from 'react';   const ${variables.componentName} = (props: SVGProps<SVGSVGElement>) => (  ${variables.jsx}  );   export default ${variables.componentName};  ` }, // 自定义输出文件名和路径 outDir: 'dist', // 输出到 dist/icons 目录 filenameCase: 'kebab', // 文件名格式为 kebab-case(例如 home-icon.tsx) }

2. ui

ui 这个包用作项目的基础组件,包括按钮,输入框,弹窗,等等; 使用 rollup 进行打包,https://rollupjs.org/。目的是为了在 apps 里使用的时候平衡开发体验,入在 vite 中引用这个组件库,减少多余的请求

i. rollup.config.js

import { defineConfig } from 'rollup' import commonjs from '@rollup/plugin-commonjs' import postcss from 'rollup-plugin-postcss' import { nodeResolve as resolve } from '@rollup/plugin-node-resolve' import typescript from 'rollup-plugin-typescript2' import { babel } from '@rollup/plugin-babel' import { name } from './package.json' import { dts } from 'rollup-plugin-dts' export default defineConfig([ { input: 'src/index.tsx', external: ['react', 'react-dom'], plugins: [ typescript({ declaration: true, check: false, // https://github.com/ezolenko/rollup-plugin-typescript2/issues/214 }), resolve({ extensions: ['.tsx', '.ts', '.js'], }), postcss({}), babel({ babelrc: false, exclude: '**/node_modules/**', babelHelpers: 'runtime', skipPreflightCheck: true, presets: ['@babel/preset-react', '@babel/preset-env'], plugins: [ '@babel/plugin-proposal-object-rest-spread', '@babel/plugin-syntax-object-rest-spread', '@babel/plugin-transform-react-jsx', [ '@babel/plugin-transform-runtime', { absoluteRuntime: false, corejs: false, helpers: false, regenerator: false, useESModules: false, }, ], ], }), commonjs(), ], output: [ { name, file: './dist/index.js', format: 'es', globals: { react: 'React', 'react-dom': 'ReactDOM', }, }, { name, file: './dist/cjs/index.cjs', format: 'commonjs', }, { name, file: './dist/es/index.js', format: 'es', }, ], }, { input: 'src/index.tsx', watch: true, output: { file: './dist/index.d.ts', format: 'es', }, plugins: [dts()], }, ])

ii. package.json

{ "name": "@monorepo/ui", "version": "1.0.0", "description": "An infrastructure monorepo ui library for all front-end apps.", "type": "module", "module": "dist/index.js", "main": "dist/index.js", "typings": "dist/index.d.ts", "files": [ "dist/" ], "scripts": { "start": "pnpm run dev", "dev": "rollup --config rollup.config.js --watch", "build": "rollup --config rollup.config.js", "prepare": "pnpm run build" }, ... }

3. pro-components

pro-components 这个包用作项目的业务组件,通常可能是基于多个 ui 组件的复合组件,以满足业务的需求 这个不做打包处理,在开发中多次打包体验也不好

使用如下的配置,可以避免使用桶文件,同时也支持了快捷的引入,直接使用 import ReactLogo from '@monorepo/pro-components/react-logo'

package.json

{ "name": "@monorepo/pro-components", "description": "This is a pro-components.", "private": true, "version": "1.0.0", "type": "module", // 这里的配置是为了支持使用 `import ReactLogo from '@monorepo/pro-components/react-logo'` "exports": { "./*": "./*/index.tsx" }, "sideEffects": false, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@monorepo/ui": "workspace:^", "@monorepo/icons": "workspace:^" } }

4. shared

这里可以存放一些项目的公共函数或者配置,比如 hookstsconfig

四、在 apps 下的项目中引入

1. infra

使用 umi 快速构建一个用于展示基础组件的项目

i. 初始化

pnpm dlx create-umi@latest

ii. 安装packages下的依赖

pnpm add @monorepo/ui @monorepo/icons @monorepo/pro-components --workspace

2. 一个业务项目

i. 使用 vite 创建一个项目

pnpm create vite my-vue-app --template react-ts

ii. 安装packages下的依赖

pnpm add @monorepo/ui @monorepo/icons @monorepo/pro-components --workspace

About

从 0 到 1 构建一个 monorepo 项目

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Contributors