写点什么

解锁新技能!TinyEngine 低代码源码双向转换全攻略

作者:OpenTiny社区
  • 2025-12-11
    广东
  • 本文字数:3194 字

    阅读完需:约 10 分钟

解锁新技能!TinyEngine低代码源码双向转换全攻略

本文由 TinyEngine 低代码源码转换功能贡献者张珈瑜原创。

背景

当前主流低代码平台普遍采用“单向出码”模式,仅支持将 DSL(Domain Specific Language,领域特定语言)转换为 Vue 或 React 源代码。一旦开发者在生成代码后手动修改了源码,平台通常无法将这些修改同步回可视化编辑器,导致代码与可视化配置割裂,严重影响开发效率与协同维护。本项目旨在构建低代码 Vue/React 源代码到 DSL 的反向转换机制,打通可视化搭建与源码开发之间的断层,实现从 UI 配置到源码编写的无缝协同。

Vue-To-DSL 方案

目标

将 Vue 单文件组件(SFC)、整包工程或 ZIP 压缩包逆向转换为 TinyEngine 所需的 DSL Schema。

核心依赖

  • @vue/compiler-sfc / @vue/compiler-dom:解析 SFC 与模板 AST

  • @babel/parser / traverse / types:脚本 AST(支持 TS/JSX)

  • jszip:ZIP 文件读取(Node 与浏览器双端支持)

  • vue:仅用于类型对齐

数据流


解析流程详解

1. SFC 粗分

  • 使用 @vue/compiler-sfc.parse 获取 descriptor

  • 提取 template / script / scriptSetup / styles / customBlocks

  • 保留语言类型(如 lang="ts")和 scoped 状态

2. 模板解析(Template)

  • 使用 @vue/compiler-dom.parse 构建 AST

  • 递归生成组件树节点 { componentName, props, children }

  • 指令处理

  • v-ifcondition: JSExpression

  • v-forloop: { type: 'JSExpression', value: '...' }

  • v-model / v-show / v-on / v-bind → 映射至 props 或事件

  • v-slotslot: name

  • 文本节点

  • 纯文本 → Text 组件

  • 插值表达式 → Text + JSExpression

  • 组件名归一化

  • 优先使用 componentMap

  • HTML 原生标签保留小写

  • tiny-icon-* → 统一为 Iconname 属性设为 PascalCase 名称

3. 脚本解析(Script)

  • 使用 Babel 解析 TS/JSX

  • 组合式 API(script setup)

  • reactive() / ref()state

  • computed()computed

  • 顶层函数 → methods

  • onMounted 等 → lifecycle

  • Options API

  • 识别 data / methods / computed / props / 生命周期钩子

  • 源码恢复

  • 利用 AST 节点位置切片还原函数体

  • 箭头函数转为命名函数字符串

  • 错误处理:非 strict 模式下收集错误,不中断流程

4. 样式解析(Style)

  • 合并所有 <style> 块内容

  • 记录 scopedlang

  • 提供辅助工具(不直接写入 Schema):

  • parseCSSRules:抽取 CSS 规则

  • extractCSSVariables:提取 CSS 变量

  • extractMediaQueries:媒体查询识别

