React之使用vite创建组件库并发布到npm
在工作过程中,经常是好几个项目都需要用到同一个或几个功能,为了偷懒,所以想着研究下能不能将这几个功能封装成组件库,然后放到 npm 上,每次需要使用时,就安装一下就可以使用了。
说干就干,这里的搭建环境是 vite+react+ts
# 项目创建及基础配置
1、初始化项目,如创建 my-react-library 文件夹,并进入,然后执行 npm init 初始化项目
2、安装 Vite、React、TypeScript 以及相关依赖
npm install react react-dom
npm install --save-dev typescript vite @vitejs/plugin-react vite-plugin-dts
2
3、设置 TypeScript
在项目根目录下创建一个 tsconfig.json 文件,配置如下:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "dist",
"moduleResolution": "node"
},
"include": ["packages"],
"exclude": ["node_modules", "dist"]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ps:
1、include:表示你需要将项目中的 packages 目录下的内容进行打包
2、outDir:表示打包后的文件夹的名称
4、在项目根目录下创建 packages 文件夹,用来存放各种组件
以 Button 组件为例,在 packages 文件夹下创建 Button 文件夹,在 Button 文件夹下创建 Button.tsx、index.ts、index.css 文件
// /packages/Button/Button.tsx
import React from "react";
import "./index.css"
export interface ButtonProps {
text: string;
onClick: () => void;
}
const MyButton: React.FC<ButtonProps> = ({ text, onClick } = {
text: 'Button',
onClick: () => {
console.log('click');
}
}) => {
return (
<div>
<button className="my-button" onClick={onClick}>{ text }</button>
</div>
);
};
export default MyButton;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/packages/Button/index.css
.my-button {
width: 100px;
height: 20px;
background-color: #000;
color: #fff;
border-radius: 5px;
cursor: pointer;
}
2
3
4
5
6
7
8
// /packages/Button/index.ts
import Button from './Button';
export type { ButtonProps } from './Button';
export default Button;
import './index.css';
2
3
4
5
5、添加打包配置
在项目根目录下创建 vite.config.ts 文件,配置如下:
import { defineConfig } from 'vite'
import { resolve } from 'path'
import react from '@vitejs/plugin-react'
import dts from 'vite-plugin-dts'; // 用于输出 .d.ts类型文件
export default defineConfig({
plugins: [
react(),
dts({
insertTypesEntry: true,
}),
],
build: {
lib: {
// 组件库源码的入口文件
entry: resolve(__dirname, 'packages/index.ts'),
name: 'my-react-component',
// 文件名称
fileName: (format) => `my-lib.${format}.js`,
},
rollupOptions: {
// 处理那些你不想打包进库的依赖
external: ['react', 'react-dom'],
output: {
// 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
globals: {
react: 'React',
'react-dom': 'ReactDOM',
},
},
},
}
})
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
6、创建组件库的入口文件
在 packages 目录下创建 index.ts 文件,将所有组件及 ts 类型导出
export { default as Button } from './Button';
export type { ButtonProps } from './Button';
2
7、更新 package.json 文件,添加如下配置
"type": "module",
"files": [
"dist"
],
"main": "./dist/my-lib.umd.js",
"module": "./dist/my-lib.es.js",
"types": "./dist/index.d.ts",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# package.json 文件配置说明
1、files:包含在发布包中的文件,这里指定了 dist 目录,确保打包输出的文件会被包含在发布包中。。
2、main: 指定 CommonJS 入口文件(通常用于 Node.js 环境)
3、module:指定 ES Module 入口文件(通常用于现代前端工具链,如 Webpack、Rollup 等)
4、types:指定类型声明文件入口
5、peerDependencies:提示宿主环境去安装满足插件 peerDependencies 所指定依赖的包,然后在插件 import 或者 require 所依赖的包的时候,永远都是引用宿主环境统一安装的 npm 包,最终解决插件与所依赖包不一致的问题
按如上步骤执行完后,执行打包命令,npm run build,生成文件结构如下:
# 发布到 npm 以及可能会遇到的问题
然后执行 npm login 登录 npm,当然,前提是你得先有一个 npm 账户,如果没有就创建一下吧,登录完后,执行 npm publish 或 npm publish --access public。
这里可能会遇到这几个问题:
1、由于创建的项目是私有的,会报错,即 package.json 中有 "private": true 的设置,将其更改为 false 即可
2、使用非官方镜像源执行提交命令,如 cnpm 或者淘宝镜像源等等,可能会报错误,将镜像源切换为官方的 (https://registry.npmjs.org/) 即可(推荐安装 nrm 解决)
3、由于 npm 服务器在国外,所以可能会很慢,建议耐心等待,当然,也可通过代理解决(npm config set proxy 代理 ip:port 或 npm config set https-proxy 代理 ip:port)(移除代理:npm config delete proxy 或 npm config delete https-proxy)
4、由于与 npm 上已存在的项目冲突,会发布失败,建议更改 package.json 中的 name 字段的值
5、如果是已经发布过,需要重新发布,需要更改 package.json 中 version 字段,更改版本号,否则会发布失败
发布到 npm 成功后,使用如下:
1、安装发布的组件
// 组件的名字是你package.json文件中的name
npm install react-lib-lei
2
2、在项目中使用如下,一个具有 ts 类型支持的组件就可以成功使用了
import { Button } from "react-lib-lei";
import "react-lib-lei/dist/style.css"
const MyComponent = () => {
const handleClick = () => {
console.log("hello world")
}
return (
<div>
<Button text="hello" onClick={ handleClick } />
</div>
)
}
export default MyComponent;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
至此,一个组件库的发布与使用就成功解决了
# 补充
# 1、静态资源配置
例如图片存放在了 /src/assets/ 目录下,那么可以借助 vite-plugin-static-copy 插件来处理静态资源的复制和引入,同时配置路径别名,后续要用,在 vite.config.ts 中增加如下配置
import {viteStaticCopy } from 'vite-plugin-static-copy';
import { resolve } from 'path';
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
plugins: [
viteStaticCopy({
targets: [
{
src: 'src/assets/*',
dest: 'assets'
}
]
})
],
build: {
assetsDir: 'assets',
assetsInlineLimit: 0, // 禁用资源内联
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
修改 package.json,files 值改为:
"files": [
"dist",
"assets"
]
2
3
4
tsconfig.json 中增加如下配置:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}
2
3
4
5
6
7
8
此时,在组件中使用 import xx from "@/assets/xxxx" 就行了,这样打包以后,组件中也能正常访问到图片资源,不会丢失
package.json 部分配置如下:
{
"name": "react-lib-lei",
"version": "0.0.8",
"type": "module",
"files": [
"dist",
"assets"
],
"main": "./dist/my-lib.umd.js",
"module": "./dist/my-lib.es.js",
"types": "./dist/index.d.ts",
// .......
}
2
3
4
5
6
7
8
9
10
11
12
13
vite.config.ts 配置如下:
import { defineConfig } from 'vite'
import path, { resolve } from 'path'
import react from '@vitejs/plugin-react'
import dts from 'vite-plugin-dts';
import { viteStaticCopy } from 'vite-plugin-static-copy';
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
plugins: [
react(),
dts({
insertTypesEntry: true,
}),
viteStaticCopy({
targets: [
{
src: 'src/assets/*',
dest: 'assets'
}
]
})
],
base: './', // 确保资源路径相对于当前目录
build: {
assetsDir: 'assets',
assetsInlineLimit: 0, // 禁用资源内联
lib: {
entry: resolve(__dirname, 'packages/index.ts'),
name: 'my-react-component',
fileName: (format) => `my-lib.${format}.js`,
},
rollupOptions: {
external: ['react', 'react-dom'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM',
},
assetFileNames: 'assets/[name].[ext]'
},
},
}
})
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
44
45
46
47
tsconfig.json 配置如下:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"jsx": "react-jsx",
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
},
"strict": true,
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"allowImportingTsExtensions": true,
"declaration": true,
"outDir": "dist",
"emitDeclarationOnly": true,
"isolatedModules": true
},
"include": ["packages"],
"exclude": ["node_modules", "dist"]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 2、组件中使用 module css 配置
由于使用了 ts,而 ts 默认并不识别 .module.css 文件。因此需要创建或修改一个类型声明文件来解决这个问题。
在项目根目录下创建 global.d.ts 文件
declare module '*.module.css' {
const classes: { [key: string]: string };
export default classes;
}
2
3
4
在 tsconfig.json 中配置修改如下:
{
"include": ["packages", "global.d.ts"] // 增加global.d.ts配置
}
2
3
现在就可以在组件中使用 module css 了