当前位置: 首页 > ops >正文

vite+react+antd,封装公共组件并发布npm包

一 概述

        为了公司项目系统风格统一,需要对部分公共组件进行封装。此文便是为了记录自己在探索过程中的步骤和注意点。这个例子主要是根据UI设计的样式封装一个antd中的弹窗组件。

二 了解NPM

        NPM(Node Package Manager)是一个 JavaScript 包管理工具,也是 Node.js 的默认包管理器(是 Node.js 自带的包管理工具,只要安装了 Node.js,NPM 就会自动安装在系统中)。

1. 作用

        用于安装、共享和管理项目依赖的代码库(称为“包”或“模块”)。

2. 核心功能

3. 核心文件:package.json

4.重点要求

        package.json中,除了dependencies、devDependencies,还有一个peerDependencies(开发包时候需要用到)。下面是这三者的区别,如下所示:

具体内容如下:

(1)dependencies(生产依赖)

        作用:项目运行时必须的依赖(如 React、ReactDOM、Redux)。

        安装命令:npm install xxxxxx  或 yarn add  xxxxxx

        特点:会被打包到最终的生产环境代码中(如 build/ 目录);安装时递归安装子依赖。

(2)devDependencies(开发依赖)

        作用:仅在开发阶段需要的工具(如测试库、打包工具、ESLint)。

        安装命令:npm install xxxxxx--save-dev  或 yarn add xxxxxx -D

        特点:不会打包到生产代码中;其他项目安装你的包时不会安装这些依赖

(3)peerDependencies(同伴依赖)

        作用:声明你的包需要宿主环境提供的依赖(避免重复安装/版本冲突),常见于 React 组件库。

        安装命令:npm install your -lib   

        特点:

                用户安装你封装的库时,需手动安装 peerDependencies里面的内容,如果版本不匹配,会抛出警告。

如何定义(写入) :

        npm install react --save-peer 

        或 yarn add react --peer  

        或 手动编辑package.json中的peerDependencies 对象

 三  使用vite进行react项目创建。

运行效果如下:

至此,项目文件结构如下如下所示:

四 安装需要使用的依赖和注意事项

        这里组件开发涉及到了 and、less等依赖包,因此需要进行手动安装。 

注意事项:

        这里将antd、react 和react-dom都需要安装在了开发依赖(npm install xxxxxx --save-dev)中。并且也在peerDependencies中手动新增(手动写上去)这几个依赖,dependencies中的依赖跟peerDependencies中的最低版本的依赖包最好相同,如react 在peerDependencies中为>=18.3.1,那么 dependencies 中版本为18.3.1。

        原因:一般正常情况下,react、antd等依赖是需要安装在dependencies中。但是这个程序主要用于npm包的开发,如果写在dependencies中,那么,使用该组件程序中的 antd、react和react-dom的版本必须要和发布包中的antd、react和react-dom的版本一样,否则会报版本冲突的问题。这样就对使用程序限制了相关依赖版本,不太友好。

        所以,在第二大点的第四小点介绍了peerDependencies的意义,这里就需要用到。然后,这里的依赖包版本也不能写死,需要写一个最低可使用版本即可。

        因此,在代码中,我直接删除了package.json 中 dependencies 内容。将dependencies中的react和react-dom  重新安装到devDependencies 中。手动安装的依赖有:

npm install @ant-design/icons --save-dev
npm install antd --save-dev 
npm install react --save-dev  
npm install react-dom --save-dev
npm install less --save-dev
npm install vite-plugin-dts --save-dev

        我的依赖文件如下所示(devDependencies对象里面没内容,package.json会自动删除,或者手动删除也可 ):

五 组件开发

1. 创建组件文件夹

        在src下面新增一个components文件夹,并在里面创建一个自己的组件文件夹:CommonModal,并开始封装自己的组件。目录如下:

2. 创建组件代码

        在index.tsx 里面开发自己的modal组件(如果想边开发边看效果,那么可以先把index.tsx搭一个框架后,根据下一步骤(步骤3)去预览,看到预览效果后过来继续开发,比较好调试)。我封装的弹窗代码如下:

index.tsx

import * as React from 'react';
import {CloseOutlined} from '@ant-design/icons';
import {Modal} from 'antd';
import styles from './index.module.less'export interface CommonModalProps {visible: boolean;title?: string;content?: React.ReactNode;onCancel?: () => void;width?: number;
}export const CommonModal: React.FC<CommonModalProps> = ({visible,title,content,onCancel,width,}) => {return (<Modalopen={visible}closeIcon={false}onCancel={onCancel}maskClosable={false}width={width}centered={true}className={styles.customModal}footer={null}><div className={styles.modalHeader}><span className={styles.title}>{title}</span><CloseOutlined className={styles.close} onClick={onCancel}/></div><div className={styles.modalBody}>{content}</div></Modal>);
};

注意:封装的组件和组件涉及到的ts类型都需要进行export导出

index.module.less

.customModal {:global {.ant-modal-content {padding: 0;border-radius: 4px;overflow: hidden;}}.modalHeader {display: flex;justify-content: space-between;align-items: center;padding: 20px 24px;background: cornflowerblue;.title {color: white;font-weight: 700;font-size: 20px;}.close {cursor: pointer;color: white;font-size: 20px;}}.modalBody {padding: 20px 24px;}}

3. 组件效果预览

        当组件开发了之后,要进行效果预览。这里就可以修改App.tsx里面的代码。App.css 里面的内容不用的话可以删除。不会影响组件的封装,只会影响本地样式。App.css直接为空也可。

        而在App.tsx中,导出写的组件,直接写用法使用即可。我的App.tsx代码如下:

import {useState} from 'react'
import {CommonModal} from "./components/CommonModal";
import {Button} from "antd";function App() {const [visible, setVisible] = useState(false);return (<div><Button type={'primary'} onClick={() => setVisible(true)}>打开弹窗</Button><CommonModal visible={visible} title={'标题'}onCancel={() => setVisible(false)}content={<div>这是一个只展示的弹窗,不会展示确定按钮</div>}/></div>)
}export default App

程序运行后,效果如下:

至此,组要封装的组件就已经完毕,确定组件没问题后,就可以进行npm包的发布了。

六 发布包准备

        进行NPM包的发布,需要对项目部分文件进行调整。

1. 新增包的入口文件

        在src下面新增一个index.ts文件,并在里面导出封装的组件以及需要导出的样式文件。如:

import 'antd/dist/reset.css';
// 导出所有组件
export {CommonModal} from './components/CommonModal';
export type {CommonModalProps} from './components/CommonModal'

2. 新增vite-env.d.ts的内容

declare module '*.module.less' {const classes: { readonly [key: string]: string };export default classes;
}

3.  配置package.json文件

{// 基础信息配置"name": "my-test-lib", // 要发布的包名"private": false,"version": "1.0.0", // 包的版本号(每发布一次,都要修改,否则会发布失败)"type": "module", // 声明项目使用 ES 模块(import/export 语法),而非 CommonJS。// 下面内容是入口文件配置"main": "./dist/index.es.js", // CommonJS/ESM 通用入口:当用户通过 require() 或 import 加载包时,默认指向此文件"module": "./dist/index.es.js", // ES 模块专用入口:支持 ESM 的打包工具(如 Webpack、Rollup)会优先使用此路径,实现 Tree Shaking 优化"types": "./dist/index.d.ts", // TypeScript 类型声明文件,提供类型支持(由 vite-plugin-dts 生成)"style": "dist/style.css", // 全局样式入口,用户可通过 import 'wxf-component-library/style' 引入全量 CSS。// 下面是发布配置"files": ["dist"], // 发布到 npm 时仅包含 dist 目录(构建后的代码、类型声明和样式),忽略源码和非必要文件。"scripts": {"dev": "vite","build": "vite build","lint": "eslint .","preview": "vite preview","prepublishOnly": "npm run build" // 在npm publish 前自动执行构建,确保发布的是最新产物},"peerDependencies": {"antd": ">=5.24.1","react": ">=18.3.1","react-dom": ">=18.3.1"},"devDependencies": {"@ant-design/icons": "^6.0.0","@eslint/js": "^9.33.0","@types/react": "^19.1.10","@types/react-dom": "^19.1.7","@vitejs/plugin-react": "^5.0.0","antd": "^5.24.1","eslint": "^9.33.0","eslint-plugin-react-hooks": "^5.2.0","eslint-plugin-react-refresh": "^0.4.20","globals": "^16.3.0","less": "^4.4.0","react": "^18.3.1","react-dom": "^18.3.1","typescript": "~5.8.3","typescript-eslint": "^8.39.1","vite": "^7.1.2"}
}