5. Schema 生成与归一化

  • Page Schema

  • 根节点为 Page,自动填充 fileNamemetaid

  • 行为域统一包装为 JSFunction / JSExpression

  • 深度清理多余空白字符

  • App Schema(多页面聚合):

  • 页面:src/views/**/*.vue

  • 国际化:src/i18n/{en_US,zh_CN}.json

  • 工具函数:src/utils.js(正则解析导出项)

  • 数据源:src/lowcodeConfig/dataSource.json

  • 全局状态:src/stores/*.js(轻量识别 Pinia defineStore

  • 路由元信息:从 src/router/index.js 提取 name / path / isHome

6. 转换器接口

  • convertFromString(code, fileName?)

  • convertFromFile(filePath)

  • convertMultipleFiles(files)

  • convertAppDirectory(appDir)

  • convertAppFromZip(zipBuffer)

React-To-DSL 方案

目标

将单个 React 组件(JSX/TSX)逆向转换为 TinyEngine 可消费的 DSL(IAppSchema),当前聚焦 单文件 → 单页面/区块 场景。

核心依赖

  • @babel/parser / traverse / generator:AST 解析与代码生成

  • nanoid:生成唯一 ID

转换流程


关键步骤说明

1. AST 解析

  • 启用 jsx + typescript 插件

  • 定位首个返回 JSX 的函数/类组件

  • 记录 useState 初始值节点、组件定义路径

2. JSX → children 树构建

  • 组件名

  • JSXIdentifier → 直接使用

  • JSXMemberExpression → 拼接如 Form.Item

  • 兜底为 Fragment

  • Props 处理

  • 字面量 → 直接值

  • 表达式 → JSExpression

  • Spread 属性 → 特殊 key '...'

  • Children

  • 文本 → 包装为 span + props.children

  • 表达式容器:

  • 若为 arr.map(item => <El />) → 提取 arr 作为 loop

  • 否则 → Fragment + JSExpression

3. 表达式序列化

  • 字面量(string/number/bool/null)→ 原值

  • 对象/数组 → 递归处理,Spread 元素标记为 '...'

  • 其他表达式(函数调用、三元等)→ 源码字符串 + JSExpression

4. State 与方法提取

  • State:仅首个 useState 的初始值

  • Methods

  • 函数组件:顶层函数声明或变量赋值函数

  • 类组件:非 renderClassMethod 或箭头属性

  • 生命周期:类组件中的 componentDidMount 等白名单方法

5. 组件归一化

  • 应用 defaultComponentMap(如 FormTinyForm

  • DatabaseOutlinedIcon + props.name = 'IconPanelMini'

  • style 对象 → 转为 kebab-case: value; 字符串

  • valuemodelValue(适配 Tiny 组件)

6. Schema 装配

  • PageSchema

  • componentName: 'Page''Block'

  • meta: 默认 isHome=true, router='/'

  • children: 来自 JSX 树

  • state/methods/lifeCycles: 提取结果

  • AppSchema:包裹单个 Page,其余字段初始化为空


(本项目为开源之夏活动贡献,欢迎大家体验并使用)源码可参考:https://github.com/opentiny/tiny-engine/tree/ospp-2025/source-to-dsl

快速上手

前置条件: 已安装 Node.js (>=18)pnpm (>=8)git,本地 8090 端口可用。

启动项目

git clone -b ospp-2025/source-to-dsl https://github.com/opentiny/tiny-engine.gitcd tiny-enginepnpm installpnpm dev
复制代码


启动成功后,访问 http://localhost:8090/?type=app&id=1&tenant=1

可视化导入 Vue 文件

进入上方地址后,点击页面右上角的“导入”。支持三种来源:


  • 单个 .vue 文件:适合导入单页或区块。

  • 项目目录:自动识别 src/views 下的页面文件。

  • ZIP 压缩包:打包后的 Vue 项目一键导入。


导入流程(任选其一):


  1. 单个 .vue 文件


  • 选择“单页上传”,挑选本地 .vue 文件。

  • 若存在同名页面,按提示“覆盖/跳过/全部覆盖”进行处理。

  • 导入完成后,在“静态页面”列表中可见,双击打开编辑。


  1. 项目目录


  • 选择“目录上传”,指向本地项目根目录。

  • 系统自动扫描 src/views 并导入页面;遇到重名同样可选择覆盖策略。

  • 完成后,页面会按目录结构展示在左侧列表。


  1. ZIP 压缩包


  • 选择“项目压缩包”,上传打包好的 Vue 项目 zip。

  • 支持批量导入与重名处理,完成后即可在列表中浏览与打开。



选择单页 vue 文件上传方式进行导入:




由于已经存在 CreateVm 页面,弹出了提示框,需要选择是否覆盖,这里点击确定:



在静态页面列表可查看到导入的页面,双击即可点开:



选择项目目录或项目压缩包上传方式进行导入:



选择目录导入则选择本地的目录进行上传:



选择项目压缩包则选择 vue 项目 zip 进行上传:



导入时有重名文件则会弹出提示框,选择是否覆盖,这里选择全选+确定:



可以看到整个项目已经被导入到可视化编辑器了


React-To-DSL 测试用例

当前 React-To-DSL 以测试用例形态展示能力,可在包内直接运行:


cd packages/react-to-dslpnpm test
复制代码


输入样例:查看用例中的 React 组件源码(JSX)



输出结果:测试通过时会生成/断言对应的 DSL 结构,便于对照验证



(本项目为开源之夏活动贡献,欢迎大家体验并使用)源码可参考:https://github.com/opentiny/tiny-engine/tree/ospp-2025/source-to-dsl

结语

本项目成功实现了 Vue/React 源码 DSL 的双向转换机制,有效解决了低代码平台“单向出码”导致的协同断层问题。通过模块化解析、健壮的错误处理与灵活的组件映射策略,确保了转换的准确性与实用性。


感谢导师的悉心指导,以及 OpenTiny 社区与开源之夏活动组委会的支持,让我有机会参与这一具有实际价值的开源项目!

关于 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

欢迎进入代码仓库 Star🌟TinyEngine、TinyVue、TinyNG、TinyCLI、TinyEditor~

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

发布于: 2025-12-11阅读数: 2
用户头像

OpenTiny 企业级web前端开发解决方案 2023-06-06 加入

官网:opentiny.design 我们是华为云的 OpenTiny 开源社区,会定期为大家分享一些团队内部成员的技术文章或华为云社区优质博文,涉及领域主要涵盖了前端、后台的技术等。

评论

发布
暂无评论
解锁新技能!TinyEngine低代码源码双向转换全攻略_Vue_OpenTiny社区_InfoQ写作社区