跳转到主文档

开发工具库

本章将介绍如何使用 Modern.js,进行工具库项目的开发。

本章对应的代码仓库地址:独立项目场景Monorepo 场景

info

工具库项目是指开发类似 Lodash、JavaScript SDK 等可复用模块的项目。

通过本章你可以了解到:

  • 如何创建一个工具库项目。
  • 如何在工具库项目中进行测试。
  • 如何为工具库项目开启 Storybook 功能并使用它进行调试。
  • 如何发布工具库项目。
  • 如何将工具库项目迁移到 Monorepo 中。
在线预览

在 StackBlitz 中预览:

环境准备

开始之前,请参考【环境准备】部分,确保本地环境正常。

创建项目

使用 @modern-js/create 创建新项目,运行命令如下:

npx @modern-js/create library-project

library-project 为创建的项目名。

按照如下选择,生成项目:

? 请选择你想创建的工程类型 模块
? 请填写项目名称 library
? 请选择开发语言 TS
? 请选择包管理工具 pnpm
? 是否需要调整默认配置? 否

项目名称为 package.json 中的 name 字段值。

编写工具函数逻辑

修改 src/index.ts 文件,增加工具库函数(以将字符串字母都转为大写为例):

export const upperCase = (s: string) => s.toUpperCase();

测试

修改默认测试文件 tests/index.test.ts

import { upperCase } from '@/index';

describe('upperCase cases', () => {
test('upperCase', () => {
expect(upperCase('abc')).toBe('ABC');
});
});

执行 test 命令对工具函数进行测试,命令如下:

pnpm run test

IDE 支持

Modern.js 对 VS Code 等主流 IDE 提供了开箱即用的支持,具备 Lint 问题自动检测、自动修复,代码提交前的准入检查等功能特性,可以让代码开发更加高效和智能。详细介绍请参考【确认编程环境】。

启用 Storybook 调试

在项目根目录下,执行 pnpm run new,可以开启 Storybook 功能。

? 请选择你想要的操作 启用可选功能
? 启用可选功能 启用「Storybook」

启用成功后,会自动创建 stories/ 目录,修改 stories/index.stories.tsx 文件内容:

import { useState } from 'react';
import { upperCase } from '@/index';

const Component = () => {
const [value, setValue] = useState('');
return (
<div>
<input value={value} onChange={e => setValue(e.target.value)} />
<div>result: {upperCase(value)}</div>
</div>
);
};

export const YourStory = () => <Component />;

export default {
title: 'Your Stories',
};

执行 pnpm run dev 调试运行结果,如下图所示:

调试 storybook

产物模式

工具库项目支持配置不同的产物模式来满足不同的开发需求,产物模式通过 output.packageMode 配置进行。

该配置支持如下值:

  • universal-js
  • universal-js-lite
  • browser-js
  • browser-js-lite
  • node-js

具体每个值对应的产物结构详见 output.packageMode

对于当前场景,如果开发的工具库只支持 Node.js 环境,可以在项目 modern.config.js 中增加如下配置:

modern.config.js
export default defineConfig({
output: {
packageMode: 'node-js',
},
});

执行 pnpm run build 之后,查看 dist/ 目录产物,可以看到只生成 nodemodern 两种类型的产物。

发布

开发完成后,可以对工具库进行发布。

发布分以下四个步骤:

  1. 添加 changeset

    执行 pnpm run change,根据提示选择升级的版本,并填写变更信息。

填写变更信息

  1. 升级对应版本号,并生成 changelog

    执行 pnpm run bump,该命令会根据上述生成的 changeset 自动更新版本号和 CHANGELOG 信息,检查信息无误后提交。

  2. 发布

    执行 pnpm run release, 发布该工具包。

  3. 推送 tags

    发布完成之后执行 git push --follow-tags,推送当前发布对应生成的 git tag。

迁移到 Monorepo

在团队协作开发中,也会存在使用 Monorepo 进行项目开发的情况。接下来介绍如何将工具库项目迁移到 Monorepo 中。

创建 Monorepo

使用 @modern-js/create 创建 Monorepo 项目,运行命令如下:

npx @modern-js/create library-monorepo

library-monorepo 既是创建的 Monorepo 目录名称,又是项目的名称。

按照如下选择,生成项目:

? 请选择你想创建的工程类型 Monorepo
? 请选择包管理工具 pnpm

生成的项目目录结构如下:

.
├── .changeset
│ └── config.json
├── .editorconfig
├── .gitignore
├── .npmrc
├── .nvmrc
├── .pnpmfile.cjs
├── .vscode
│ ├── extensions.json
│ └── settings.json
├── README.md
├── apps
│ └── .gitkeep
├── features
│ └── .gitkeep
├── monorepo.code-workspace
├── package.json
├── packages
│ └── .gitkeep
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── tsconfig.json

