使用yarn + lerna + rollup + storybook + verdaccio创建react多包组件库

最近在研究多包的管理,参阅了很多大牛项目,比如create-react-app, vue-cli, storybook, material-ui。它们都是采用了lerna做的分包管理。

MateriaUI的一套流程是非常棒的,值得我们学习,他实现了自动修改版本号,发包,自动通过commit的记录生成changelog,因此想着能模仿它来创建一个咱们自己的多包组件库。

全局环境安装

node环境:V16

全局三方库:

npm i yarn verdaccio -g

项目结构搭建

项目结构如下:三个子组件包,一个storybook包
子包的包名分别是

@joseph/base
@joseph/theme
@joseph/composite

分别给三个子包追加依赖:

比如base/theme包:

{
  "name": "@joseph/base",
  "version": "1.0.0",
  "description": "base component",
  "main": "dist/cjs/index.js",
  "module": "dist/esm/index.js",
  "types": "dist/esm/index.d.ts",
  "author": "joseph",
  "license": "MIT",
  "private": false,
  "scripts": {
    "build": "rimraf dist/* && rollup -c"
  },
  "files": [
    "dist"
  ],
  "dependencies": {
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "@emotion/react": "^11.9.0",
    "@emotion/styled": "^11.8.1",
    "@mui/icons-material": "^5.6.0",
    "@mui/lab": "^5.0.0-alpha.76",
    "@mui/material": "^5.6.0",
    "@mui/x-data-grid": "^5.8.0",
    "@joseph/theme": "^1.0.0"
  },
  "devDependencies": {
    "@babel/core": "^7.17.8",
    "@babel/preset-env": "^7.16.11",
    "@babel/preset-react": "^7.16.7",
    "@babel/preset-typescript": "^7.16.7",
    "@rollup/plugin-commonjs": "^21.0.3",
    "@rollup/plugin-image": "^2.1.1",
    "@rollup/plugin-node-resolve": "^13.1.3",
    "rollup": "^2.70.1",
    "rollup-plugin-babel": "^4.4.0",
    "rollup-plugin-dts": "^4.2.0",
    "rollup-plugin-peer-deps-external": "^2.2.4",
    "rollup-plugin-postcss": "^4.0.2",
    "rollup-plugin-terser": "^7.0.2",
    "rollup-plugin-typescript2": "^0.31.2",
    "style-loader": "^3.3.1",
    "styled-components": "^5.3.5",
    "ts-loader": "^9.2.8",
    "typescript": "^4.6.3",
    "rimraf": "^3.0.2"
  }
}

composite包

可以看到,它依赖了theme和base包

{
  "name": "@joseph/composite",
  "version": "1.0.0",
  "description": "base component",
  "main": "dist/cjs/index.js",
  "module": "dist/esm/index.js",
  "types": "dist/esm/index.d.ts",
  "author": "joseph",
  "license": "MIT",
  "private": false,
  "scripts": {
    "build": "rimraf dist/* && rollup -c"
  },
  "files": [
    "dist"
  ],
  "dependencies": {
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "@emotion/react": "^11.9.0",
    "@emotion/styled": "^11.8.1",
    "@mui/icons-material": "^5.6.0",
    "@mui/lab": "^5.0.0-alpha.76",
    "@mui/material": "^5.6.0",
    "@mui/x-data-grid": "^5.8.0",
    "@joseph/theme": "^1.0.0",
    "@joseph/base": "^1.0.0"
  },
  "devDependencies": {
    "@babel/core": "^7.17.8",
    "@babel/preset-env": "^7.16.11",
    "@babel/preset-react": "^7.16.7",
    "@babel/preset-typescript": "^7.16.7",
    "@rollup/plugin-commonjs": "^21.0.3",
    "@rollup/plugin-image": "^2.1.1",
    "@rollup/plugin-node-resolve": "^13.1.3",
    "rollup": "^2.70.1",
    "rollup-plugin-babel": "^4.4.0",
    "rollup-plugin-dts": "^4.2.0",
    "rollup-plugin-peer-deps-external": "^2.2.4",
    "rollup-plugin-postcss": "^4.0.2",
    "rollup-plugin-terser": "^7.0.2",
    "rollup-plugin-typescript2": "^0.31.2",
    "style-loader": "^3.3.1",
    "styled-components": "^5.3.5",
    "ts-loader": "^9.2.8",
    "typescript": "^4.6.3",
    "rimraf": "^3.0.2"
  }
}

storybook包,storybook相关参考官网,此处不全。

{
  "name": "@joseph/storybook",
  "version": "1.0.0",
  "description": "base component",
  "main": "index.js",
  "author": "joseph",
  "license": "MIT",
  "private": false,
  "scripts": {
    "storybook": "start-storybook -p 6009",
    "build-storybook": "build-storybook"
  },
  "dependencies": {
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "@emotion/react": "^11.9.0",
    "@emotion/styled": "^11.8.1",
    "@mui/icons-material": "^5.6.0",
    "@mui/lab": "^5.0.0-alpha.76",
    "@mui/material": "^5.6.0",
    "@mui/x-data-grid": "^5.8.0",
    "@joseph/theme": "^1.0.0",
    "@joseph/base": "^1.0.0",
    "@joseph/composite": "^1.0.0"
  },
  "devDependencies": {
    "@storybook/addon-a11y": "^6.4.22",
    "@storybook/addon-actions": "^6.4.19",
    "@storybook/addon-docs": "^6.4.20",
    "@storybook/addon-essentials": "^6.4.19",
    "@storybook/addon-interactions": "^6.4.19",
    "@storybook/addon-links": "^6.4.19",
    "@storybook/addons": "^6.4.22",
    "@storybook/react": "^6.4.19",
    "@storybook/testing-library": "0.0.9",
    "@storybook/theming": "^6.4.22"
  }
}


