AI助力快速引入外部组件到TinyEngine低代码引擎

本文由羽毛笔记作者观默原创。

背景:低代码时代的开发挑战

TinyEngine作为一款优秀的低代码平台,以其强大的功能和快速迭代能力赢得了众多开发者的青睐。它让开发者能够通过可视化界面快速构建应用,大大提升了开发效率。

然而,就像一座美丽的花园需要更多花卉品种来装点一样,TinyEngine也面临着组件生态的挑战:官方提供的组件虽然精心设计,但数量有限,难以满足企业级项目的多样化需求。更具挑战性的是,要将私有组件库接入TinyEngine,必须先为每个组件生成对应的schema文件——这就像为每朵花制作专属的标签卡片。

从痛点到机遇:AI时代的解决方案

最近,我们公司面临着将私有组件库接入TinyEngine的需求。如果采用传统的人工方式为每个组件编写schema文件,不仅耗时费力,更重要的是缺乏那种让人兴奋的技术创新感。想象一下,如果有几十个组件需要处理,那将是一个令人望而却步的重复性工作。

这让我开始思考:在AI技术日新月异的今天,是否有更优雅的解决方案?

传统方案 VS AI方案

在AI时代之前,我可能会选择Babel + AST(抽象语法树)的技术路线:

  • 解析Vue组件源码
  • 遍历AST节点
  • 提取props、events、slots信息
  • 手工编写转换逻辑

这种方法虽然可行,但就像用显微镜逐个检查细胞一样繁琐。而现在,我们有了更强大的工具——AI大语言模型

经过反复调试和优化,我成功开发出了一套与Claude Sonnet 4模型完美配合的Prompt,能够自动化生成高质量的组件schema文件。这就像拥有了一位经验丰富的架构师助手,能够理解组件的每一个细节并生成标准化的配置文件。

schema 生成 Prompt:

注意:group/category/npm 这三个字段请根据实际情况调整,不要照抄!
# 任务:Vue 组件 Schema 生成器

你是一位精通 Vue 3 Composition API、TypeScript 及低代码平台组件集成的资深架构师。你的任务是接收一个 Vue 组件的源代码及相关项目文件,然后生成一个完全符合指定规则、高度精确且信息丰富的 JSON Schema 文件,用于驱动低代码平台。

你的输出必须是一个**完整的、格式正确的 JSON 对象**,不包含任何额外的解释性文字。

---

### 前置检查:验证输入信息

在开始生成 Schema 之前,你必须首先验证是否已收到所有必需的信息。

**以下项目是必需的:**

1.  **`组件源代码`**: 目标 `.vue` 文件的完整内容。
2.  **`package.json`**: 项目的 `package.json` 文件完整内容。

**可选的组件元数据**:
*   `中文名称 (zh_CN)`: 如果未提供,从组件的 `defineComponent({ name: '...' })` 中提取 `name` 值。
*   `图标 (icon)`: 如果未提供,从组件的 `defineComponent({ name: '...' })` 中提取 `name` 值。
*   `描述 (description)`
*   `标签 (tags)`
*   `关键词 (keywords)`
*   `文档链接 (doc_url)`

**如果必需信息缺失,请不要继续。** 你的回应应该是一个清晰的请求,明确指出用户需要提供哪些缺失的具体内容。

只有在确认所有必需输入都已提供后,才能继续执行下面的生成步骤。

---

### 第一步:分析输入 (假设已通过前置检查)

你将收到以下输入:

1.  **`组件源代码`**: 一个完整的 Vue 组件 (`.vue`) 的文本内容。
2.  **`package.json`**: 项目的 `package.json` 文件内容。
3.  **`组件元数据` (可选)**:
    *   `中文名称 (zh_CN)`
    *   `图标 (icon)`
    *   `描述 (description)`
    *   `标签 (tags)`
    *   `关键词 (keywords)`
    *   `文档链接 (doc_url)`

---

### 第二步:执行生成规则

请严格按照以下规则,一步步构建最终的 JSON 对象:

#### 1. **顶层字段填充**

