这里分别介绍两种方法创建自己的 npm package,分别是最流行的两个框架 vite 和 react。

用 vite 创建 npm 库

首先,确保你已经安装了 vite , vite 5+ 的版本直接用以下命令就可以创建一个项目:

1
npm init vite VueButtonYy

运行后会在命令行里选择一个模板,选择 vue 即可。

创建一个 vue 的组件:

创建文件夹

项目创建完成后,我们将原来的 src 文件夹删除,然后新建一个 src 文件夹,然后在 src 文件夹下新建一个 index.js 文件和一个 components 文件夹,文件夹目录结构如下:

1
2
3
4
/src
|__index.js
|__/components
|__VueButtonYy.vue

创建组件

其中 VueButtonYy.vue 文件就是一个简单的 vue 组件。我这里随便写了一个 button 组件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<button class="vue-button-yy">
<slot />
</button>
</template>

<style scoped>
.vue-button-yy {
background: #43b883;
color: white;
outline: none;
border: none;
}
</style>

编写 vite 配置文件

vite 的功能强大,可以通过配置文件 vite.config.js 来实现一些功能,比如打包成一个 npm package。这里我用 vite.config.js,配置文件 vite.config.js 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { resolve } from "path";

export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, "src/index.js"),
name: "VueButtonYy",
// 输出文件的格式名:rollup是用 umd和es进行区分的:
fileName: (format) => `vue-button-yy.${format}.js`,
},
rollupOptions: {
external: ["vue"],
globals: {
vue: "Vue",
},
},
},
plugins: [vue()],
});

这里需要注意

  • fileName 参数,用来指定输出文件的格式名,这里我用的是 vue-button-yy.${format}.js,这样打包出来的文件名就是 vue-button-yy.cjs.jsvue-button-yy.esm.js
  • vite 是用 rollup 打包的,所以需要配置 rollupOptions,配置 rollupOptions 的 external 参数,将 vue 的包名 vue 添加到 external 中,这样打包的时候,vue 的包不会打包进去,出来的文件会更小。

编写入口文件 index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
import VueButtonYy from "./components/VueButtonYy.vue";

// 具名导出:
export { VueButtonYy };

// 默认导出:
export default {
install: (app) => {
app.component("VueButtonYy", VueButtonYy);
// 如有多个插件:
// app.component('Xxxx', xxxx);
},
};

编写 package.json 文件

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
{
"name": "vue-button-yy",
"version": "1.0.0",
"files": ["dist"],
"main": "./dist/vue-button-yy.umd.js",
"module": "./dist/vue-button-yy.es.js",
"exports": {
".": {
"import": "./dist/vue-button-yy.es.js",
"require": "./dist/vue-button-yy.umd.js"
},
"./dist/style.css": "./dist/style.css"
},
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.4.15"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.3",
"vite": "^5.1.0"
}
}
  • name: 这里我用的是 vue-button-yy,这个就是你创建的 npm 包的名称,这个名称在 npm 上不能重复,所以要自己取一个。
  • version: 这里我用的是 1.0.0,这个就是你创建的 npm 包的版本号,每次发布一个版本,都需要在这里修改版本号,然后在命令行里输入 npm publish,发布一个新版本。
  • files: 这里我用的是 dist,这个就是打包后的文件夹名称,打包后的文件会放在 dist 文件夹下。
  • main: 这里我用的是 ./dist/vue-button-yy.umd.js,这个就是打包后的入口文件,打包后的入口文件会放在 dist 文件夹下。vite 用的是 rollup.js 打包,rollup 默认的格式为 umd (require 导入) 和 es (import 导入) 两种文件名格式,写在 packge.json 里做一个说明,用的时候会根据 main 和 module 的文件名进行自动选择。

本地测试

在一个包编写完成后,我们需要在本地进行测试,在包文件夹的根目录下跑命令

1
npm link

生成一个本地全局软链接,再去另外一个 vue 的现成项目里,通过

1
npm link vue-button-yy

进行引用即可。

比如我在另一个项目里跑了 npm link vue-button-yy,可以看到该项目的 package.json 文件里添加了 vue-button-yy 的依赖如下,这是一种本地引用的方式

1
2
3
"dependencies": {
"vue-button-yy": "file:../vueDemoComp"
},

然后在 main.js 里引用了 vue-button-yy 的组件,代码如下:

1
2
3
4
5
6
7
import VueButtonYy from "vue-button-yy";
import "vue-button-yy/dist/style.css";

const app = createApp(App);

app.use(VueButtonYy);
app.mount("#app");

App.vue 里引用了 vue-button-yy 的组件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
<script setup>
</script>

<template>
<vue-button-yy>
hello world
</vue-button-yy>
</template>

<style scoped>
</style>

发布你的 npm 包

1
2
npm login
npm publish --access=public

创建一个 react 的 npm package

上面的步骤,已经用了一次 vite 作为打包工具,这里我直接用 rollup 作为打包工具,即 vite的底层打包工具,创建一个 react 的 npm package,步骤如下:

