从0到1搭建react组件库-项目规范篇

632 阅读7分钟

前言

前端开发的过程中,组件库是必不可少的一环,但是在日常的使用中,往往更关注它的api使用,并没有真正的去思考其背后的实现思路以及设计思想。

为了加强自己对前端工程化的了解,从0到1去搭建一个react的组件库,并且在这里记录下整个过程。(如果文章中有错误内容,欢迎大家交流指正。)

相关文档链接

从0到1搭建react组件库-开发方案篇

从0到1搭建react组件库-文档篇

# React文档深扒:useState背后的真相!

项目说明

项目结构

本项目是基于pnpm包管理器的monorepo结构,通过pnpm的workspace特性来统一管理多个互相关联的项目和模块。

由于在项目中会包含多个模块,例如组件的实现,hooks的封装,以及文档站点搭建、测试的集成,因此选用monorepo结构(monorepo可以简单理解为在一个仓库下去维护多个包的内容)。

项目初始化

  1. 创建项目文件夹,这里将项目命名为mini-ui。

image.png

  1. cd到mini-ui项目目录中,然后通过 npm init -y 命令生成package.json文件

image.png

  1. 创建如下的目录结构。

image.png

  1. 在根目录下,创建一个pnpm-workspace.yaml文件,在文件中声明包的内容。其中*代表目录下的所有文件夹。声明之后,就可以在其它的子包中通过npm包的形式引用。

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

技术选型

项目规范

ESlint

eslint主要通常是在开发环境中检查代码书写是否符合规范,因此安装在devDependencies中即可。

  1. 下载安装。这里直接继承了js和ts的推荐配置,省去自定义配置规则。因此还下载了@eslint/js以及typescript-eslint。 配置好pacakge.json后,在根目录命令行中通过pnpm i的命令安装依赖。
//package.json
{
    ...,
    "devDependencies": {
        "eslint": "9.10.0",
        "@eslint/js": "9.10.0",
        "typescript-eslint": "8.6.0"
    }
}
  1. 配置文件。由于没有在package.json声明type为mudule,这里选用cjs文件声明配置。在根目录创建eslint.config.cjs文件,通过commonjs的规范导出配置规则。
// eslint.config.cjs
const js = require("@eslint/js")
const tsLint = require("typescript-eslint")

module.exports = tsLint.config({
    extends: [
        js.configs.recommended,
        ...tsLint.configs.recommended
    ],
    files: ['**/*.{js, ts, tsx}'],
    ignores: ['node_modules/**'],
    rules: {
        "no-console": "error"
    }
})
  1. 设置脚本。 在package.json中设置执行脚本。
// 追加package.json内容
{
...
"scripts": {
    "eslint": "eslint "**/*.{js,ts,tsx}" --fix"
},
}

  1. 测试。 在@mini-ui/packages/ui目录下,新建一个js文件,可以先行测试配置是否成功。
  • 创建index.js
const a = {}
  • 在命令行中执行pnpm eslint命令,也就是运行第三步的脚本,查看是否执行完成。
pnpm eslint
  • 出现下面的内容, 即说明检测出声明了a变量,但并未使用a变量,证明eslint配置成功。

image.png

Stylelint

stylelint是在开发环境中检测样式是否符合规范,这里我们使用less作为组件库的样式方案,因此还需要下载配置postcss-less、stylelint-less等,否则无法识别less语法。

  1. 下载安装。在package.json的devDependencies字段中添加下面代码块中的内容,然后在根目录命令行中执行pnpm i命令,更新依赖。
// packages.json
{
    ...
    "devDependencies": {
        "stylelint": "16.9.0",
        "stylelint-config-standard": "36.0.1",
        "postcss-less": "6.0.0",
        "stylelint-less": "3.0.1"
    }
}
  1. 配置文件。在根目录创建stylelint.config.cjs文件。
// stylelint.config.cjs
module.exports = {
    extends: [
        'stylelint-config-standard'
    ],
    customSyntax: "postcss-less",
    plugin: ['stylelint-less'],
    rules: {
        "at-rule-no-unknown": null,
        "selector-class-pattern": null
    }
}
  1. 设置脚本。在package.json的脚本命令中追加stylelint命令。
// package.json
{
    "scripts": {
        ...
        "stylelint": "stylelint "**/*.{css,less}""
    },
}
  1. 测试。
  • 创建index.less