*   `component`: 从组件的 `defineComponent({ name: '...' })` 中提取 `name` 值。
*   **`name.zh_CN`**: 
    - 如果 `组件元数据` 中提供了 `中文名称`,则使用该值
    - 否则根据 `component` 名称进行智能推断和翻译:
      - `Button` -> `"按钮"`
      - `LoginInfo` -> `"登录信息"`
      - `SixAxisRobot` -> `"六轴机械臂"`
      - `Chamber` -> `"腔体"`
      - `Aligner` -> `"对中器"`
      - `MainMenuButton` -> `"主菜单按钮"`
      - `CdsState` -> `"CDS状态"`
      - 其他名称按语义进行合理的中文翻译
*   **`icon`**: 
    - 如果 `组件元数据` 中提供了 `图标`,则使用该值
    - 否则默认使用 `component` 名称(如 `"SixAxisRobot"` -> `"SixAxisRobot"`)
*   **`group`**: 固定为 `"DCP"`
*   **`category`**: 固定为 `"DCP"`
*   `description`: 如果 `组件元数据` 中提供了 `description`,则使用该值;否则默认为 `""`。
*   `tags`: 如果 `组件元数据` 中提供了 `tags`,则使用该值;否则默认为 `""`。
*   `keywords`: 如果 `组件元数据` 中提供了 `keywords`,则使用该值;否则默认为 `""`。
*   `doc_url`: 如果 `组件元数据` 中提供了 `doc_url`,则使用该值;否则默认为 `""`。
*   `devMode`: 固定为 `"proCode"`。

#### 2. **`npm` 对象构建**

根据 `package.json` 的内容,动态构建 `npm` 对象:

*   `package`: 从 `package.json` 中读取 `name` 字段。
*   `exportName`: **必须**与顶层 `component` 字段的值保持一致。
*   `version`: 从 `package.json` 中读取 `version` 字段。
*   `script`: 基于 `package.json` 的信息,拼接成固定格式:`"http://192.168.0.212:4874/{package}@{version}/js/web-component.mjs"`。
*   `destructuring`: 固定为 `true`。
*   `npmrc`:
    1.  从 `package.json` 的 `name` 字段提取 scope (例如 `@dcp/component-library` -> `@dcp`)。
    2.  从 `package.json` 的 `publishConfig.registry` 字段提取 registry URL (并移除末尾的 `/`)。
    3.  拼接成 `"{scope}:registry={registry_url}"` 的格式。

#### 3. **`configure` 对象构建**

生成完整的 `configure` 对象,包含以下所有字段:

**基础行为控制**:
*   `loop`: 固定为 `true`(支持循环渲染)
*   `condition`: 固定为 `true`(支持条件渲染)
*   `styles`: 固定为 `true`(支持样式配置)

**组件类型标识**:
*   `isContainer`: 根据组件分析决定:
    - 如果组件模板中包含 `` 标签,设置为 `true`
    - 如果组件名称暗示容器用途(如 Layout、Container、Wrapper),设置为 `true`
    - 否则设置为 `false`
*   `isModal`: 固定为 `false`(除非组件明确是模态框)
*   `isPopper`: 固定为 `false`(除非组件明确是弹出框)
*   `isNullNode`: 固定为 `false`
*   `isLayout`: 根据组件用途判断,Layout 类组件设置为 `true`,否则为 `false`

**嵌套规则**:
*   `nestingRule`: 对象包含以下字段,通常设置为默认值:
    - `childWhitelist`: `""`(允许的子组件白名单,通常为空)
    - `parentWhitelist`: `""`(允许的父组件白名单,通常为空)
    - `descendantBlacklist`: `""`(禁止的后代组件黑名单,通常为空)
    - `ancestorWhitelist`: `""`(允许的祖先组件白名单,通常为空)

**编辑器配置**:
*   `rootSelector`: 固定为 `""`
*   `shortcuts.properties`: 识别出组件最核心、最常用的 1-3 个 props,填入此数组
*   `contextMenu`: 对象包含:
    - `actions`: 默认为 `["copy", "remove", "insert", "updateAttr", "bindEevent"]`
    - `disable`: 默认为 `[]`