创建项目文件夹安装依赖

1
2
3
4
5
6
7
8
9
10
11
12
mkdir yuwing-react-ui && cd yuwing-react-ui
npm init -y

# 安装 react 及 ts:
npm i -D @types/react typescript react

# 生成tsconfig.json 文件,配置ts检查规则
npx tsc --init

# rollup 所需的依赖:
npm i -D rollup @rollup/plugin-node-resolve @rollup/plugin-typescript @rollup/plugin-commonjs rollup-plugin-dts

tsconfig.json 文件,配置 ts 检查规则,代码如下:

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
{
"compilerOptions": {
/* Language and Environment */
"target": "ESNext",
"jsx": "react-jsx",

/* Modules */
"module": "ESNext" /* Specify what module code is generated. */,
"moduleResolution": "Node" /* Specify how TypeScript looks up a file from a given module specifier. */,

/* JavaScript Support */
"allowJs": false /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */,
"maxNodeModuleJsDepth": 1 /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */,

/* Emit */
"declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. 将所有写在源码的types文件生成到types文件夹下 */,
"emitDeclarationOnly": true /* Only output d.ts files and not JavaScript files. */,
"sourceMap": true /* Create source map files for emitted JavaScript files. */,
"outDir": "dist" /* Specify an output folder for all emitted files. */,
"declarationDir": "types" /* Specify the output directory for generated declaration files. */,

/* Interop Constraints */
"allowSyntheticDefaultImports": true /* Allow 'import x from y' when a module doesn't have a default export. */,
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,

/* Type Checking */
"strict": true /* Enable all strict type-checking options. */,
"noUnusedLocals": true /* Enable error reporting when local variables aren't read. */,
"noUnusedParameters": true /* Raise an error when a function parameter isn't read. */,
"noImplicitReturns": true /* Enable error reporting for codepaths that do not explicitly return in a function. */,
"noFallthroughCasesInSwitch": true /* Enable error reporting for fallthrough cases in switch statements. */,
"noUncheckedIndexedAccess": true /* Add 'undefined' to a type when accessed using an index. */,
"allowUnreachableCode": true /* Disable error reporting for unreachable code. */,

/* Completeness */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}

创建 rollup.config.mjs 文件,配置 rollup 打包规则,代码如下:

1
2
# 这里用 mjs 扩展名,因为我们文件里用到 import 语句,不再用require()
touch rollup.config.mjs
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
import commonjs from "@rollup/plugin-commonjs";
import resolve from "@rollup/plugin-node-resolve";
import typescript from "@rollup/plugin-typescript";
import dts from "rollup-plugin-dts";
import packageJson from "./package.json" assert { type: "json" };

export default [
{
input: "src/index.ts",
output: [
{
file: packageJson.main,
format: "cjs",
sourcemap: true,
},
{
file: packageJson.module,
format: "esm",
sourcemap: true,
},
],
external: ["@types/node"],
plugins: [
resolve(),
commonjs(),
typescript({
tsconfig: "./tsconfig.json",
}),
],
},
{
input: "dist/esm/types/index.d.ts",
output: [
{
file: "dist/index.d.ts",
format: "esm",
},
],
plugins: [dts()],
},
];

package.json 文件,配置打包规则,代码如下:

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
{
"name": "@yuwing/react-ui",
"version": "0.0.1",
"description": "simple react compoent library",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"type": "module",
"types": "dist/index.d.ts",
"files": ["dist"],
"scripts": {
"dev": "rollup -c --watch",
"build": "rollup -c"
},
"keywords": ["react", "react-ui", "react-component"],
"author": "Yu Yi (Kyle Yu)",
"license": "MIT",
"devDependencies": {
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-typescript": "^11.1.6",
"@types/react": "^18.2.58",
"react": "^18.2.0",
"rollup": "^4.12.0",
"rollup-plugin-dts": "^6.1.0",
"typescript": "^5.3.3"
}
}

发布自己的包

这一步和上面的 vue 发布一样,不过多说,这里就不写了。

值得注意,package.json 文件中可以加入 "repository" 字段,用来配置发布包的配置,可以在上传到 GitHub 后,直接发布到 npm 仓库:

1
2
3
{
"repository": "https://github.com/ys558/yuwing-react-ui.git"
}

我们可以在 GitHub 上配置,然后将 package 的压缩文件发布在 GitHub 上,必须先去 GitHub 上创建一个 token
点击右上角的头像,选择 Settings -> Developer Settings -> Personal access tokens -> Generate new token (classic) -> Select scopes -> repo -> Generate token -> Copy the token -> 把生成的 token 粘贴到你的.npmrc 文件里,默认文件路径是 ~/.npmrc,并添加以下字段:

1
2
3
registry=https://registry.npmjs.org/
@YOUR_GITHUB_USERNAME:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=<YOUR_GITHUB_TOKEN>

至此,我们 react 组件库的基本流程已完成。