Skip to main content

2 posts tagged with "发包"

View All Tags

· 5 min read

一般公司内都会搭建自己的私有 npm 仓库(可以基于 nexus 搭建),主要用途:

  • 在内部服务器存储自研包,安全又方便
    • 组件库,比如基础组件/业务组件/权限组件/...
    • 工具库,比如 hooks/utils/...
  • 缓存第三方包,加速下载
  • 对 npm 包可以配置权限管理

nrm

nrm 用于管理 npm 源

npm i nrm -g
nrm --help

nrm 默认已内置一些常用 npm 源(taobao/cnpm/...),常用方法如下:

# 查看npm源列表和当前使用的npm源
nrm ls
# npm config get registry 查看当前使用的源
# 添加
nrm add <name><npm源>
# 切换
nrm use <name> # == npm config set registry <npm源>
# 删除
nrm del <name>
# 测试npm源的响应时间
nrm test <npm源>

登录

先切换到私有源,第一次登录需要先通过以下命令输入用户名、密码、邮箱

npm adduser

后续就可以直接通过以下命令登录

npm login
# 查看当前登录用户
npm whoami
# 退出
npm logout

创建包和发布

npm init 初始化 node 项目,package.json 如下:

{
"name": "my-pkg",
"version": "1.0.0",
"description": "",
"author": "",
"license": "MIT",
"main": "./bin/index.js",
"bin": {
"test-pkg": "./bin/index.js"
}
}

bin 指定包的可执行文件,编辑 ./bin/index.js

#!/usr/bin/env node
console.log("007");
module.exports = "007";

#!/usr/bin/env node 的作用是告诉系统使用 node 解释脚本

调试

开发完项目后先在本地调试,进入项目目录,执行 npm link 将包链接到全局环境,然后尝试输入 test-pkg,看输出是否正常

之后记得通过 npm unlink <包名> 解除链接

版本