**交互行为** (可选字段,根据组件类型添加):
*   `clickCapture`: 对于按钮类、交互类组件设置为 `true`,其他组件可省略或设置为 `false`
*   `framework`: 如果是第三方组件库保持原值,自定义组件设置为 `"Vue"`

#### 4. **`schema.properties` (Props 分组映射)**

将 Vue 组件的所有 props 按逻辑功能分组,生成一个**分组数组**:

**分组策略**:
*   **基础属性**: 核心功能相关的 props(如 name、size、type 等)
*   **样式属性**: 外观、颜色、尺寸相关的 props(如 width、height、backgroundColor、color 等)
*   **行为属性**: 交互、事件、状态相关的 props(如 disabled、loading、onClick 等)
*   **高级属性**: 可选的、专业配置项(如复杂对象配置、高级选项等)

**每个分组对象必须包含**:
*   `name`: 分组标识符,使用数字字符串(如 `"0"`, `"1"`, `"2"`)
*   `label.zh_CN`: 分组的中文显示名称(如 `"基础属性"`, `"样式属性"`)
*   `description.zh_CN`: 分组的中文描述
*   `content`: 该分组下的具体属性配置数组

**`content` 数组中的每个属性对象必须包含以下固定字段**:
*   `property`: Prop 的名称
*   `label.text.zh_CN`: 中文标签
*   `description`: 中文描述
*   `required`: 根据 Vue Prop 中的 `required` 字段决定,默认为 `false`
*   `readOnly`: 固定为 `false`
*   `disabled`: **固定为 `false`**
*   `cols`: **固定为 `12`**
*   `labelPosition`: 固定为 `"left"`
*   `type`: Vue 类型转换为小写字符串
*   `defaultValue`: Vue Prop 的默认值
*   `widget`: 根据以下规则推断

**Widget 推断规则 (按优先级顺序)**:

1. **validator 函数解析 (最高优先级)**:
   - 如果 Prop 定义中存在 `validator` 函数,解析函数体中的选项数组
   - 设置 `widget.component` 为 `"SelectConfigurator"`
   - 设置 `widget.props.options` 为解析出的选项数组

2. **属性名称模式匹配**:
   - 名称包含 `color` 或默认值以 `#` 开头 -> `"ColorConfigurator"`,props: `{}`
   - 名称包含 `icon` -> `"InputConfigurator"`,props: `{ "placeholder": "请输入图标名称" }`

3. **Vue 类型 + 语义推断**:
   - `Boolean` 类型:
     - 开关语义 (show*, enable*, is*) -> `"SwitchConfigurator"`,props: `{}`
     - 选项语义 (disabled, loading, plain, round, circle) -> `"CheckBoxConfigurator"`,props: `{}`
   - `Number` 类型 -> `"NumberConfigurator"`,根据属性名称设置 props:
     - 尺寸类 (width, height, size): `{ "min": 50, "max": 2000, "step": 10 }`
     - 角度类 (rotate, angle): `{ "min": 0, "max": 360, "step": 1 }`
     - 比例类 (scale): `{ "min": 0.1, "max": 5, "step": 0.1 }`
     - 时间类 (duration, delay): `{ "min": 0, "max": 50, "step": 0.1 }`
     - 默认: `{ "step": 1 }`
   - `String` 类型 -> `"InputConfigurator"`,props: `{ "placeholder": "请输入..." }`
   - `Object`/`Array` 类型 -> `"CodeConfigurator"`,props: `{ "language": "json", "height": 150 }`

4. **智能类型分析**:
   - 如果 Prop 类型为 `Array as PropType`,在 `description` 中补充接口结构信息

#### 5. **`schema.events` (事件映射)**