// packages/ui/index.less
@prefix: mini-ui;

.@{prefix} {
    background-color: #fff;
    .aaa {

    }
}
  • 在命令行中执行pnpm stylelint命令,也就是运行第三步的脚本,查看是否执行完成。
pnpm stylelint
  • 出现下面的内容, 即说明检测出在样式选择器前需要空白行, 不期望有一个空白的规则。符合预期,证明stylelint配置成功。

image.png

Husky

husky提供了一系列的git-hooks, 可以在git流程中对不同的步骤进行拦截,在这里主要是通过配置pre-commit,在代码提交之前,运行stylelint和eslint,检测是否存在不合规的代码,在检测中如果抛出error,git流程就会终止。(要先关联远程仓库,这一部分不做示范。)

  1. 下载安装。在package.json的devDependencies字段中添加下面代码块中的内容,然后在根目录命令行中执行pnpm i命令,更新依赖。
// packages.json
{
    ...
    "devDependencies": {
        ...
        "husky": "9.1.6"
    }
}
  1. 配置文件, 并运行husky init命令。
  • 在package.json中汇总eslint和stylelint的命令,在scripts中新增一个命令lint,用来执行上面两个的lint规则。
// package.json
{
    "scripts": {
        ...
        "lint": "pnpm eslint && pnpm stylelint",
    },
}
  • husky的配置可以通过命令直接生成,在命令行中运行命令pnpm exec husky init, 后即可生成对应的.husky目录,在pre-commit中配置pnpm lint。
# .husky/pre-commit
pnpm lint
  1. 测试。
  • 命令行中依次执行 git add .git commit -m "test", 会出现出下图的报错,即证明husky配置成功,git commit失败。

image.png

cz-git & commitlint

commitlint主要是用来对commit message进行规范校验,即对用户的提交信息进行验证,如果提交信息没有按照规定的格式,就会报错,终止commit流程。大家可以根据灵活选择配置cz-git,笔者个人比较喜欢cz-git的emoji方案,因此进行配置。

  1. 下载安装。在package.json中的devDependencies字段中追加commitizencz-gitcommitlint@commitlint/config-conventional的包。然后运行pnpm i命令安装所有的包内容。
// package.json
{
    "devDependencies": {
        ...,
        "commitizen": "4.3.0",
        "cz-git": "1.9.4",
        "commitlint": "19.5.0",
        "@commitlint/config-conventional": "19.5.0"
    }
}
  1. 配置文件。
  • 在项目根目录下建立commitlint.config.cjs, 并配置如下内容
