-
因为很多基建项目,在一开始就搭建完成了,目前又有新的业务诉求,之前的工具包,目前已经不能满足业务,现在重新写一个工具库来进行维护新的业务。重新搭建,近几年也伴随这业务还有技术圈的升级,有很多技术已经革新,让我们一起试着用现有的市场上的新方式,搭建一个NPM 工具库吧。
前言
在日常的工作中,我们难免会用到一些工具函数,有些工具函数会经常在不同的项目中用到;此时要么直接在需要的项目中将所用到的工具函数复制过去(前提是你有沉淀自己的工具函数的习惯),要么就是将工具函数以第三方库依赖的形式引用进来。
显然,以第三方库的形式引入能更加节省我们的时间,也更符合程序员DRY(Do not Repeat Yourself)的良好习惯。
所以,本篇文章就简单讲一下如何封装一个工具函数库并发布到npm上。
代码准备
每个人要发布的npm包类型都不尽相同,有UI组件库,有工具函数库,还有使用的插件等。这里要发布的npm包是在项目中常用的工具函数组成的工具函数库,构建工具使用的是rollup,代码托管在github上。
下面简述一下一些关键点:
新建仓库
首先在github上新建仓库,新建仓库时License 选择MIT, 此步骤不选择也无妨,后续添加license也可以。但是一定要有License才能发布npm包。
拉取代码
初始化git仓库之后将代码拉取到本地
git clone xxxxx
1
初始化项目
执行npm init -y生成package.json文件
安装依赖
由于我们此次主要是打包一个js库,所以我们选择更适合此场景的rollup作为打包工具。关于webpack和rollup的比较可以看下我的另一篇博客webpack VS rollup。
安装rollup
pnpm add rollup
1
安装必要的rollup插件
pnpm add @rollup/plugin-commonjs @rollup/plugin-node-resolve @rollup/plugin-typescript
1
插件作用说明:
@rollup/plugin-commonjs:rollup本身是不支持CommonJS的,使用了这个插件,就可以解析CommonJS模块了
@rollup/plugin-node-resolve:rollup 无法识别 node_modules 中的包,帮助 rollup 查找外部模块,然后导入
@rollup/plugin-typescript:rollup中使用typescript必备的插件
在安装@rollup/plugin-typescript插件时提示我们需要tslib和typescript,所以我们安装这两个第三方库
pnpm add tslib typescript
1
如果需要进行代码压缩和清除注释等需要安装如下插件
pnpm add rollup-plugin-terser rollup-plugin-cleanup
1
rollup-plugin-terser插件用于代码压缩
rollup-plugin-cleanup插件用于去除无效代码
配置typescript
在终端中执行npx tsc --init命令,生成tsconfig.json文件
修改tsconfig.json文件如下:
{
"compilerOptions": {
"baseUrl": ".",
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"downlevelIteration": true,
// 是否自动创建类型声明文件
"declaration": true,
// 类型声明文件的输出目录
"declarationDir": "dist/types",
// 只生成声明文件,而不会生成js文件
"emitDeclarationOnly": true,
// 指定输出文件目录(用于输出),用于控制输出目录结构
"rootDir": "src",
"paths": {
"@/*": ["src/*"],
"@types/*": ["src/types/*"],
"@utils/*": ["src/utils/*"]
}
},
"include": ["src"]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
如果要为工具库生成类型声明文件则必须有如下配置
{
// 是否自动创建类型声明文件
"declaration": true,
// 类型声明文件的输出目录
"declarationDir": "dist/types",
// 只生成声明文件,而不会生成js文件
"emitDeclarationOnly": true,
// 指定输出文件目录(用于输出),用于控制输出目录结构
"rootDir": "src",
}
1
2
3
4
5
6
7
8
9
10
为什么要为工具库生成类型声明文件呢?
主要是为了避免引入 JS 库的时候,遇到这样的报错:无法找到模块“@superying/remote-ui”的声明文件,让工具库有更好的typescript类型支持,且生成类型声明文件之后可以在引入工具库之后很方便的校验和看到工具方法的出入参。
配置rollup
在项目根目录下新建文件rollup.config.js,关于rollup的详细配置可参考官网教程。对于我们的工具库而言,rollup可简单配置如下:
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import { terser } from 'rollup-plugin-terser';
import cleanup from 'rollup-plugin-cleanup';
export default [
{
input: './src/index.ts',
output: {
dir: 'dist',
format: 'cjs',
entryFileNames: '[name].cjs.js',
},
plugins: [resolve(), commonjs(), typescript(), terser(), cleanup()],
}, {
input: './src/index.ts',
output: {
dir: 'dist',
format: 'esm',
entryFileNames: '[name].esm.js',
},
plugins: [resolve(), commonjs(), typescript(), terser(), cleanup()],
}
];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
编写工具函数
在根目录下新建src目录,在src目录下新建index.ts编写我们的工具函数;这里我们先写一个简单的工具函数
// 返回传入的日期是今年的第几天,如果不传参数则默认是当前日期
export const dayOfYear = (date?: Date | string): number => {
let formatDate = null;
if (!date) {
formatDate = new Date();
} else {
formatDate = typeof date === "string" ? new Date(date) : date;
}
// 如果传入的是无效的字符串,那么就默认是当前日期
if (!formatDate.getFullYear) {
formatDate = new Date();
}
const year = formatDate.getFullYear();
const firstDayOfYear = new Date(year, 0, 0);
const timeGap = formatDate.getTime() - firstDayOfYear.getTime();
return Math.floor(timeGap / 1000 / 60 / 60 / 24);
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
完善package.json配置
{
"name": "wangcc-utils",
"version": "0.1.0",
"description": "wangchaochuan's util function library",
"main": "./dist/index.cjs.js",
"module": "./dist/index.esm.js",
"types": "./dist/types/index.d.ts",
"files": [
"dist"
],
"scripts": {
"dev": "rollup -w -c",
"build:types": "tsc -b ./tsconfig.json",
"build": "npm run build:types && rollup -c",
"prepublish": "pnpm version && pnpm build"
},
"repository": {
"type": "git",
"url": "git+https://github.com/wangchaochuan/wangcc-utils.git"
},
"keywords": [
"utils"
],
"author": "wangchaochuan",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"bugs": {
"url": "https://github.com/wangchaochuan/wangcc-utils/issues"
},
"homepage": "https://github.com/wangchaochuan/wangcc-utils#readme",
"dependencies": {
"@rollup/plugin-commonjs": "^22.0.2",
"@rollup/plugin-node-resolve": "^14.1.0",
"@rollup/plugin-typescript": "^8.5.0",
"rollup": "^2.79.1",
"rollup-plugin-cleanup": "^3.2.1",
"rollup-plugin-terser": "^7.0.2",
"tslib": "^2.4.0",
"typescript": "^4.8.4"
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
说明:
"types": "./dist/types/index.d.ts"的作用:
仅仅是生成了类型声明文件还不行,外部在引用该工具库的时候还是无法找到模块的声明文件。若要彻底解决这个问题,可以在 package.json 中配置 types 属性,将其指向生成的类型声明文件。
如果发布公有包需要在package.json中增加publishConfig的配置
"publishConfig": {
"access": "public"
},
1
2
3
打包代码
在终端中执行pnpm build打包文件后会发现在根目录下生成了dist文件夹,在文件夹下会生成如下文件
此时我们的工具库已经初步形成,接下来就是不断地沉淀我们的工具函数即可。
账号准备
发布npm包需要我们先有一个npm账号,如果没有npm账号的话需要先注册一个。
注册网址:www.npmjs.com,根据提示进行账号注册即可
npm包发布
登录npm账号
执行npm login命令即可根据提示登录npm账号;
如果顺利的话根据提示依次输入用户名、密码、邮箱和验证码之后即可顺利登录;
如果不顺利的话可能会遇到如下问题Error: 500 Internal Server Error - PUT https://registry.npm.taobao.org/-/user/org.couchdb.user:linshen,这个错误时因为没有设置npm源导致的,我们在终端执行如下命令即可,npm config set registry https://registry.npmjs.org/ ;然后重新执行npm login即可正常登录。
登录成功之后会有如下提示Logged in as xxx on https://registry.npmjs.org/.
发布npm包
首次发布
成功登录npm账号之后执行如下命令即可发布npm包
npm publish
1
发布成功之后会有如下提示:
同时会向你的邮箱发送一封发布成功的邮件,然后我们就可以在npm上搜索到自己发布的npm包,就可以正常当做第三方依赖添加进项目使用了。
更新npm包
更新npm包两步走:
第一步:执行npm version <版本号类型>
第二步:执行 npm publish
执行第一步的时候有可能会报错,如下图:
这个报错是因为我们的代码还没有提交,提交代码之后就可以正常执行了。
重新npm publish之后会提示我们新的版本已经发布成功了。
删除npm包
npm unpublish <包名> -force
1
**注意:**删除后需要等24h后才能重新发布同名的npm包
使用npm包
安装依赖
在另外一个项目中执行如下命令
npm install wangcc-utils --save
1
引入依赖
在需要用到的地方引入依赖,此处我们仅作为示例,故在home.tsx中引入
import { FC } from 'react';
import { Button } from 'antd';
import styles from './index.module.scss';
import { useNavigate } from 'react-router-dom';
// 引入依赖
import { dayOfYear } from 'wangcc-utils';
const Home: FC = () => {
const navigate = useNavigate();
const jump = () => {
navigate('/product');
};
// 使用工具函数
console.log(dayOfYear());
return (
<div className={styles.home}>
<div className={styles.header}>react app home</div>
<Button size="large" onClick={jump}>
跳转
</Button>
</div>
);
};
export default Home;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
我们启动这个项目,发现在控制台中正确的打印了今天是今年的第几天,说明这个工具函数被正确引用和使用了。
同时,当我们鼠标移到dayOfYear的时候,开发工具给我们提示了dayOfYear这个工具函数的出入参,这说明我们生成的类型声明文件被正确的引用了。
以上就是封装自己的工具函数库并发布npm包的简略流程,均为笔者亲测。如果有什么遗漏或者错误的地方欢迎指正。
详细代码可参考笔者github。