*   在组件 `

通过AI处理后,会生成一个包含以下关键信息的schema:

  • Props分组:label、type、size被分类为“基础属性”,disabled、loading分类为“行为属性”
  • 组件类型:type属性自动配置为下拉选择器,并包含正确的选项
  • 事件映射:click事件被正确转换为onClick处理函数

第二步:添加物料

把上一步得到的json文件保存到TinyEngine项目根目录下的materials/components 文件夹下。

第三步:添加仓库

如果你没有添加过仓库配置,那么你需要在 项目根目录下的materials/packages.json 文件中添加你的仓库信息:

{
      "name": "DCP组件库",
      "package": "@dcp/component-library",
      "version": "0.0.60",
      "script": "http://192.168.0.212:4874/@dcp/[email protected]/js/web-component.mjs",
      "destructuring": true,
      "npmrc": "@dcp:registry=http://192.168.0.212:4873"
    }

第四步:构建物料

现在你可以直接在终端执行pnpm buildMaterials ,等终端不再有新的输出时,可以ctrl + c 退出脚本。

一切就绪,启动项目验收

至此,你可以pnpm serve:frontend 后访问项目来使用新增的组件了。

问题排查指南

在实际开发过程中,即使按照上述步骤操作,也可能遇到组件未正常显示的情况,这里为您提供了系统化的排查方法。

关键检查点一:组件获取机制

首先检查TinyEngine的组件获取机制是否正常工作。在以下位置添加调试信息:

文件位置: packages/canvas/render/src/material-function/material-getter.ts#L109

export const getComponent = (name) => {
  // 添加调试信息
  console.log(`正在获取组件: ${name}:${getNative(name)}`)
  
  const result = Mapper[name] || getNative(name) || getBlock(name) || (isHTMLTag(name) ? name : getBlockComponent(name))
  
  console.log(`获取结果:`, result)
  return result
}

ℹ️ 排查要点:对于引入的组件,getNative(name)应该返回非空值。如果返回undefined,说明组件没有被正确注册。

关键检查点二:动态导入机制

如果上一步检查发现问题,接下来排查动态导入机制。在以下位置添加调试代码:

文件位置: packages/canvas/common/src/utils.ts#L100

const dynamicImportComponentLib = async ({ pkg, script }: DynamicImportParams): Promise => {
  console.log(`开始导入组件库: ${pkg}`)
  
  if (window.TinyComponentLibs[pkg]) {
    console.log(`组件库已存在缓存中: ${pkg}`)
    return window.TinyComponentLibs[pkg]
  }

  if (!script) {
    console.warn(`组件库 ${pkg} 缺少 script 配置`)
    return {}
  }

  const href = window.parent.location.href || location.href
  const scriptUrl = script.startsWith('.') ? new URL(script, href).href : script
  
  console.log(`动态导入组件库脚本: ${scriptUrl}`)

  if (!window.TinyComponentLibs[pkg]) {
    try {
      const modules = await import(/* @vite-ignore */ scriptUrl)
      console.log(`组件库导入成功:`, modules)
      window.TinyComponentLibs[pkg] = modules
    } catch (error) {
      console.error(`组件库导入失败: ${pkg}`, error)
      return {}
    }
  }

  return window.TinyComponentLibs[pkg]
}

常见问题及解决方案

最常见问题:组件未正确导出

在我的实际经验中,这个问题出现频率最高。很多时候我们在组件库中开发了新组件,但忘记在入口文件中导出它。

解决方案

  1. 检查组件库的index.tsindex.js文件
  2. 确认目标组件已经被正确导出:

    export { default as YourComponent } from './YourComponent.vue'

脚本路径错误

检查packages.json中的script字段是否正确。常见错误包括:

  • 版本号不匹配
  • 域名或端口错误
  • 文件路径错误

通过以上系统化的排查步骤,绝大多数问题都能够得到快速解决。

如果您对AI开发感兴趣,欢迎关注我的公众号:观默视界

在这里,我会分享更多关于AI技术在实际开发中的实战经验和最新趋势。

关于OpenTiny

欢迎加入 OpenTiny 开源社区。添加微信小助手:opentiny-official 一起参与交流前端技术~

OpenTiny 官网https://opentiny.design
OpenTiny 代码仓库https://github.com/opentiny
TinyVue 源码https://github.com/opentiny/tiny-vue
TinyEngine 源码: https://github.com/opentiny/tiny-engine
欢迎进入代码仓库 StarTinyEngine、TinyVue、TinyNG、TinyCLI、TinyEditor~

如果你也想要共建,可以进入代码仓库,找到 good first issue标签,一起参与开源贡献~

你可能感兴趣的:(前端ai开发低代码)