// commitlint.config.cjs
// .commitlintrc.js
/** @type {import('cz-git').UserConfig} */
module.exports = {
    extends: ["@commitlint/config-conventional"],
    rules: {
        // @see: https://commitlint.js.org/#/reference-rules
    },
    prompt: {
        alias: { fd: "docs: fix typos" },
        messages: {
            type: "Select the type of change that you're committing:",
            scope: "Denote the SCOPE of this change (optional):",
            customScope: "Denote the SCOPE of this change:",
            subject: "Write a SHORT, IMPERATIVE tense description of the change:\n",
            body: 'Provide a LONGER description of the change (optional). Use "|" to break new line:\n',
            breaking: 'List any BREAKING CHANGES (optional). Use "|" to break new line:\n',
            footerPrefixesSelect: "Select the ISSUES type of changeList by this change (optional):",
            customFooterPrefix: "Input ISSUES prefix:",
            footer: "List any ISSUES by this change. E.g.: #31, #34:\n",
            generatingByAI: 'Generating your AI commit subject...',
            generatedSelectByAI: 'Select suitable subject by AI generated:',
            confirmCommit: "Are you sure you want to proceed with the commit above?"
        },
        types: [
            { value: "feat", name: "feat:     ✨  A new feature", emoji: ":sparkles:" },
            { value: "fix", name: "fix:      🐛  A bug fix", emoji: ":bug:" },
            { value: "docs", name: "docs:     📝  Documentation only changes", emoji: ":memo:" },
            { value: "style", name: "style:    💄  Changes that do not affect the meaning of the code", emoji: ":lipstick:" },
            { value: "refactor", name: "refactor: ♻️   A code change that neither fixes a bug nor adds a feature", emoji: ":recycle:" },
            { value: "perf", name: "perf:     ⚡️  A code change that improves performance", emoji: ":zap:" },
            { value: "test", name: "test:     ✅  Adding missing tests or correcting existing tests", emoji: ":white_check_mark:" },
            { value: "build", name: "build:    📦️   Changes that affect the build system or external dependencies", emoji: ":package:" },
            { value: "ci", name: "ci:       🎡  Changes to our CI configuration files and scripts", emoji: ":ferris_wheel:" },
            { value: "chore", name: "chore:    🔨  Other changes that don't modify src or test files", emoji: ":hammer:" },
            { value: "revert", name: "revert:   ⏪️  Reverts a previous commit", emoji: ":rewind:" }
        ],
        useEmoji: true,
        emojiAlign: "center",
        useAI: false,
        aiNumber: 1,
        themeColorCode: "",
        scopes: [],
        allowCustomScopes: true,
        allowEmptyScopes: true,
        customScopesAlign: "bottom",
        customScopesAlias: "custom",
        emptyScopesAlias: "empty",
        upperCaseSubject: false,
        markBreakingChangeMode: false,
        allowBreakingChanges: ['feat', 'fix'],
        breaklineNumber: 100,
        breaklineChar: "|",
        skipQuestions: [],
        issuePrefixes: [{ value: "closed", name: "closed:   ISSUES has been processed" }],
        customIssuePrefixAlign: "top",
        emptyIssuePrefixAlias: "skip",
        customIssuePrefixAlias: "custom",
        allowCustomIssuePrefix: true,
        allowEmptyIssuePrefix: true,
        confirmColorize: true,
        scopeOverrides: undefined,
        defaultBody: "",
        defaultIssues: "",
        defaultScope: "",
        defaultSubject: ""
    }
};
  • 配置commitlint, 结合husky, 在commit-msg文件下配置执行npx commitlint --edit $1
  1. 配置命令。
//package.json
{
    "scripts": {
        ...,
        "commit": "git-cz"
    },
    "config": {
        "commitizen": {
            "path": "node_modules/cz-git"
        }
    }
}
  1. 测试运行结果。
  • 执行pnpm commit命令, 就会出现emoji提示内容。

image.png

  • 如果git commit的命令不遵从规范的话,会被中止。(需要配置rule,这里继承了推荐的配置。)

image.png

lint-staged

lint-staged主要是为了在对文件是否符合规范进行校验时,仅对git暂存区的内容进行校验。

  1. 下载安装。在package.json中的devDependencies字段中追加lint-staged的包。然后运行pnpm i命令安装所有的包内容。
// package.json
{
    ...,
    "devDependencies": {
        ...,
        "lint-staged": "15.2.10"
    }
}
  1. 配置命令。在package.json中配置lint-staged的校验规则,针对不同的后缀名走不同的校验。
// package.json
{
    "lint-staged": {
        "*.{js,ts,tsx}": [
            "eslint --fix"
        ],
    "*.{css,less}": [
        "stylelint --fix"
    ]
},
}
  1. 测试运行结果。
  • 在packages/ui/index.less文件中,增加不符合stylelint的规则,但是不将该文件存入暂存区,发现不影响commit时的校验。

image.png

项目地址

未完待续...

后续补充进开发方案以及打包方案的内容。