还要配置根目录的package.json, 我们使用yarn管理,添加workspace。

{
  "name": "lerna-tpl",
  "version": "1.0.0",
  "description": "for testing",
  "private": true,
  "main": "index.js",
  "scripts": {
    "build": "lerna run build",
    "start": "lerna run storybook --scope @joseph/storybook",
    "publish:local": "lerna publish from-package --registry=\"http://localhost:4873/\""
  },
  "workspaces": ["packages/*", "storybook", "demo/*"],
  "repository": {
    "type": "git",
    "url": "https://gitee.com/cary123/lerna-tpl"
  },
  "keywords": [
    "lerna"
  ],
  "author": "joseph",
  "license": "ISC",
  "devDependencies": {
    "lerna": "^4.0.0",
    "babel-loader": "^8.2.4",
    "babel-preset-react-app": "^10.0.1",
    "eslint": "^8.12.0",
    "eslint-config-google": "^0.14.0",
    "eslint-config-prettier": "^8.5.0",
    "eslint-plugin-prettier": "^4.0.0",
    "eslint-plugin-react": "^7.29.4",
    "husky": "^7.0.4",
    "less": "^4.1.2",
    "less-loader": "^10.2.0",
    "lint-staged": "^12.3.7",
    "postcss": "^8.4.12",
    "prettier": "^2.5.1"
  }
}

然后配置lerna.json

{
    "npmClient": "yarn",
    "useWorkspaces": true,
    "version": "independent"
}

rollup.config.js,给每个子文件夹拷贝一份,这里图省事,当然你可以在根目录只配置一个,通过在script里面传参去调用打包也可以。

import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from 'rollup-plugin-typescript2';
import { terser } from 'rollup-plugin-terser';
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
import postcss from 'rollup-plugin-postcss';
import dts from 'rollup-plugin-dts';
import image from '@rollup/plugin-image';
const isProd = process.env.NODE_ENV === 'production';
const packageJson = require('./package.json');

export default [
  {
    input: 'src/components/index.ts',
    output: [
      {
        file: packageJson.main,
        format: 'cjs',
        sourcemap: true
      },
      {
        file: packageJson.module,
        format: 'esm',
        sourcemap: true
      }
    ],
    plugins: [
      peerDepsExternal({ includeDependencies: !isProd }),
      resolve(),
      commonjs(),
      typescript({
        useTsconfigDeclarationDir: true
      }),
      postcss(),
      // terser(),
      image()
    ]
  },
  {
    input: 'dist/esm/types/index.d.ts',
    output: [{ file: 'dist/esm/index.d.ts', format: 'esm' }],
    external: [/\.css$/],
    plugins: [dts(), image()]
  }
];

ts的配置,也是每个子文件一份,当然只在根目录放一份,子目录使用extend的方式也行,但是路径上可能会出错。

{
    "compilerOptions": {
        "strict": true,
        "strictPropertyInitialization": false,
        "forceConsistentCasingInFileNames": true,
        "noFallthroughCasesInSwitch": true,
        "noImplicitAny": false,
        "outDir": "dist",
        "module": "esnext",
        "target": "es5",
        "lib": [
            "es2019",
            "DOM"
        ],
        "declaration": true,
        "declarationDir": "dist/esm/types",
        "emitDeclarationOnly": false,
        "jsx": "react",
        "allowJs": false,
        "checkJs": false,
        "moduleResolution": "node",
        "esModuleInterop": true,
        "allowSyntheticDefaultImports": true
    }
}
 .babelrc来一个,解析jsx用的
{
    "presets": [
      "@babel/preset-env",
      "@babel/preset-typescript",
      ["@babel/preset-react", { "runtime": "automatic" }]
    ]
}​

创建私有仓库

cmd去跑一下:

verdaccio

它会在http://localhost:4873开个服务,浏览器打开如下:

接着根据它说的:

npm adduser --registry http://localhost:4873/

然后输入用户名和密码,它就会记住你的身份

此服务不能停,一直开着。

安装包

跑如下命令,因为我们使用的是yarn的workspace的功能,所以不要用npm

yarn

此时它会把所有的包和依赖都给你建好,统一放在了根节点的node_modules里面,而子节点地下,只是放了一些bin里面的命令。

看,软链都给我们生成好了。

打包

跑下面命令

yarn build

它会使用lerna去扫描所有workspace,去寻找build命令,因为我们给每个子包都写了build的命令,所以会独立打包,打完包之后你会看到每个子包都有dist目录。

发版

yarn publish:local

因为我们没有配置scope,导致lerna把storybook也给我们发到仓库去了,现在刷新一下仓库看看:

四个包都在,先不管storybook那个包,我们随便下载一个其它包,解压看看是不是我们想要的效果。

跑storybook

yarn start

正常工作

而且你可以看到,storybook可以通过宝明直接找到我们workspace里面的package,不用写'../../package/...'这样的引用,大大方便了开发。

至此告一段落,后面会继续研究version和changelog的管理。。。更新待续

项目地址:

相关标签:
  • lerna
  • rollup
  • yarn
0人点赞

发表评论

当前游客模式,请登陆发言

所有评论(0)