该小节主要知识点:
- npm 包项目搭建
- 认识类库开发时常用
package.json字段 - 正确使用
bin字段 - 什么是
npm link - 带大家一起发布第一个包
本小节所有代码已放入 mini-umi 仓库
/juejin/cli目录 下
这里我带大家一步一步来实现我们的第一个npm包
请对 npm包或 CLI开发 不熟悉的小伙伴 拿起鼠标键盘跟着我一起完成
1.项目搭建
安装 pnpm
pnpm 比 npm 更快更强大 放心用
npm i pnpm -g // 全局安装 pnpm
安装 TypeScript
优秀的类型提示可以给使用者极佳的体验
npm i typescript -d //我个人比较喜欢全局安装
创建项目
打开终端: 不熟悉终端操作的小伙伴可手动操作
mkdir first-pkg //创建文件夹
code first-pkg // 打开vscode并进入该项目
初始化项目
生成 package.json
pnpm init //初始化并自动生成package.json
+{
+ "name": "first-pkg",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo "Error: no test specified" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
+}
生成 tsconfig.json
tsc --init
创建 src/index.ts
现在项目的目录结构是这样:
加入以下代码:
// src/index.ts
export const yang = 'yang'
console.log(yang);
修改 tsconfig.json
先配置几个需要的
{
"compilerOptions": {
"target": "es2016", // 编译产物
"module": "commonjs",
"moduleResolution": "node", // 模块声明
"baseUrl": "./",
"declaration": true, // 生成 d.ts 文件
"sourceMap": true, // 开启 sourcemap 方便打断点调试源文件
"outDir": "dist", // 编译后输出文件夹
"declarationDir": "dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}
修改 package.json
{
"name": "first-pkg",
"version": "1.0.0",
"description": "",
+ "main": "/dist/index.js",
- "test": "echo "Error: no test specified" && exit 1"
"scripts": {
+ "dev": "tsc --watch", // --watch表示文件修改后自动重新编译
+ "build": "tsc --target es5"
},
"keywords": [],
"author": "",
"license": "ISC"
}
测试脚本是否可用
npm run dev
成功生成 dist目录 如下则说明成功~
到这里,一个项目雏形就搭建好啦~
2.认识类库开发时常用 package.json 字段
下面以 mini-umi 这个包中的 package.json 为例:
⭐️ name
发到 npm 的包名,也是其他用户下载时的名字
注意:包名与当前目录的文件夹名无关
⭐️ version
发布的版本号 版本号这里有一套详细的规则
一般是三位 X.Y.Z
X是大版本 一般只有大版本更新产生大量 breakchanges 之后才会上 比如Vue2-Vue3
我们在安装依赖的时候一般都是 --- '^X.Y.Z'
^表示:在安装的时候 如果没有 lock文件 都会自动安装当前 X大版本下的最新小版本
description
这个 npm 包的描述,可以在 npm 仓库里面展示
⭐️ main
别人在使用这个 npm 包时 通过直接 import '包名' 时默认指向这个文件
也就是别人使用我们这个包时 访问的入口文件
⭐️ types
d.ts 声明文件的入口
⭐️ ⭐️ bin
这个 npm包 的 bin 脚本
⭐️ ⭐️ 非常重要 接下来会带大家去使用 bin字段
⭐️ script
设置当前工作区的脚本
设置之后使用 npm run xx 会优先使用在当前工作区的 node_modules
⭐️ dependencies
项目的依赖 生产环境依然有效
⭐️ devDependencies
项目的本地依赖 只有开发环境生效 生产环境 build的时候不会生效
通常安装像 typescript、@types/xxx 这种只有开发阶段起作用的库
⭐️⭐️ files
在发布这个 npm包 的时候 上传的所有文件夹
通常情况下都是默认 dist
tips: 后续开发中不仅仅需要上传 dist
也就是说我们在发布这个 npm 包的时候 可能不仅仅只有 dist 目录以及 package.json 会被上传
repository
在 npm 仓库中可以展示链接的 Github 仓库
keywords
关键词 例如: [ "umi","umijs","mini-umi" ]
author
仓库的作者
license
开源协议:默认为"ISC"
最终填写效果展示如下:
{
"name": "mini-umi",
"version": "1.3.5",
"description": "a simple model for Umi and Vue3.2 + Vite",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"bin": {
"mumi": "bin/mini-umi.js"
},
"scripts": {
"dev": "father dev",
"build": "father build",
"publish:all": "pnpm publish --no-git-checks",
"build:deps": "father prebundle",
"prepublishOnly": "father doctor && npm run build"
},
"dependencies": {
"@mini-umi/preset-example": "workspace:*",
"@mini-umi/preset-umi": "workspace:*",
"@mini-umi/core": "workspace:*",
"@umijs/utils": "^4.0.3"
},
"files": [
"bin",
"dist"
],
"repository": "https://github.com/BoyYangzai/mini-umi",
"keywords": [
"umi",
"umijs",
"mini-umi"
],
"author": "洋",
"license": "ISC"
}
到这里需要单独把 bin 字段给提出来给大家演示一下
3.正确使用 bin 字段
接下来回到我们的 first-pkg 项目中
新建 bin 字段
+ "bin": {
+ "yang": "./bin/yang.js"
+ },
意思就是 当你 在终端输入 yang 的时候他会自动执行 './bin/yang.js' 文件里的代码
新建 bin目录 在该目录下新建 yang.js
将以下代码复制到文件中
bin/yang.js
+ #!/usr/bin/env node
+ console.log('我是洋');
tips:
#!/usr/bin/env node 表示我们会使用node环境执行以下代码 这是必须要有的
这样 当我们 在终端输入yang的时候他会自动执行 './bin/yang.js' 里的代码
在终端尝试输入
yang
很明显 失败了 command not found
不要慌
我们执行这个命令
npm link
在终端上成功输出了--’我是洋‘
可是为什么一定要有 npm link 才会成功呢?
4.什么是 npm link 呢?
简单说就是为当前开发的这个包创造一个 全局软链接
你可以使用 bin 命令 是因为当你在安装这个类库时
你的 node_modules 下会 自动创建 一个 .bin 目录
当你去执行 比方说 esbuild 这个命令的时候 他会去自动执行 .bin 目录下特定的代码
但是
我们 本地的 first-pkg 并 没有作为 node_modules 安装在我们的项目里
自然也就出现了 command not found
当你在 first-pkg 这个 路径下 使用 npm link的时候
first-pkg 这个包就会 被链接到全局
相当于 你 npm i first-pkg -g
而且你随时修改 first-pkg 里面的代码 全局的 first-pkg 都会随着更新
因为他 本质上是个链接 链接尽头还是我们这个包
npm link 是一个非常强大的功能
当我们在 B包里面想使用A包时必须 npm i A包,那我们本地就需要先把A包发布到npm仓库里,但是 我想开发B包的同时也修改A包里面的内容该怎么办呢?
我这里是一个Vue3项目=B包 引入了Element-plus=A包
但是我想在 Vue3 里使用的是我 本地克隆下来的 Element-plus
这样我就能在 Element-plus 这个项目中 修改代码并生效 在我的Vue3项目里啦 然后 修复某个组件的 Iusse 并提交 pr 美滋滋
(当然 大多数专业的类库都会在自己的仓库中使用 monorepo 配备自己的 example,上述只是举个例子)
像 Element-plus 仓库中的 play 这个包 其实就是我们这里的 Vue3(B包)
package下的包 就是我们的 Element-plus本体 也就是A包
只不过A包B包在同一个仓库里通过 monorepo 的方式管理 通过 workspace 去进行 软链接 而不是 npm link
说白了 monorepo 是一种 仓库管理模式
monorepo仓库可以通过 workspace 达到 究极加强版 npm link 而不是本地link类库开发的方式
5.发布我们的npm包
1.注册npm账号
在npm官网点击 Sign up,根据提示操作完成账号注册
2.开发我们的npm包
根据前面的步骤完成我们的 npm包 搭建与开发 ~
3.IDE绑定npm账号
打开终端,执行 npm login 登录,按照提示填写对应的内容
依次是用户名、密码、邮箱,可能还会有一次性邮箱验证码
4.在终端输入命令发布公有包,可以被全世界下载
npm publish --access public
6.现在我们一起发布一下first-pkg这个包
-
1.注册账号 ✅
-
2.开发npm包 ✅
-
3.绑定账号 ✅
就剩下第四步npm publish了
检查一下package.json
- 包名符合规范 ✅
tips:@开头的包必须加入对应的组织 比如@Vue/xxx 你就必须在Vue组织里才有发包权限
-
版本号ok ✅
-
入口文件 我们这里没有用到 ✅
-
bin脚本 ✅
-
作者信息等 ✅
这里有个问题----没有files字段,因为我们要执行 bin 文件夹里面的内容 所以需要加上 files 字段上传bin目录
//package.json
+ "files":[
+ "bin"
+ ]
发包!-执行 npm publish --access public
很好,失败了 看下日志 原因可能是这个名字早就被占用了 我们换个包名
重新执行 npm publish --access public
成功了!
我们验证一下是否 开发并发包成功
首先执行 以下命令 取消 我们这个包的 全局软链接 link
tips: 为什么要有 -g ,因为它是 npm link 是
软链接到全局
不信你执行npm ls -g看看是不是有它
npm unlink first-pkg -g
这样我们输入 yang 就 command not found 了
现在我们去新的终端执行以下命令 :
npx yang-first-pkg
什么是npx?
npx 会在当前目录下的./node_modules/.bin里去查找是否有可执行的命令,没有找到的话再从全局里查找是否有安装对应的模块,全局也没有的话就会自动下载对应的模块,并且用完立即删除,所以用来执行CLI再好不过了
(1.0.1 是因为第一次上传忘记上传 files 字段了哈哈哈 重新发了一次包)
目标完成 成功~
小结:
本小节我们
1.首先搭建了基本的项目 修改了 tsconfig.json 配置和 package.json 的 script ,完成了使用 tsc 编译 ts 代码、生成 js 产物文件的功能。
2.为大家介绍了在进行类库开发时常用的一些 package.json 字段 例如:bin、files等
3.详细展开为大家介绍了 bin 脚本,使用 bin 脚本编写了第一个 CLI ,并讲解了 CLI 不能顺利执行的原因
4.接下来我们 引入了强大的 npm link 功能,顺利执行我们的CLI 并 介绍了 npm link 是什么,以及举例 Element-plus仓库 的 play 与 package 两个包,说明 monorepo 中 workspace 与 npm link 的关系
5.带着大家学习了 npm包 的发布流程,成功发布了我们的第一个 npm包
下一小节将为大家介绍工程化中的 Bundle、Bundless、No-Bundle 与常见的生态工具~
小节思考:
你能回答的出来 执行 npm run xxx 之后发生了什么 吗?