迁移工具库项目

这里只需要将之前创建的工具库项目复制到 packages/ 目录下面,并删除 .change.vscode.git 目录及 .editorconfig.gitignore.nvmrc.npmrc 文件。

.
├── packages/
│ └── library-project
├── CHANGELOG.md
├── README.md
├── modern.config.js
├── package.json
├── src
│ ├── index.ts
│ └── modern-app-env.d.ts
├── stories
│ ├── index.stories.tsx
│ └── tsconfig.json
├── tests
│ ├── index.test.ts
│ ├── modern-app-env.d.ts
│ └── tsconfig.json
└── tsconfig.json
| ...

创建子项目

Modern.js 支持 Monorepo 工程方案的管理,我们可以在 Monorepo 项目下通过 new 命令创建不同类型的子项目。例如在刚刚创建的 Monorepo 项目根目录下执行 pnpm run new

然后分别选择创建应用模块(内部)项目。

? 请选择你想创建的工程类型 应用
? 请填写子项目名称 app
? 请填写子项目目录名称 app
? 请选择开发语言 TS
? 是否需要支持以下类型应用 不需要
? 是否需要调整默认配置? 否
? 请选择你想创建的工程类型 模块(内部)
? 请填写子项目名称 internal-lib
? 请填写子项目目录名称 internal-lib
? 请选择开发语言 TS
? 是否需要调整默认配置? 否

在应用项目中使用组件以及内部模块

接下来在 app 项目中通过以下方式把工具库项目加到依赖中:

cd ./apps/app
pnpm add library

此时可以观察到 app 项目的 package.json 内容更新如下:

{
"dependencies": {
"@modern-js/runtime": "^1",
++ "library": "workspace:^0.1.1",
"react": "^17.0.1",
"react-dom": "^17.0.1"
}
}

接下来导入内部模块 internal-lib,由于内部模块并不需要进行发布,因此通过如下方式添加到项目中:

内部模块是指不需要发布到 npm 上的 package,它们只提供源码给应用项目使用,应用项目会将它们打包到构建产物中。

cd ./apps/app
pnpm add internal-lib -D

此时可以观察到 app 项目的 package.json 内容更新如下:

{
"devDependencies": {
"@modern-js/app-tools": "^1",
"@modern-js/plugin-jarvis": "^1",
"@types/jest": "^27",
"@types/node": "^14",
"@types/react": "^17",
"@types/react-dom": "^17",
++ "internal-lib": "workspace:^0.1.0",
"typescript": "^4"
},
}

此时在 app 项目下的 src/App.tsx 文件引用 libraryupperCase 函数以及 internal-lib 模块,并使用它们:

App.tsx
import { Switch, Route } from '@modern-js/runtime/router';
import { upperCase } from 'library';
import sayHelloWorld from 'internal-lib';

import './App.css';

const App = () => (
<Switch>
<Route exact={true} path="/">
<div className="container-box">
<main>{/* //... */}</main>
<div>{upperCase('abc')}</div>
<div>{sayHelloWorld()}</div>
<footer className="footer">
<a href="#" target="_blank" rel="noopener noreferrer">
Powered by Modern.JS
</a>
</footer>
</div>
</Route>
<Route path="*">
<div>404</div>
</Route>
</Switch>
);

export default App;

然后我们在 apps/app 目录下执行 pnpm run dev 命令,可以看到页面渲染效果如下:

app-tools

批量发布

我们可以在 Monorepo 中再创建一个工具库项目(以将字符串字母都转为小写为例):

在 Monorepo 项目根目录下执行:

pnpm run new
? 请选择你想创建的工程类型 模块
? 请填写子项目名称 library2
? 请填写子项目目录名称 library2
? 请选择开发语言 TS
? 是否需要调整默认配置? 否

修改 packages/library2/src/index.ts 文件,增加工具库函数:

export const lowerCase = (s: string) => s.toLowerCase();

开发完成后可以在 Monorepo 的场景下对所有工具库做批量发布:

  1. 添加 changeset

在 Monorepo 根目录执行 pnpm run change,根据提示选择发布的包(注意这里只选择工具库包名)和升级的版本,并填写变更信息。

monorepo 添加 changeset

  1. 升级发布包对应版本号,并生成 changelog

执行 pnpm run bump,该命令会根据上述生成的 changeset 自动更新版本号和 CHANGELOG 信息,检查信息无误后提交。

  1. 发布

执行 pnpm run release, 发布对应的多个工具包。

  1. 推送 tags

发布完成之后执行 git push --follow-tags,推送当前发布对应生成的 git tag。