# react-widget-template **Repository Path**: lesley92/react-widget-template ## Basic Information - **Project Name**: react-widget-template - **Description**: No description available - **Primary Language**: JavaScript - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-06-09 - **Last Updated**: 2025-06-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # React 18 控件开发模板 (TypeScript) - 使用指南 欢迎使用本控件开发模板!该模板旨在为第三方开发者提供一个基于 React 18 和 TypeScript 的现代化、开箱即用的控件开发环境。它预配置了 Vite 作为构建工具,支持 Less 样式,并针对无代码平台的集成需求进行了优化。 **核心目标:** * 提供快速的本地开发和调试体验。 * 打包输出为单一的 UMD 格式 JavaScript 文件。 * CSS 样式内联到最终的 JS 文件中。 * 核心依赖 (`react`, `react-dom`, `lodash`, `moment`) 作为外部依赖,由宿主平台提供。 * 提供清晰的 TypeScript 类型支持和全局类型定义。 ## 目录 1. [项目概览](#项目概览) 2. [快速开始](#快速开始) 3. [项目结构](#项目结构) 4. [核心配置与自定义](#核心配置与自定义) * [配置控件名称](#配置控件名称) 5. [开发您的控件](#开发您的控件) * [编写控件逻辑与样式](#编写控件逻辑与样式) * [使用全局类型定义](#使用全局类型定义) * [Props 定义](#props-定义) * [与平台交互 (EventBus 和 MethodRegistry)](#与平台交互-eventbus-和-methodregistry) 6. [本地调试](#本地调试) 7. [类型检查与代码规范](#类型检查与代码规范) 8. [打包控件](#打包控件) 9. [注意事项](#注意事项) ## 1. 项目概览 * **技术栈**: React 18, TypeScript, Vite, Less * **Node**: Node.js 版本建议为 v18 * **依赖外部化**: `react`, `react-dom`, `lodash`, `moment` * **打包格式**: UMD (Universal Module Definition) * **样式处理**: Less 样式内联打包到 JS 文件中 * **输出**: 单一 JS 文件 (`${ComponentName}.js`) * **本地调试**: 提供独立的调试页面,方便开发测试。 * **类型系统**: 全面支持 TypeScript,提供全局类型定义。 ## 2. 快速开始 1. **获取项目模板**: 下载或克隆此项目模板到您的本地。 2. **安装依赖**: 进入项目根目录,运行以下命令安装所有必要的依赖: ```bash yarn install # 或者 # npm install ``` ## 3. 项目结构 ``` react-widget-template/ ├── public/ # 静态资源 (可选,如 favicon) ├── src/ │ ├── components/ │ │ └── MyWidget.tsx # 您的控件主要逻辑 (您可以重命名) │ │ └── MyWidget.less # 您的控件样式 (您可以重命名) │ ├── App.tsx # 本地调试用根组件 │ ├── index.ts # 控件库入口文件 (导出您的主控件组件) │ └── main.tsx # 本地调试用 React 渲染入口 ├── .eslintrc.js # ESLint 配置 ├── .gitignore ├── index.html # 本地调试页面 HTML 入口 ├── package.json ├── tsconfig.json # TypeScript 主配置文件 ├── tsconfig.node.json # TypeScript 配置文件 (用于 vite.config.ts) ├── vite.config.ts # Vite 配置文件 (TypeScript 版本) └── types/ └── index.d.ts # 全局 TypeScript 类型定义 ``` ## 4. 核心配置与自定义 ### 配置控件名称 您需要为您的控件定义一个**唯一名称**。这个名称将用于: * UMD 模块的全局变量名 (例如 `window.MyUniqueWidget`) * 打包后输出的 JS 文件名 (例如 `MyUniqueWidget.js`) 打开项目根目录下的 `vite.config.ts` 文件,找到并修改文件顶部的 `COMPONENT_NAME` 常量: ```typescript // vite.config.ts // ! 第三方开发者需要修改这里的组件名 ! const COMPONENT_NAME: string = 'MyUniqueWidget'; // 将 'MyUniqueWidget' 替换为您的控件名称 // 例如: 'SuperButton', 'DataGridWidget' 等 // ... (文件其余内容) ``` 请确保这个名称具有唯一性,以避免与平台或其他控件发生冲突。 ## 5. 开发您的控件 ### 编写控件逻辑与样式 * 您的控件核心逻辑应编写在 `src/components/MyWidget.tsx` (您可以根据需要重命名此文件和相关导入)。 * 相关的 Less 样式文件是 `src/components/MyWidget.less` (同样可重命名)。 * `src/index.ts` 是您控件库的入口文件。请确保它默认导出了您的主控件组件。例如: ```typescript // src/index.ts import MyWidget from './components/MyWidget'; // 导入您的组件 export default MyWidget; ``` ### 使用全局类型定义 本项目在 `src/types/global.d.ts` 文件中定义了一些平台相关的全局 TypeScript 类型。这些类型无需导入即可在您的控件代码中使用。 主要的全局类型包括: * `IEventBus`: 事件总线接口,用于控件与平台或其他控件之间的通信。 * `MethodResult`: 控件方法执行后的返回结果结构。 * `MethodFunction`: 控件可暴露的方法的类型签名,支持同步和异步。 * `IMethodRegistry`: 方法注册中心接口,用于控件向平台注册其可调用方法。 * `IWidgetBaseProps`: 所有控件都会接收的基础 Props,包含了 ID、尺寸、运行状态以及平台提供的交互接口等。 您可以在 `src/types/index.d.ts` 查看完整的定义。 ### Props 定义 您的控件组件 Props 应该继承自全局的 `IWidgetBaseProps`,并可以添加控件特有的 Props。 **示例 (`src/components/MyWidget.tsx`):** ```typescript import React, { FC, useState, useEffect } from 'react'; import _ from 'lodash'; import moment from 'moment'; import './MyWidget.less'; // 继承 IWidgetBaseProps 并添加自定义 Props interface MyWidgetSpecificProps { countAttr?: number; message?: string; // 添加更多您控件需要的 props } // 合并基础 Props 和特定 Props type MyWidgetProps = IWidgetBaseProps & MyWidgetSpecificProps; const MyWidget: FC = (props) => { const { id, // 来自 IWidgetBaseProps name, // 来自 IWidgetBaseProps w, h, width, height, // 尺寸信息 isRun, // 运行态 onChange, // 值变化回调 onAttrChange, // 属性变化回调 eventBus, // 平台提供的事件总线实例 methodRegistry, // 平台提供的方法注册实例 countAttr = 0, // 自定义 Prop message = 'Default Message', // 自定义 Prop } = props; const [count, setCount] = useState(countAttr); useEffect(() => { // 控件加载时或特定条件下注册方法 if (methodRegistry && isRun) { const myMethod: MethodFunction = async (params) => { console.log( `Method 'myCustomMethod' called on widget ${id} with params:`, params ); // 执行逻辑... return { result: { newCount: count + ((params?.incrementBy as number) || 1) }, }; }; methodRegistry.register(id, 'myCustomMethod', myMethod); // 控件卸载时注销方法 return () => { methodRegistry.unregister(id, 'myCustomMethod'); }; } }, [id, methodRegistry, isRun, count]); // 依赖项 useEffect(() => { // 监听平台发出的事件 if (eventBus && isRun) { const handlePageEvent = (eventData: { event: string; source: string; payload: Record; }) => { console.log( `Widget ${id} received event '${eventData.event}' from '${eventData.source}':`, eventData.payload ); try { const { event } = eventData.payload; switch (event) { case 'dataGet': { console.log('数据获取事件'); break; } default: break; } } catch (error) { console.error('cycle oageEvent error', error); } }; eventBus.addListener('pageEvent', handlePageEvent); // 示例:监听页面的事件 return () => { eventBus.removeListener('pageEvent', handlePageEvent); }; } }, [id, eventBus, isRun]); // 依赖项 const handleIncrement = () => { const newCount = count + 1; setCount(newCount); // 如果控件属性值发生变化,调用 onAttrChange 回调 if (onAttrChange) { onAttrChange([{ id, values: { countAttr: newCount } }]); } // 示例:eventBus 发送自定义事件 if (eventBus) { /** * event: actionEvent * source: 控件id * payload: { event: 事件名称, eventParams: 事件参数{} } */ eventBus.dispatch('actionEvent', id, { event: 'countChange', eventParams: { count: newCount }, }); } }; return (

