npm发包 - zptime/blog GitHub Wiki
-
在 npm 官网https://www.npmjs.com/注册并创建 npm 账号
-
注册之后进入新首页https://www.npmjs.com/?track=newUserCreated,有如下提示:
- It looks like you still do not have two-factor authentication(2FA) enabled on your npm account. To enable 2FA, please follow the instructions fount here(看起来您的 npm 帐户仍未启用双因素身份验证 (2FA)。要启用 2FA,请按照此处的说明进行操作)。
- 点击官方英文文档去验证,按照文档操作就行
- 阮一峰 双因素认证(2FA)教程
- 5 款好用的 2FA 两步验证工具推荐
- 总结:认证工具需要科学上网,你懂的,稍微有些麻烦。千万要注意备份,多端通用,不然万一设备出问题了,找不到了,那就麻烦了。所以我暂时先没弄。
- You have not verified yout email address. (您尚未验证您的电子邮件地址)
- 点击这个提示,就会向邮箱发送验证邮件,在邮箱中操作即可
先拿一个 utils 工具文件尝试,放入一些常用的工具函数,如手机号校验、金额格式化等
- 新建一个文件夹(shang-utils)
- 进入文件夹,运行 npm init 命令 如下图所示,会有一些初始化的配置项,大部分只要按 enter 就行,最后会生成一个 package.json 文件,之后也可以随时改。
npm Docs 之 package.json
:https://docs.npmjs.com/cli/v8/configuring-npm/package-json
{
"name": "shang-utils", // 包名,必须要独一无二
"version": "1.0.0", // 版本号
"author": "xxx", // 作者
"description": "common toolkit", // 描述信息
"keywords": ["utils", "format", "money", "phone"], // 关键词,提升SEO
"repository": {
// 代码托管位置
"type": "git",
"url": "https://github.com/xxx/shang-utils"
},
"license": "ISC", // 许可证
"homepage": "https://your-package.org", // 包的主页或者文档首页
"bugs": "https://github.com/xxx/shang-utils/issues", // 用户问题反馈地址
"main": "index.js", // 入口文件
"scripts": {
// 存放可执行脚本
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
// 运行依赖
},
"devDependencies": {
// 开发依赖
}
}
- 完成工具函数文件
// a. index.js 入口文件
import Format from "./src/format";
import Validate from "./src/validate";
export default {
Format,
Validate,
};
// b. format.js 格式化文件
const Validate = {
/**
* 手机号校验
*/
mobileCheck: (value) => /^[1][3,4,5,7,8][0-9]{9}$/.test(value),
/**
* 身份证校验
*/
IDCardCheck: (value) =>
/^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(
value
),
/**
* 邮箱校验
*/
emailCheck: (value) =>
/^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/.test(value),
};
export default Validate;
// c. validate.js 校验文件
const Format = {
// 解决toFixed保留小数的问题
formatToFixed: (money, decimals = 2) => {
return (
Math.round(
(parseFloat(money) + Number.EPSILON) * Math.pow(10, decimals)
) / Math.pow(10, decimals)
).toFixed(decimals);
},
// 格式化金额展示: 12341234.246 -> $ 12,341,234.25
formatMoney: (money, symbol = "", decimals = 2) =>
formatToFixed(money, decimals)
.replace(/\B(?=(\d{3})+\b)/g, ",")
.replace(/^/, `${symbol}`),
};
export default Format;
-
Github 上创建新仓库
shang-utils
-
按照说明文档,上传项目,并初始化本地分支
git init
git add .
git commit -m "first commit"
git branch -M master
git remote add origin [email protected]:xxx/shang-utils.git
git push -u origin master
- 检查 npm 源,如果是淘宝源,则需要改回 npm 源
// 查看npm镜像源地址
npm config get registry
// 切换npm镜像源
// 设置npm默认源
npm config set registry https://registry.npmjs.org/
// 设置npm镜像源为淘宝镜像
npm config set registry https://registry.npm.taobao.org/
- 在终端中切换到项目目录下,运行登陆命令,之后按照终端提示输入用户名、密码等信息即可
// 登陆
npm login
// 输入相关信息
Log in on https://registry.npmjs.org/
Username: // 用户名
Password: // 密码
Email: (this IS public) // 邮箱
Enter one-time password: // 如果之前做过 双因素身份验证 (2FA),需要生成一次性密钥
Logged in as xxx on https://registry.npmjs.org/.
- 运行发布命令
// 发布命令
npm publish
我这个工具函数比较简单,一下就发布成功了,没有碰到其他问题。发布成功后,就可以登陆 npm 网站,查看发布包的情况了
在项目中安装依赖包
npm install shanglv-utils
安装成功后,可以在项目的 node_modules 中看见包文件
使用举例:
// 导出方式
export { Format, Validate };
// 使用方式
import { Format, Validate } from "shanglv-utils";
Format.formatMoney(12341234.246, "$", 2); // $12,341,234.25
Validate.mobileCheck("123456"); // false
如下所示:更新了一下说明文档,重新发布
// 自动更改版本号,并且commit
npm version xxx
// 控制台会返回下一个小版本号 如v1.0.1
npm version patch
// 重新发布
npm publish
// patch:补丁号,修复bug,小变动,如 v1.0.0->v1.0.1
npm version patch
// minor:次版本号,增加新功能,如 v1.0.0->v1.1.0
npm version minor
// major:主版本号,不兼容的修改,如 v1.0.0->v2.0.0
npm version major
之前是发布了工具函数,纯 JS 文件,现在尝试发布自定义组件
# 创建项目
$ pnpm create vite vite-npm-package -- --template vue
# 运行项目
$ cd vite-npm-package
$ pnpm install
$ pnpm dev
操作:新建 packages/button 目录,在该目录下创建 src/index.vue 文件和 index.js 文件
- packages/button/src/index.vue
<template>
<button class="tButton">测试 发布 按钮组件</button>
</template>
<script>
// name是必须要写的
export default {
name: "tButton",
};
</script>
<style>
.tButton {
background-color: #1188ff;
color: white;
border: none;
padding: 6px 12px;
}
</style>
- packages/button/index.js
import tButton from "./src/index.vue";
// 如果想通过CDN方式引入,需要编写install函数
// 注册组件
tButton.install = function (Vue) {
Vue.component(tButton.name, tButton);
};
export default tButton;
<template>
<div class="demo">
<t-button class="block" />
</div>
</template>
<script setup>
import tButton from "packages/button/src/index.vue";
</script>
<style scoped></style>
操作:在 vite.config.js 中进行打包配置,然后运行打包命令
打包配置,可见 Vite 官网中文文档之构建生产版本:https://cn.vitejs.dev/guide/build.html
- 修改 vite.config.js 配置
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";
const resolve = (dir) => path.join(__dirname, dir);
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
"@": resolve("src"),
packages: resolve("packages"),
},
},
build: {
lib: {
// 注意此处的路径要配置正确,
entry: resolve("packages/button/index.js"),
name: "TButton",
fileName: (format) => `tbutton.${format}.js`,
},
// 自定义构建配置,可直接调整底层Rollup选项;Rollup有一套预设
// https://rollupjs.org/guide/en/#big-list-of-options
rollupOptions: {
// 确保外部化处理那些你不想打包进库的依赖
external: ["vue"],
output: {
// 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
globals: {
vue: "Vue",
},
},
},
},
});
- 运行打包命令
pnpm build
,打包效果如下所示:
<template>
<div class="demo">
<t-button />
</div>
</template>
<script setup>
import tButton from "../../dist/tbutton.es.js";
</script>
<style></style>
操作:在 package.json 文件中进行 npm 发布的相关配置,然后进行 npm 发布操作
- 修改 package.json
{
"name": "vite-npm-package",
"version": "1.0.4",
"description": "使用vite发布自定义组件到npm",
"files": ["dist"],
"main": "./dist/tbutton.umd.js",
"module": "./dist/tbutton.es.js",
"exports": {
".": {
"import": "./dist/tbutton.es.js",
"require": "./dist/tbutton.umd.js"
},
"./dist/style.css": {
"import": "./dist/style.css",
"require": "./dist/style.css"
}
},
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"license": "MIT",
"private": false,
"author": "zptime",
"dependencies": {
"vue": "^3.2.23"
},
"devDependencies": {
"@vitejs/plugin-vue": "^1.10.2",
"vite": "^2.7.0"
}
}
- 按照之前的方式发布到 npm
注意:不要忘记每次更改发布都要更改版本号
// 更新版本号,小版本,如1.0.0 -> 1.0.1
npm version patch
// 重新发布 v1.0.4成功
npm publish
- 安装依赖
pnpm i vite-npm-package
- 使用
<template>
<div class="demo">
<tButton />
</div>
</template>
<script setup>
import tButton from "vite-npm-package";
import "vite-npm-package/dist/style.css";
</script>
报错信息:Internal server error: Failed to resolve entry for package "vite-npm-package". The package may have incorrect main/module/exports specified in its package.json: Failed to resolve entry for package "vite-npm-package". The package may have incorrect main/module/exports specified in its package.json.
报错解析:之前写的 tButton 组件中,没有设置 name 值,这个是必须要设置的
参考文档:
之前对 Ant Design TreeSelect 组件进行了二次封装,可查看文章Antd TreeSelect 树选择控件“二次封装”及封装原理,现在使用 vite 将该组件发布到 npm 上
整体打包发布流程和使用 vite 发布自定义组件到 npm基本一致,如下只列出差异
- 安装依赖
pnpm i ant-design-vue
pnpm i vite-plugin-components -D
- 按需引入配置
// 2.1 新增src/libs/antdv.js
import { TreeSelect } from "ant-design-vue";
const components = [TreeSelect];
export function setupAntd(app) {
components.forEach((component) => {
app.use(component);
});
}
// 2.2 main.js引入
import { setupAntd } from "./libs/antdv";
const app = createApp(App);
setupAntd(app);
app.mount("#app");
// 2.3 vite.config.js配置
import ViteComponents, { AntDesignVueResolver } from "vite-plugin-components";
export default defineConfig({
plugins: [
vue(),
ViteComponents({
customComponentResolvers: [AntDesignVueResolver()],
}),
],
});
- 新建 packages/treeSelect/src/index.vue
<template>
<a-tree-select
v-bind="$attrs"
placeholder="请选择"
style="width: 100%"
:tree-data="departSource"
:dropdownStyle="{ maxHeight: '400px', overflow: 'auto' }"
show-search
allow-clear
tree-default-expand-all
treeNodeFilterProp="title"
:getPopupContainer="(triggerNode) => triggerNode.parentNode"
@select="handleSelect"
>
</a-tree-select>
</template>
<script>
import { watchEffect } from "vue";
export default {
name: "tTreeSelect",
props: {
// 数据源
departSource: {
type: Array,
default: [],
},
},
setup(props, context) {
console.log("props:", props);
console.log("context.attrs:", context.attrs); // $attrs
console.log("context.slots:", context.slots); // $slots
console.log("context.emit:", context.emit); // $slots
console.log("context.expose:", context.expose); // vue3.2 新增
watchEffect(() => {
console.log(`value is: ` + context.attrs.value);
});
const handleSelect = (value, node, extra) => {
console.log(value, node, extra);
};
return { handleSelect };
},
};
</script>
<style scoped></style>
- 新建 packages/treeSelect/index.js 文件
import tTreeSelect from "./src/index.vue";
tTreeSelect.install = function (Vue) {
Vue.component(tTreeSelect.name, tTreeSelect);
};
export default tTreeSelect;
- 修改 vite.config.js 文件
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import ViteComponents, { AntDesignVueResolver } from "vite-plugin-components";
import path from "path";
const resolve = (dir) => path.join(__dirname, dir);
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
ViteComponents({
customComponentResolvers: [AntDesignVueResolver()],
}),
],
resolve: {
alias: {
"@": resolve("src"),
packages: resolve("packages"),
},
},
build: {
lib: {
// 2. 基于Ant Design Vue TreeSelect二次封装的组件的打包配置
entry: resolve("packages/treeSelect/index.js"),
name: "TTreeSelect",
fileName: (format) => `tTreeSelect.${format}.js`,
},
// 自定义构建配置,可直接调整底层Rollup选项;Rollup有一套预设
// https://rollupjs.org/guide/en/#big-list-of-options
rollupOptions: {
// 确保外部化处理那些你不想打包进库的依赖
external: ["vue"],
output: {
// 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
globals: {
vue: "Vue",
},
},
},
},
});
- 修改 package.json 文件
{
"name": "vite-npm-package",
"version": "2.0.0",
"description": "使用vite发布自定义组件到npm",
"files": ["dist"],
"main": "./dist/tTreeSelect.umd.js",
"module": "./dist/tTreeSelect.es.js",
"exports": {
".": {
"import": "./dist/tTreeSelect.es.js",
"require": "./dist/tTreeSelect.umd.js"
},
"./dist/style.css": {
"import": "./dist/style.css",
"require": "./dist/style.css"
}
},
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"license": "MIT",
"private": false,
"author": "zptime",
"dependencies": {
"ant-design-vue": "^3.0.0-alpha.14",
"vue": "^3.2.23"
},
"devDependencies": {
"@vitejs/plugin-vue": "^1.10.2",
"vite": "^2.7.0",
"vite-plugin-components": "^0.13.3"
}
}
<template>
<div class="demo">
<t-tree-select
:depart-source="departTree"
v-model:value="departId"
:fieldNames="fieldNames"
/>
</div>
</template>
<script setup>
// 1. 本地引入
import tTreeSelect from "packages/treeSelect/src/index.vue";
// 2. 打包后引入(pnpm build)
import tTreeSelect from "../../dist/tTreeSelect.es.js";
import "../../dist/style.css";
// 3. 发包到npm后使用 v2.0.0
import tTreeSelect from "vite-npm-package";
import "vite-npm-package/dist/style.css";
const fieldNames = {
children: "children",
label: "name",
key: "id",
value: "id",
};
const departTree = [
{
id: 1,
name: "根部门",
departNumber: 6,
children: [
{
id: 11,
pid: 1,
name: "一级部门1",
departNumber: 2,
children: [
{ id: 111, pid: 11, name: "二级部门1", departNumber: 0 },
{ id: 112, pid: 11, name: "二级部门2", departNumber: 0 },
],
},
{ id: 21, pid: 1, name: "一级部门2", departNumber: 0 },
{ id: 31, pid: 1, name: "一级部门3", departNumber: 0 },
{ id: 41, pid: 1, name: "一级部门4", departNumber: 0 },
{ id: 51, pid: 1, name: "一级部门5", departNumber: 0 },
{
id: 61,
pid: 1,
name: "一级部门6",
departNumber: 2,
children: [
{ id: 611, pid: 61, name: "二级部门3", departNumber: 0 },
{ id: 612, pid: 61, name: "二级部门4", departNumber: 0 },
],
},
],
},
];
const departId = 11;
</script>
<style scoped></style>
使用效果:
主要是之前的两个组件 tButton 和 tTreeSelect,一起发布
- 在 package 文件夹下新增 index.js 文件
import tButton from "./button";
import tTreeSelect from "./treeSelect";
const components = [tButton, tTreeSelect];
// 如果想通过CDN方式引入,需要编写install函数
// 注册组件
const install = function (Vue) {
components.forEach((component) => {
Vue.component(component.name, component);
});
};
if (typeof window !== "undefined" && window.Vue) {
install(window.Vue);
}
export default {
install,
tButton,
tTreeSelect,
};
- 修改 vite.config.js 配置
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import ViteComponents, { AntDesignVueResolver } from "vite-plugin-components";
import path from "path";
const resolve = (dir) => path.join(__dirname, dir);
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
ViteComponents({
customComponentResolvers: [AntDesignVueResolver()],
}),
],
resolve: {
alias: {
"@": resolve("src"),
packages: resolve("packages"),
},
},
build: {
lib: {
// 3. 发布自定义组件库,包含tButton 和 tTreeSelect两个组件
entry: resolve("packages/index.js"),
name: "tAntDesign",
fileName: (format) => `tAntDesign.${format}.js`,
},
rollupOptions: {
external: ["vue"],
output: {
globals: {
vue: "Vue",
},
},
},
},
});
- 修改 package.json 文件
{
"name": "vite-npm-package",
"version": "3.0.0",
"description": "使用vite发布自定义组件库到npm",
"files": ["dist"],
"main": "./dist/tAntDesign.umd.js",
"module": "./dist/tAntDesign.es.js",
"exports": {
".": {
"import": "./dist/tAntDesign.es.js",
"require": "./dist/tAntDesign.umd.js"
},
"./dist/style.css": {
"import": "./dist/style.css",
"require": "./dist/style.css"
}
},
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"license": "MIT",
"private": false,
"author": "zptime",
"dependencies": {
"ant-design-vue": "^3.0.0-alpha.14",
"vite-npm-package": "^2.0.0",
"vue": "^3.2.23"
},
"devDependencies": {
"@vitejs/plugin-vue": "^1.10.2",
"vite": "^2.7.0",
"vite-plugin-components": "^0.13.3"
}
}
- 打包发布
// 打包
pnpm build
// 发布
npm publish
- 使用测试
<template>
<div class="demo">
本地引入
<t-button class="block" />
<t-tree-select
:depart-source="departTree"
v-model:value="departId"
:fieldNames="fieldNames"
/>
</div>
</template>
<script setup>
import tAntDesign from "vite-npm-package";
import "vite-npm-package/dist/style.css";
const { tButton, tTreeSelect } = tAntDesign;
</script>