4. 修改 tsconfig.json 文件

{"compilerOptions": {"allowImportingTsExtensions": true, // 允许在导入语句中使用 TypeScript 特有的扩展名(如 .ts, .tsx)"noEmit": true,"declaration": true, // 生成 .d.ts 类型声明文件(即使 noEmit=true 也生效)、使组件库支持 TypeScript 类型提示"declarationMap": true, // 为声明文件生成 Source Maps(.d.ts.map)、允许用户 "跳转到定义" 时直接查看源码而非声明文件
//    "outDir": "./dist", // 输出目录(当有文件生成时)
//    "declarationDir": "./dist", // 声明文件的专用输出目录"jsx": "react-jsx", // 使用 React 17+ 的新 JSX 转换"jsxImportSource": "react", // 指定 JSX 函数的导入来源为 react"esModuleInterop": true, // 改进 ES 模块与 CommonJS 的互操作性,允许 import React from 'react' 代替 import * as React from 'react'"skipLibCheck": true, // 跳过声明文件(.d.ts)的类型检查"moduleResolution": "node" // 使用 Node.js 风格的模块解析策略},"include": ["src/**/*"], // 仅编译 src 目录下的所有文件"exclude": ["node_modules", "dist"] // 排除 node_modules 和 dist 目录
}

5. 修改vite.config.ts文件

import {defineConfig} from 'vite'
import react from '@vitejs/plugin-react'
import {resolve} from 'path'
import dts from "vite-plugin-dts";export default defineConfig({plugins: [react(),dts({outDir: './dist',insertTypesEntry: true, // 生成类型入口include: ['src/**/*'],// 包含所有源文件// 添加路径解析配置compilerOptions: {allowImportingTsExtensions: true}})],resolve: {// 确保 Vite 能解析 .tsx 文件extensions: ['.tsx', '.ts', '.js', '.jsx']},build: {lib: {entry: resolve('./src/index.ts'), // 组件库入口name: 'MyTestLib', // 全局变量名fileName: (format) => `index.${format}.js`},rollupOptions: {// 外部化依赖,避免打包进库external: ['react', 'react-dom', 'antd', '@ant-design/icons', 'react/jsx-runtime'],output: {globals: {react: 'React','react-dom': 'ReactDOM',antd: 'antd','@ant-design/icons': 'icons','react/jsx-runtime': 'jsxRuntime'},preserveModules: false}},cssCodeSplit: true,// sourcemap: true,sourcemap: process.env.NODE_ENV !== 'production',minify: false},css: {preprocessorOptions: {less: {javascriptEnabled: true}}}
})

6. 文件结构如下

7. 发布打包测试

        这里先进行发布前的打包测试。运行npm run build 。如果成功了,可以出现一个dist文件夹,里面有相关内容如下:

8. README.md

        对于要发布的包,可以完善 README.md 文件,使用户可以更好的了解该组件以及其使用方式。示例如下:

七 开始发布

        如果上面dist文件准确,则就可以进行npm发布了。步骤如下:

1. npm账号登录

        发布包需要先登录npm账号,如果没有登录,会报403错误(报错后,点击提示信息中的登录网址也可以进行登录)。

(1)输入 npm login ,控制台会提示在浏览器进行登录,并赋予登录链接。

(2)点击链接,跳转浏览器登录

如果有账号,直接从邮箱里面获取一次性密码登录即可。如果没有,需要注册再登录。

如果登录成功,浏览器会显示:

并且控制台也会显示已登录字样

2. 开始发布

        账号登录后,在该项目目录下运行npm publish 命令。

       我这里首次发布报错 403,原因是:npm库已经有 my-test-lib 这个包了。所以继续发布是有问题的。因此需要改一下package.json和vite.config.ts 中的name,使包名唯一(无论该包是否被废弃还是有效)

        所以,我把包名改为了 teach-test-lib ,继续进行发布。就发布成功了。

发布成功后,在网页上登录自己的npm,会在上面看到已经发布的包。

3. 使用

        发布包的使用方式跟npm上其他包的使用方式相同。直接使用npm或yarn安装即可。

        注意:这里需要手动引入包的css样式。例子如下:

import { useState} from 'react';
import {Button} from 'antd'
import {CommonModal} from "teach-test-lib"; // 导入包
import "teach-test-lib/dist/index.css"; // 手动引入CSS样式export default function Login() {const [visible, setVisible] = useState(false);return (<div  ><Button type={"primary"} onClick={() => setVisible(true)}>打开弹窗</Button><CommonModal visible={visible} title={"标题"}onCancel={() => setVisible(false)}content={<div>这是引入的测试内容</div>}/></div>);
}

4. npm 包相关的其他命令

5. 发布403问题及解决方法

(1) 未登录:点击提示信息中的登录地址登录;

(2) 邮箱未验证:登录 npm 官网验证邮箱 ;

(3)包名已被占用 :用 npm view <包名> 检查,修改 package.json 中的 name;

(4)镜像源非官方(可能是自己的私服):切换回官方源:npm config set registry https://registry.npmjs.org;

(5) 版本号冲突:报错You cannot publish over previously published versions,去修改版本号(不可重复)即可。

http://www.xdnf.cn/news/18058.html

相关文章:

  • lamp架构部署wordpress
  • 【新手易混】find 命令中 -perm 选项的知识点
  • Vue2篇——第六章 Vue 路由(VueRouter)全解析
  • 【AI论文】观察、聆听、记忆与推理:具备长期记忆能力的多模态智能体
  • 神经网络显存占用分析:从原理到优化的实战指南
  • 51c大模型~合集170
  • 窗口看门狗(WWDG)
  • SpringBoot--JWT
  • 【加密PMF】psk-pmk-ptk
  • FPGA驱动量子革命:微美全息(NASDAQ:WIMI)实现数字量子计算关键验证
  • DFS与BFS模块总结
  • 【论文阅读】-《HopSkipJumpAttack: A Query-Efficient Decision-Based Attack》
  • 哪里找最新AI工具官网?如何快速对比ChatGPT替代品?AI工具导航指南 - AIbase
  • WordPress (LNMP 架构) 一键部署 Playbook
  • 【运维实战】系统全链路监测方案~架构到实践
  • linux:告别SSH断线烦恼,Screen命令核心使用指南
  • 计算机视觉(9)-实践中遇到的问题(六路相机模型采集训练部署全流程)
  • Day119 持续集成docker+jenkins
  • 机器学习之数据预处理(二)
  • 探索性测试:灵活找Bug的“人肉探测仪”
  • 双通道审核智能合约更新路径:基于区块链与AI融合的编程范式分析
  • gflags框架安装与使用
  • [激光原理与应用-296]:理论 - 非线性光学 - 线性光学与非线性光学对比
  • 《亚矩阵云手机重构出租接单:KVM 虚拟化与边缘计算驱动的设备替代技术路径》
  • leetcode43. 字符串相乘
  • 06.文件权限管理
  • 从 UI 角度剖析蔬菜批发小程序的设计之道——仙盟创梦IDE
  • Nextcloud容器化部署革新:Docker+Cpolar构建高效私有云远程访问新架构
  • 构建经典PyTorch框架卷积神经网络参数demo
  • Python 调试工具的高级用法