Widget: {name} (ID: {id})

{message}

Current date: {moment().format('YYYY-MM-DD HH:mm:ss')}

Running: {isRun ? 'Yes' : 'No'}, Dimensions: {w}x{h}

Count: {count}

Random number via lodash: {_.random(1, 100)}

); }; export default MyWidget; ``` ### 与平台交互 (EventBus 和 MethodRegistry) 平台会通过 Props (`eventBus` 和 `methodRegistry`) 将事件总线和方法注册中心的实例传递给您的控件。 * **`eventBus: IEventBus`**: * `eventBus.dispatch(event: string, source: string, payload: Record)`: 发送事件。`source` 通常是您控件的 `id`,如果是触发控件自定义事件,`event` 为 `actionEvent`,`payload` 为 `{ event: 事件名称, eventParams: 事件参数{} }`。 * `eventBus.addListener(event: string, listener: Function)`: 监听事件。 * `eventBus.removeListener(event: string, listener: Function)`: 移除事件监听。 * **重要**: 确保在组件卸载时移除通过 `addListener` 添加的监听器,以防止内存泄漏。通常在 `useEffect` 的清理函数中完成。 * **`methodRegistry: IMethodRegistry`**: * `methodRegistry.register(id: string, methodName: string, fn: MethodFunction)`: 向平台注册一个可供外部调用的方法。`id` 是您控件的 `id`,`methodName` 是方法的名称,`fn` 是实际的方法实现。 * `methodRegistry.unregister(id: string, methodName?: string)`: 注销方法。如果 `methodName` 未提供,则注销该控件 `id` 下的所有方法。 * **重要**: 方法应在控件准备好被调用时注册 (例如,`isRun` 为 `true` 时),并在控件卸载或不再需要该方法时注销。 ## 6. 本地调试 为了在开发过程中方便地测试您的控件,我们提供了一个本地调试环境。 * **运行开发服务器**: 在项目根目录运行以下命令启动开发服务器: ```bash yarn dev # 或者 # npm run dev ``` * Vite 会自动打开浏览器,访问 `http://localhost:3001` (如果端口被占用,可能会使用其他端口)。 * **`index.html` 说明**: `index.html` 文件用于承载本地调试页面。它会引入 `react`, `react-dom`, `lodash`, `moment` 的 CDN 脚本,以模拟平台环境已经提供了这些全局依赖。`` 是 Vite 开发模式下加载您的调试应用的入口。**这些仅用于本地开发,不会影响最终打包的控件文件。** ## 7. 类型检查与代码规范 * **TypeScript 类型检查**: 运行以下命令来检查项目中的 TypeScript 类型错误: ```bash yarn typecheck ``` * **ESLint 代码规范**: 本项目已配置 ESLint 和 Prettier 以保证代码风格一致性和潜在错误检查。 ```bash yarn lint # 检查代码 yarn lint --fix # 尝试自动修复问题 ``` ## 8. 打包控件 当您完成控件开发并准备发布时,执行打包命令: ```bash yarn build # 或者 # npm run build ``` * Vite 会使用 `vite.config.ts` 中的配置进行打包。 * 打包后的文件将输出到项目根目录下的 `dist/` 文件夹中。 * **输出文件命名**: 文件名将根据您在 `vite.config.ts` 中设置的 `COMPONENT_NAME` 来命名,例如 `dist/MyUniqueWidget.js`。 * **打包内容**: 此 JS 文件包含了您的控件所有代码和内联的 Less 样式,并且 `react`, `react-dom`, `lodash`, `moment` 等核心依赖已被外部化。 * 打包产物中的 `process.env.NODE_ENV !== "production"` 等开发模式代码会被移除,确保输出纯净的生产代码。 ## 9. 注意事项 * **`COMPONENT_NAME` 的唯一性**: 请务必在 `vite.config.ts` 中将 `COMPONENT_NAME` 设置为您的控件独有的名称。 * **全局类型**: 熟悉 `src/types/global.d.ts` 中定义的全局类型,并在您的控件 Props 和逻辑中正确使用它们。 * **平台交互**: 正确使用通过 Props 传递的 `eventBus` 和 `methodRegistry` 与平台进行交互,并注意在组件卸载时进行清理(移除监听器、注销方法)。 * **依赖外部化**: 控件打包时不包含 `react`, `react-dom`, `lodash`, `moment`。确保您的控件逻辑不依赖这些库的特定打包版本特性(通常不会有问题)。 * **本地调试**: `index.html` 中的 CDN 引入和 `