文章定位:纯原生手写、零第三方低代码SDK、工程化落地实战
技术栈:Vue3 + Vite + CompositionAPI + 原生Drag&Drop + 自定义DSL + 代码生成器
适合人群:前端进阶、架构落地、想吃透低代码底层原理、拒绝只会用现成平台的开发者
核心亮点(区别于全网烂大街教程):
- 零封装黑盒:不使用
vuedraggable、Sortable 等拖拽库,纯手写原生拖拽引擎,彻底吃透底层 - 解决工程痛点:根治拖拽层级冲突、组件重叠、多选穿透、画布抖动、拖拽吸附错位问题
- 代码生成优化:业界少见的「去冗余、去模板废话、精简AST生成」方案,导出可直接上线的生产级Vue代码
- 完整闭环:组件拖拽 → 属性编辑 → 图层管理 → Schema存储 → 一键生成Vue源码 → 预览渲染
- 稀缺深度:市面教程只做拖拽,本文重点讲低代码DSL设计、渲染机制、代码编译优化、踩坑解决方案
目前网上99%的Vue低代码教程存在两个致命问题:
- 重度依赖第三方库:拖拽靠Sortable、渲染靠封装组件,开发者只会调用API,完全不懂低代码核心原理,无法二次定制、解决线上冲突Bug。
- 代码生成极度冗余:拖拽导出的代码满页无用属性、嵌套冗余标签、硬编码样式,完全无法直接用于生产。
真正的企业级低代码核心能力只有两个:
- 可视化拖拽编排引擎:精准控制拖拽落点、层级、选中、吸附、无冲突渲染
- 高质量代码生成引擎:将可视化DSL Schema,编译为无冗余、可维护、可直接上线的Vue3源码
本文完全避开商用低代码平台、避开第三方拖拽库,从零手写一套轻量化、可扩展、生产可用的低代码编辑器,同时解决新手高频踩坑点:拖拽重叠冲突、事件穿透、画布刷新抖动、生成代码臃肿等问题。
2.1 整体分层架构
我们手写的低代码编辑器分为四层架构,完全对标大厂低代码设计:
- 物料层:统一注册基础组件(输入框、按钮、文本、卡片),规范组件默认属性、默认样式、可编辑字段
- 拖拽引擎层:原生HTML5 Drag&Drop封装,手写落点计算、网格吸附、冲突检测、选中管理
- DSL状态层:自定义标准化页面Schema,统一存储组件位置、样式、属性、层级
- 代码编译层:遍历Schema,AST精简生成Vue3 SFC代码,自动剔除冗余属性、默认样式、空字段
2.2 自定义低代码DSL设计(核心规范)
拒绝杂乱JSON,设计高可维护性组件Schema结构,每一个组件节点规范如下:
// 单个组件DSL规范 interface ComponentSchema { id: string; // 唯一ID(防拖拽冲突、精准选中) type: string; // 组件类型:input/button/text/card label: string; // 组件展示名称 props: Record
; // 业务属性 style: Record
; // 内联样式 position: {
x: number; y: number; w: number; h: number;
}; zIndex: number; // 层级,解决拖拽重叠冲突 }
// 页面整体Schema interface PageSchema { components: ComponentSchema[]; canvas: {
width: number; height: number; grid: number; // 网格吸附粒度
} }
该规范最大优势:结构清晰、无冗余字段、可无限扩展、适配代码编译。
使用Vite+Vue3搭建轻量化工程,无多余依赖:
# 创建项目 npm create vite@latest lowcode-vue3 – –template vue cd lowcode-vue3 npm install
仅安装必要依赖,零冗余
npm install uuid
不使用任何第三方拖拽库,基于原生 dragstart/drag/dragend/drop 手写引擎,彻底解决:
- 组件拖拽层级重叠冲突
- 画布频繁刷新DOM抖动
- 拖拽落点偏移、吸附不准
- 选中事件穿透、多选冲突
4.1 全局状态管理(CompositionAPI 状态中心)
新建 src/store/useLowcodeStore.ts
import { ref, reactive } from ‘vue’ import { v4 as uuidv4 } from ‘uuid’
// 网格吸附粒度 export const GRID_SIZE = 8
// 页面Schema状态 export const pageSchema = reactive({ canvas: {
width: 800, height: 600, grid: GRID_SIZE
}, components: [] })
// 当前选中组件ID export const selectId = ref(“)
// 生成唯一组件ID export const generateComponentId = () => uuidv4()
// 清空选中 export const clearSelect = () => { selectId.value = ” }
// 设置选中组件 export const setSelect = (id: string) => { selectId.value = id }
4.2 物料组件注册表(统一规范默认属性)
新建 src/config/material.ts,统一管理所有可拖拽组件的默认属性、样式、尺寸,杜绝拖拽生成冗余属性。
export interface MaterialItem { type: string label: string defaultProps: Record
defaultStyle: Record
defaultSize: { w: number; h: number } }
// 基础物料库 export const materialList: MaterialItem[] = [ {
type: 'input', label: '输入框', defaultProps: { placeholder: '请输入内容', modelValue: '' }, defaultStyle: { width: '100%', fontSize: '14px' }, defaultSize: { w: 200, h: 36 }
}, {
type: 'button', label: '按钮', defaultProps: { text: '按钮', type: 'primary' }, defaultStyle: { padding: '6px 16px', fontSize: '14px' }, defaultSize: { w: 80, h: 36 }
}, {
type: 'text', label: '文本', defaultProps: { text: '默认文本内容' }, defaultStyle: { fontSize: '14px', color: '#333' }, defaultSize: { w: 160, h: 32 }
}, {
type: 'card', label: '卡片', defaultProps: { title: '卡片标题' }, defaultStyle: { border: '1px solid #eee', borderRadius: '4px', padding: '12px' }, defaultSize: { w: 300, h: 180 }
} ]
// 根据组件类型获取默认配置 export const getMaterialDefault = (type: string) => { return materialList.find(item => item.type === type)! }
4.3 拖拽核心逻辑(手写落点计算+网格吸附+冲突规避)
新建 src/hooks/useDragCanvas.ts,封装拖拽核心逻辑,解决所有拖拽工程问题。
import { ref } from ‘vue’ import { pageSchema, generateComponentId, GRID_SIZE } from ‘../store/useLowcodeStore’ import from ‘../config/material’
export const canvasRef = ref
// 拖拽开始:记录当前拖拽组件类型 export function handleDragStart(type: string) { dragType.value = type }
// 拖拽覆盖:阻止默认行为,允许放置 export function handleDragOver(e: DragEvent) { e.preventDefault() }
// 核心放置逻辑:落点精准计算 + 网格吸附 + 防重叠抖动 export function handleDrop(e: DragEvent) ,
style: { ...material.defaultStyle }, position: { x: snapX, y: snapY, w: material.defaultSize.w, h: material.defaultSize.h }, zIndex: pageSchema.components.length + 1 // 新组件层级最高,防被遮挡
}
// 插入组件 pageSchema.components.push(newComponent) dragType.value = ” }
4.4 组件选中逻辑(解决事件穿透、多选冲突)
新建 src/hooks/useSelect.ts
import from ‘../store/useLowcodeStore’
// 画布空白处点击清空选中 export function handleCanvasClick(e: MouseEvent) }
// 组件点击选中(阻止冒泡,解决穿透) export function handleComponentClick(e: MouseEvent, id: string)
// 判断组件是否选中 export function isActive(id: string) { return selectId.value === id }
主页面 src/App.vue,完整实现低代码编辑器布局与核心交互。
组件物料
{{ item.label }}
属性配置
// 获取当前选中组件 const currentComp = computed(() => { return pageSchema.components.find(item => item.id === selectId.value) })
6.1 组件渲染组件 ComponentItem.vue
动态渲染不同类型组件,添加选中边框,解决层级重叠、选中高亮冲突。
class="canvas-component" :class="{ active: isActive }" :style="componentStyle" @click="$emit('click')"
>
{{ schema.props.text }}
{{ schema.props.title }}
卡片内容区域
const props = defineProps<{ schema: ComponentSchema isActive: boolean }>()
// 合并位置尺寸样式,精准定位 const componentStyle = computed(() => ({ position: ‘absolute’, left: props.schema.position.x + ‘px’, top: props.schema.position.y + ‘px’, width: props.schema.position.w + ‘px’, height: props.schema.position.h + ‘px’, zIndex: props.schema.zIndex }))
6.2 属性编辑面板 AttrEditor.vue
实时修改组件属性、样式,双向绑定更新Schema,无冗余刷新。
const props = defineProps<{ schema: ComponentSchema }>
// 文本属性适配多组件 const targetText = computed(, set(val) })
// 字体大小快捷修改 const fontSize = computed(, set(val) {
props.schema.style.fontSize = val + 'px'
} })
市面90%低代码教程生成的代码存在大量默认属性、空样式、冗余嵌套、硬编码无用字段。本节手写精简型代码生成引擎,实现:
- 自动剔除默认属性、默认样式(只保留用户修改项)
- 删除空值、空样式、无效参数
- 生成标准、整洁、可直接上线的Vue3 SFC代码
- 格式化缩进,无乱码、无冗余换行
新建 src/utils/codeGenerator.ts
import { pageSchema } from ‘../store/useLowcodeStore’ import from ‘../config/material’
// 过滤冗余属性:只保留用户修改后的非默认值 function filterDiffProps(type: string, nowProps: Record
// 过滤冗余样式:剔除默认样式、空样式 function filterDiffStyle(type: string, nowStyle: Record
// 样式对象转内联字符串 function styleToString(style: Record
${k}:${v}).join(‘;’) }
// 生成单个组件模板代码 function genComponentCode(comp: any): string “: ''}${styleStr ? style=”${styleStr}”: ''} />
case 'button': return `
${comp.props.title}
default: return ''
} }
// 生成完整Vue SFC代码(无冗余、生产可用) export function generateVueCode() { // 生成所有组件模板 const templateNodes = pageSchema.components.map(comp => {
return ` ${genComponentCode(comp)}`
}).join(’ ‘)
const template =
${templateNodes}
const script =
const style =
// 拼接完整代码 return `${template}
${script}
${style}` }
在 App.vue 中新增导出按钮和弹窗,实现一键生成、查看、复制优化后的纯净代码。
对应脚本逻辑:
import { generateVueCode } from ‘./utils/codeGenerator’ import { ElMessage } from ‘element-plus’ // 可选,自行替换提示
const showCode = ref(false) const codeText = ref(“)
watch(showCode, (val) => })
// 复制代码 const copyCode = async () => { await navigator.clipboard.writeText(codeText.value) ElMessage.success(‘复制成功’) }
9.1 拖拽高频Bug解决方案
- 拖拽落点偏移:未计算画布相对视口坐标,改用
getBoundingClientRect精准计算偏移 - 组件重叠遮挡:新增组件zIndex自增,新组件永远置顶
- 选中穿透:组件点击事件添加
stopPropagation,阻止冒泡 - 画布抖动:固定画布尺寸、使用绝对定位,避免DOM回流重绘
- 吸附不准:实现网格取整吸附算法,强制对齐8px网格
9.2 代码生成冗余问题根治方案
- 冗余属性:对比组件默认配置,仅导出用户修改后的差异化属性
- 空样式泛滥:过滤空字符串、默认样式,不生成无效style字段
- 模板臃肿:动态按需拼接标签,不写固定冗余结构
- 默认值残留:所有默认参数自动剔除,保证代码极简
9.3 渲染性能优化点
- 使用
computed缓存组件样式,避免频繁计算 - 精准单向更新Schema,不做全量刷新
- 唯一ID精准绑定组件,避免DOM复用错乱
- 样式、属性差异化更新,最小化DOM变动
本文完全脱离第三方低代码、拖拽库依赖,纯Vue3原生手写实现了一套轻量化、可落地、可扩展的低代码编辑器,覆盖:物料拖拽、网格吸附、选中管理、属性编辑、Schema存储、高质量精简代码生成全链路。
区别于全网入门教程,本文核心价值在于工程化问题解决:根治拖拽冲突、渲染抖动、代码冗余三大行业痛点,生成的代码可直接投入生产,无黑盒、无冗余、完全可控。
后续可扩展方向:
- 新增组件缩放、拖拽移动、删除、复制功能
- 支持组件嵌套、层级管理、图层显隐
- 支持Schema导入导出、页面模板保存
- 基于AST实现更精细的代码压缩、逻辑代码注入
- 实现手写代码反向解析为低代码DSL
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/282964.html