按语义化版本控制规范 SemVer,版本格式为:major/minor/patch,版本号递增规则如下:

  • 大版本更新(包含 breaking change
  • 小版本更新(小的功能迭代)
  • 补丁更新(bugfix
  • 先行版本号及版本编译信息可以加到 major/minor/patch 的后面,作为延伸。如:1.0.0-alpha.0

发包前可以通过 npm version 控制版本信息

npm version prepatch # 可以指明预发版关键词 --preid=alpha # v1.1.0-alpha.0
# v1.0.1-0
npm version prerelease
# v1.0.1-1
npm version patch # 结束预发版
# v1.0.1

发布

  1. https://www.npmjs.com/signup 注册 npm 账户
  2. 在 npm 账户上启用 2FA https ://docs.npmjs.com/configuring-two-factor-authentication
  3. 在本地终端登录 npm login,后续流程如下
npm login
// 输入账号名、密码、注册所用的邮件地址、OTP秘钥,没问题就会登录成功
// Logged in as xxx on https://registry.npmjs.org/.
  1. 打包,注意 package.json 的格式如下:
{
// name 需要声明个人账号名,即 @lucascv/包名
"name": "@lucascv/box-resizer",
// 每次发布需要更新 version,否则会提示发布失败
"version": "1.0.6",
// cjs 包入口
"main": "lib/index.cjs.js",
// esm 包入口
"module": "lib/index.esm.js",
// 不能设置为 true
"private": false,
}

进入包目录,先打包

yarn build
npm publish --access=public

之后在本地安装新发布的包并测试

npm i -g my-pkg

输入命令 test-pkg 查看输出是否正常

# 删除包
npm unpublish <name@version>
# 删除整包
npm unpublish <name> --force

出现 401/403 报错时,需要检查登录状态和项目是否和已有包冲突;如果是公司内部仓库需要额外申请发包权限

一些实践

下面是我对第三方包的打包和发布的一些实践

· 8 min read

这个组件其实算是我参加正式工作以来真正意义上的第一个自研的组件,很早就开源到 github 了,虽然只收获了 3 个 star,但也还是蛮值得纪念的

不过当时是将一整个 react demo 项目上传,在 demo 项目里引用 BoxResizer 组件,不算是正规的三方库。所以最近就索性用 rollup 将其打包成一个稍微正规点的三方库便于引用

github 链接

写在前面

为啥用 rollup?其实主流的 js 库都会用它来打包,比如 antd、react、vue、balabala... 有以下几个优势:

  • 轻量、代码简洁
  • 默认支持 tree shaking
  • 插件丰富、社区活跃

其实对比 webpack,主要就在于代码简洁和打包产物体积小吧。不过现在也出了蛮多其他的构建工具了,比如最近号称 700 倍快于 webpack 的 turbopack,基于 go 的esbuild以及在其基础上的 vite、基于 rust 的 swc,感觉就是前端很卷很卷。不过大部分场景实际上都还不需要用到这么高效的工具,webpack、rollup 足矣...

那什么时候用 webpack?我理解在应用级项目中建议使用,可以带来比较好的开发体验,比如代理服务、热更新等等

项目初始化

yarn init
yarn add rollup eslint prettier -D
npx eslint --init

先加下代码规范配置 eslintprettier,参考 统一前端编码规范和相关工具

因为这是个 react 组件,使用技术栈是 react 和 typescript,所以需要先增加 ts 相关配置,其中 rollup-plugin-typescript2 是用于 rollup 编译 ts 代码的插件

yarn add typescript rollup-plugin-typescript2 -D
npx tsc --init

tsconfig.json 如下:

{
"compilerOptions": {
"target": "es2016",
"outDir": "lib",
"module": "ES2015",
"moduleResolution": "node",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"jsx": "react",
// 生成声明文件,ts项目导入需要
"declaration": true, /* 生成相关的 '.d.ts' 文件。 */
"declarationDir": "./lib", /* '.d.ts' 文件输出目录 */
},
"include": ["src"]
}

更多选项参考 ts 编译选项

增加 babel 插件支持 js 代码编译

yarn add rollup-plugin-babel @babel/core @babel/preset-env @babel/preset-react -D

增加 .babelrc

{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}

因为项目中使用了 less 和 css module 来编写 css 代码,需要借助 rollup-plugin-postcss 插件来编译

yarn add rollup-plugin-postcss postcss less -D

项目结构如下:

box-resizer
├─ src
│ ├─ components
│ │ └─ box-resizer
│ │ ├─ index.module.less
│ │ └─ index.tsx
│ ├─ index.ts
│ └─ react-app-env.d.ts
├─ README.md
├─ package.json
├─ rollup.config.js
├─ tsconfig.json
└─ yarn.lock

其他插件:

  • 显示包体积 rollup-plugin-filesize
  • 压缩代码 rollup-plugin-uglify

最终的 rollup.config.js 参考如下:

import babel from "rollup-plugin-babel";
import typescript from "rollup-plugin-typescript2";
import postcss from "rollup-plugin-postcss";
import filesize from "rollup-plugin-filesize";
import { uglify } from "rollup-plugin-uglify";

const isProd = process.env.NODE_ENV === "production";

export default {
input: "src/index.ts",
output: {
file: "./lib/index.esm.js",
format: "esm",
},
plugins: [typescript(), babel(), postcss(), filesize(), isProd && uglify()],
external: ["react"],
};

打包组件和测试

package.json 配置命令

// cjs包入口
"main": "lib/index.cjs.js",
// esm包入口
"module": "lib/index.esm.js",
"scripts": {
"start": "rollup -c -w",
"build": "rm -rf ./lib && NODE_ENV=production rollup -c --bundleConfigAsCjs",
}

执行打包命令

yarn build

注意打包后需要生成对应的 d.ts 文件,没有的话 check 下 tsconfig.json

本地测试

// 在 box-resizer 目录下执行,给包做个本地链接
yarn link
// 建一个demo项目用来验证组件,进入项目根目录
cd demo
yarn link @lucascv/box-resizer

之后就可以在项目中正常引入

// @ts-ignore
import { BoxResizer } from '@lucascv/box-resizer'

然后在页面验证下吧 ~

注意本地 yarn link 可能会存在不同 react 版本的问题(hook invalid...)react 官网也有提示 https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate-react

可以将 react 放在 package.json 的 peerDependencies 中,使其依赖宿主环境的 react 版本,这样就不会导致冲突了,如下

"peerDependencies": {
"react": ">=16.12.0",
"react-dom": ">=16.12.0"
}

这里也稍微提下三种依赖包:

  • dependencies 生产依赖。指应用运行时依赖的第三方包
  • devDependencies 开发依赖。指开发阶段依赖的第三方包,比如构建包期间需要的 rollup、typescript、babel、postcss 以及对应的插件、jest 等等
  • peerDependencies。提示宿主环境去安装满足 peerDependencies 所指定的包,然后在插件导入包的时候,永远都是引用宿主环境统一安装的包,避免重复安装,可以解决插件与所依赖包不一致的问题

打包 esm 和 cjs 包

当然作为组件其实没必要生成 cjs 包,况且现在很多第三方包也抛弃了 cjs,这里的目的是为了以后开发其他库做的提前练习。首先改下 rollup.config.js 和 package.json

// rollup.config.js
output: [
{
file: 'lib/index.cjs.js',
format: 'cjs'
},
{
file: 'lib/index.esm.js',
format: 'esm'
}
],

执行 yarn build,会看到 lib 中生成了两种格式的包

但注意此时通过 commonjs 方式引入的包,并没有对应的类型提示

单元测试

一般应该要通过测试用例,才能允许合入和发布。现在还没做,先过吧

组件怎么做单元测试?后续参考下开源组件的单元测试方法再补充吧

版本维护

在功能修改后提交一个 tag,便于后续追踪。按照 semver 语义化版本规则 来就行

git tag v1.0.0 -m "第一个上线版本"
// 或者可以用 npm version 标记版本
// npm version [major|minor|patch]

git push origin v1.0.0

发布

  • 创建 npm 账号,比如我是 lucascv(lucas 是我英文名,cv 也就是复制粘贴工程师)
  • 包名(package.json 的 name)以账号名开头,比如@lucascv/box-resizer,这样才能通过发布审核
// 注意切回官方源
nrm use npm
npm login
npm publish --access=public

更多 npm 发包内容可以参考我之前总结的文章 - npm 发包

如何自动化发布?在 develop merge 到 master 时自动推送到 npm 库?并更新版本,也就是自动打 tag

总结

头一次产出自己的组件包,后面也有计划开发自己的组件库,在这个过程持续增强个人的工程化能力。该文章会随着组件的优化持续更新,欢迎 comment 哈 ~