跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
TypeScriptAI大前端

基于 Renderless 架构的 DialogBox 可缩放功能实现与 WebAgent 实践

综述由AI生成分享了基于 OpenTiny TinyVue 组件库,利用 Renderless 架构为 DialogBox 实现 resizable 功能的实战经验。通过逻辑与视图分离,提升了代码对 AI 生成的友好度。文章详细解析了拖拽缩放的数据流设计、核心函数实现(start/move/end)、内存泄漏处理及移动端兼容方案(Pointer Events)。同时探讨了 WebMCP 协议在 AI Agent 与组件交互中的应用,以及开发者在 AI 时代的角色转变与开源贡献价值。

Pythonist发布于 2026/4/6更新于 2026/5/2236 浏览
基于 Renderless 架构的 DialogBox 可缩放功能实现与 WebAgent 实践

一、缘起:为什么我要给 DialogBox 加上 resizable 能力?

作为一名在企业级应用开发一线摸爬滚打多年的前端,DialogBox 这个组件我用了不下百次。但每次用的时候,总觉得差点意思——用户想自己调整弹窗大小?不支持。

在 OpenTiny 前端智能化技术分享中,听到关于 AI Agent 和 WebMCP 的讨论时,我意识到这是一个契机。传统的组件开发模式是开发者定义好所有功能,用户被动接受。但在 AI 时代,组件应该是'可对话'的——用户说'我想把这个弹窗调大一点',AI 就能理解意图并调用相应的 API。

要实现这个愿景,首先得让组件具备足够的能力。所以,我决定从最基础的开始:为 TinyVue 的 DialogBox 组件实现真正的 resizable 功能。这不仅是功能增强,更是为未来的 WebAgent 交互打下基础。

二、实战:深度解析 Renderless 架构的开发体验

2.1 第一次扫描源码的震撼

刚开始看 TinyVue 源码时,被它的架构设计惊艳到了。

以前接触过的组件库(比如 Element、Ant Design Vue),都是把逻辑和视图混在一起的。但 TinyVue 采用了 Renderless 架构——核心思想就是逻辑和视图彻底分离。

怎么理解这个架构?

  • 传统架构就像一个厨师,既要负责炒菜(业务逻辑),又要负责摆盘(UI 渲染)。结果就是换个盘子(比如从 PC 端换到移动端)就得重新学一遍炒菜。
  • Renderless 架构则把厨师分成两个角色:
    • 逻辑厨师:只负责炒菜,不管摆盘(对应 vue.ts 文件)
    • 摆盘师傅:只负责摆盘,不会炒菜(对应 pc.vue、mobile.vue 文件)

这样做的好处是什么?AI 友好的秘密就在这里!AI 最擅长的是生成纯逻辑代码,但不太理解复杂的 HTML/CSS。Renderless 架构正好让 AI 专注于它擅长的部分,提高了 AI 生成代码的准确性。

2.2 实现 resizable 的核心思路
第一步:理解 OpenTiny 的组件结构

通过查看源码,发现 DialogBox 的文件结构是这样的:

dialog-box/
├── src/
│   ├── pc.vue # PC 端的 UI 表现
│   └── mobile.vue # 移动端的 UI 表现
├── vue.ts # 核心逻辑层(所有平台共用)
└── types.ts # 类型定义

这意味着只需要在 vue.ts 中添加逻辑,然后在 pc.vue 和 mobile.vue 中添加对应的 UI 句柄,就能实现跨平台的 resizable 功能。

操作如下:

环境搭建 安装 Node.js 18+ 安装 pnpm 9+

npm install -g pnpm

使用 CLI 创建低代码平台

npx @opentiny/tiny-engine-cli@latest create-platform
cd <project-name>
pnpm install
第二步:设计 resizable 的数据流

要实现拖拽缩放,需要设计一套完整的数据流。

基于这个流程,设计了以下几个关键状态:

// 在 vue.ts 中添加的状态
resizing: , 
: , 
: , 
: , 
: , 
: , 
: , 
:  
false
// 标记是否正在缩放
resizeDirection
''
// 缩放方向(东南西北、东南、东北等 8 个方向)
startX
0
// 鼠标按下的 X 坐标
startY
0
// 鼠标按下的 Y 坐标
startWidth
0
// 初始宽度
startHeight
0
// 初始高度
minWidth
200
// 最小宽度限制
minHeight
100
// 最小高度限制
第三步:实现核心的三个函数

在 OpenTiny 的 Renderless 架构中,将 resizable 逻辑封装成了三个函数。

1. handleResizeStart(开始拖拽)

const handleResizeStart = (direction) => (event) => {
  // 1. 检查是否允许 resize(比如全屏状态下不允许)
  if (!props.resize || state.isFull) return;
  
  // 2. 记录初始状态
  state.resizing = true;
  state.resizeDirection = direction;
  state.startX = event.clientX;
  state.startY = event.clientY;
  
  // 获取当前尺寸作为基准
  // 3. 添加全局监听(这样即使鼠标移出元素也能继续跟踪)
  document.addEventListener('mousemove', handleResizeMove);
  document.addEventListener('mouseup', handleResizeEnd);
};

为什么要添加到 document 而不是 element?

如果只监听元素本身,当用户快速拖动鼠标超出元素范围时,拖拽就会中断。添加到 document 上,就能保证整个拖拽过程的连续性。

2. handleResizeMove(拖拽中)

const handleResizeMove = (event) => {
  if (!state.resizing) return;
  
  // 1. 计算鼠标移动的偏移量
  const deltaX = event.clientX - state.startX;
  const deltaY = event.clientY - state.startY;
  
  // 2. 根据拖拽方向计算新尺寸
  let newWidth = state.startWidth;
  let newHeight = state.startHeight;
  
  if (direction.includes('e')) { // 向东(右)拖拽
    newWidth = Math.max(state.minWidth, state.startWidth + deltaX);
  }
  if (direction.includes('s')) { // 向南(下)拖拽
    newHeight = Math.max(state.minHeight, state.startHeight + deltaY);
  }
  
  // ... 其他方向类似处理
  
  // 3. 直接修改 DOM 元素的样式
  dialog.style.width = `${newWidth}px`;
  dialog.style.height = `${newHeight}px`;
  
  // 4. 触发事件,让外部知道尺寸变化
  emit('resize-move', { width: newWidth, height: newHeight });
};

注意使用了 Math.max(state.minWidth, ...),这是为了防止用户把窗口缩得太小。这种边界检查在实际开发中非常重要。

3. handleResizeEnd(结束拖拽)

const handleResizeEnd = () => {
  if (!state.resizing) return;
  
  // 1. 重置状态
  state.resizing = false;
  state.resizeDirection = '';
  
  // 2. 移除监听器(非常重要!防止内存泄漏)
  document.removeEventListener('mousemove', handleResizeMove);
  document.removeEventListener('mouseup', handleResizeEnd);
  
  // 3. 触发结束事件
  emit('resize-end', { width, height });
};

为什么要移除监听器?

如果不移除,组件销毁后监听器还在,就会导致内存泄漏。

第四步:在 UI 层添加拖拽句柄

逻辑写好了,接下来要在 UI 上让用户能够操作。在 pc.vue 中添加了 8 个方向的拖拽句柄:

<template>
  <div ref="dialog">
    <!-- 原有的弹窗内容 -->
    <!-- 新增:8 个方向的拖拽句柄 -->
    <template v-if="resize && !isFull">
      <!-- 四个角 -->
      <div @mousedown="handleResizeStart('nw')"></div>
      <div @mousedown="handleResizeStart('ne')"></div>
      <div @mousedown="handleResizeStart('sw')"></div>
      <div @mousedown="handleResizeStart('se')"></div>
      <!-- 四个边 -->
      <div @mousedown="handleResizeStart('n')"></div>
      <div @mousedown="handleResizeStart('s')"></div>
      <div @mousedown="handleResizeStart('w')"></div>
      <div @mousedown="handleResizeStart('e')"></div>
    </template>
  </div>
</template>

因为逻辑和 UI 分离,可以轻松地为不同平台定制不同的 UI。比如在 PC 端显示 8 个句柄,在移动端可能只需要显示 4 个角的句柄(节省屏幕空间)。

2.3 踩坑实录:那些让我头疼的问题
坑 1:内存泄漏的教训

刚开始实现时,忘记清理事件监听器。后来添加了 onBeforeUnmount 钩子来兜底:

onBeforeUnmount(() => {
  // 确保组件销毁时清理所有监听器
  document.removeEventListener('mousemove', handleResizeMove);
  document.removeEventListener('mouseup', handleResizeEnd);
});

Vue 提供了完整的生命周期钩子,让我们能在合适的时机做清理工作。

坑 2:CSS 单位的精度问题

一开始用 offsetWidth 获取尺寸,发现会有小数位丢失的问题。解决方案是用 getComputedStyle:

const computedStyle = getComputedStyle(dialog);
const currentWidth = parseFloat(computedStyle.width); // 精确值

offsetWidth 返回的是整数(四舍五入后的像素值),而 getComputedStyle 返回的是精确的计算值。在连续拖拽的场景中,这种精度丢失会累积,导致明显的抖动。

坑 3:移动端的兼容性问题

在 PC 上测试完美,一到移动端就歇菜。原因很简单:移动端没有 mouse 事件,只有 touch 事件。

最初的做法是写两套逻辑,但后来发现了一个更好的方案——Pointer Events。

Pointer Events 是一个 W3C 标准,统一了鼠标、触摸、手写笔的输入事件:

// 一套代码搞定所有输入设备
element.addEventListener('pointerdown', handlePointerDown);
element.addEventListener('pointermove', handlePointerMove);
element.addEventListener('pointerup', handlePointerUp);

这样代码量直接减少了 40%!

三、思考:从 resizable 到 WebAgent 的技术演进

3.1 为什么要做这件事?

做到这里,你可能要问:不就是给弹窗加个缩放功能吗,值得这么大费周章?

还真不是。我想做的,是为未来的 WebAgent 交互 做准备。想象一下这个场景:

用户对 AI 说:'帮我把这个弹窗调大一点,里面的表格看不全'

如果没有 resizable 能力,AI 只能束手无策。但现在,AI 可以:

  1. 通过 WebMCP 协议获取 DialogBox 的能力清单
  2. 识别到 resize 方法可用
  3. 自动调用 handleResize 或修改 width/height 属性
  4. 完成用户的指令

这就是 GenUI(生成式 UI)的核心理念:UI 不再是静态的,而是可以根据用户意图动态调整的。

3.2 WebMCP:智能体与组件的'翻译官'

通过这次实践,对 WebMCP(Model Context Protocol for Web)有了更深的理解。

简单来说,WebMCP 就是让 AI 能够理解 Web 组件的能力。每个组件需要暴露一份'能力清单':

{
  "component": "DialogBox",
  "version": "1.0.0",
  "capabilities": {
    "methods": ["open", "close", "resize"],
    "properties": {
      "width": { "type": "string", "writable": true, "description": "弹窗宽度" },
      "height": { "type": "string", "writable": true, "description": "弹窗高度" },
      "resize": { "type": "boolean", "readonly": true, "description": "是否支持缩放" },
      "isFull": { "type": "boolean", "readonly": true, "description": "是否全屏" }
    },
    "events": ["resize-move", "resize-end"]
  }
}

有了这份清单,AI 就知道可以调用哪些方法,修改哪些属性,以及有哪些限制。

3.3 Renderless 架构对 AI 友好的秘密

经过这次实战,发现 Renderless 架构 简直就是为 AI 而生的:

  1. 逻辑纯净:AI 最擅长生成纯函数式的逻辑代码,不需要理解 DOM
  2. 类型完备:TypeScript 类型定义让 AI 生成的代码更准确
  3. 职责单一:逻辑层只管状态和 API,表现层只管渲染,AI 不容易出错
  4. 易于测试:纯函数更容易编写单元测试,AI 可以自动生成测试用例

未来组件库,可能会专门为 AI 优化架构设计。

四、感悟:开发者如何在 AI 时代找到新定位?

4.1 我的角色转变

这次实践真切感受到:开发者的角色真的在变。

以前的我:

  • 花 2 小时写 CRUD 代码和表单验证
  • 花 1 小时调试事件监听的边界问题
  • 花 30 分钟写注释和文档

现在的我:

  • 花 10 分钟跟 AI 描述需求
  • 花 20 分钟审查 AI 生成的代码
  • 花 30 分钟优化架构和性能

这不是炫耀,而是实实在在的效率提升。同样一个 resizable 功能,如果完全手写,至少需要半天。但借助 AI,只用了 2 小时就完成了,而且代码质量更高。

4.2 什么应该交给 AI,什么必须自己把控?

这是我的经验总结:

✅ 放心交给 AI:

  • 样板代码(getter/setter、事件处理框架)
  • 单元测试(AI 很擅长根据代码生成测试用例)
  • 类型定义(TypeScript interface/type)
  • 代码格式化和小重构
  • 查找 Bug 的可能原因

⚠️ 需要审核:

  • 业务逻辑的正确性
  • 性能优化方案
  • 错误处理策略
  • 兼容性处理

❌ 必须自己决策:

  • 架构设计和技术选型
  • 用户体验细节
  • 安全合规问题
  • 技术债务的取舍

记住一句话:AI 是最好的副驾驶,但方向盘必须在你手里。

4.3 参与开源的真实收获

很多人问我:为什么要花时间参与 OpenTiny 开源项目?

技术层面:

  • 真正理解了 Renderless 架构的设计精髓
  • 学会了如何编写对 AI 友好的代码
  • 掌握了企业级组件库的开发规范

思维层面:

  • 从'使用者'变成'贡献者',视角完全不同
  • 开始思考组件的通用性和扩展性
  • 理解了 API 设计的重要性

职业发展:

  • 在 GitHub/AtomGit 上留下了实实在在的贡献记录
  • 认识了一群优秀的开源开发者
  • 获得了官方颁发的贡献者证书

最重要的是,感觉不是在'卷',而是在创造价值。写的每一行代码,都可能被成千上万的开发者使用;设计的每一个功能,都可能成为未来 AI 交互的基础设施。

五、展望:前端智能化的下一步

5.1 接下来的计划

这次 resizable 实践只是个开始。已经在规划下一步:

  1. 完善 resizable 功能:
    • 添加保持宽高比选项
    • 支持拖拽到边缘自动吸附
    • 添加动画过渡效果
  2. 探索 WebMCP 集成:
    • 为 DialogBox 编写 MCP Schema
    • 实现 AI 可调用的标准接口
    • 与大模型平台对接测试
  3. 输出最佳实践:
    • 写一篇《如何用 AI 高效开发组件》的教程
    • 录制一个完整的实战视频
    • 在团队内部分享经验
5.2 给同行的建议

如果想在前端智能化领域有所建树,建议是:

  1. 别观望,先动手:找个真实的开源项目,提交第一个 PR
  2. 善用 AI 工具:GitHub Copilot、Cursor、通义灵码,选一个顺手的
  3. 深入理解架构:不要满足于会用 API,要搞懂背后的设计思想
  4. 保持开放心态:新技术层出不穷,快速学习才是核心竞争力
  5. 拥抱开源社区:一个人的力量有限,一群人才能走得更远

六、写在最后

前端智能化不是口号,而是正在发生的现实。

从辅助编码到智能体自主执行,这条路可能还需要走 3 年、5 年甚至 10 年。但每一步前进,都需要我们这一代开发者去铺路。

我很庆幸,自己能参与到这场变革中来。用代码为 AI 时代的基础设施添砖加瓦,这本身就是件很酷的事情。

参考资料

项目源码:

  • OpenTiny TinyVue - DialogBox 组件
  • OpenTiny TinyEngine 低代码引擎

目录

  1. 一、缘起:为什么我要给 DialogBox 加上 resizable 能力?
  2. 二、实战:深度解析 Renderless 架构的开发体验
  3. 2.1 第一次扫描源码的震撼
  4. 2.2 实现 resizable 的核心思路
  5. 第一步:理解 OpenTiny 的组件结构
  6. 第二步:设计 resizable 的数据流
  7. 第三步:实现核心的三个函数
  8. 第四步:在 UI 层添加拖拽句柄
  9. 2.3 踩坑实录:那些让我头疼的问题
  10. 坑 1:内存泄漏的教训
  11. 坑 2:CSS 单位的精度问题
  12. 坑 3:移动端的兼容性问题
  13. 三、思考:从 resizable 到 WebAgent 的技术演进
  14. 3.1 为什么要做这件事?
  15. 3.2 WebMCP:智能体与组件的“翻译官”
  16. 3.3 Renderless 架构对 AI 友好的秘密
  17. 四、感悟:开发者如何在 AI 时代找到新定位?
  18. 4.1 我的角色转变
  19. 4.2 什么应该交给 AI,什么必须自己把控?
  20. 4.3 参与开源的真实收获
  21. 五、展望:前端智能化的下一步
  22. 5.1 接下来的计划
  23. 5.2 给同行的建议
  24. 六、写在最后
  25. 参考资料
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • GitNexus 核心引擎:架构、流程与优化实践
  • NestJS InstanceWrapper 深度解析与前端缓存架构借鉴
  • 中国人工智能大模型技术白皮书核心内容解读与学习指南
  • Qwen3-14B 本地部署与 Ollama WebUI 集成实战
  • Python 开发 MCP Server 集成 MongoDB 数据库查询服务实战
  • 10 款 AIGC 降重与论文辅助工具介绍
  • OpenClaw 接入 Telegram 机器人配置与加入群聊
  • Python 版本管理实战指南:pyenv 安装与配置详解
  • Photoshop 集成 ComfyUI AI 绘画功能指南
  • 使用 copilot-api 实现 GitHub Copilot 兼容 OpenAI 与 Anthropic 生态
  • 使用 FastAPI 和 HTML/CSS/JavaScript 构建博客系统示例
  • Studio One 7 核心功能解析:从创作到母带的一体化 DAW 工作流
  • 前端实现 PC 网站微信扫码授权登录
  • 2023 年网络安全 HW 行动蓝队面试常见问题与解答
  • Vue 3 开发实战:10 个提升效率的核心技巧
  • QoderWork:一款桌面级通用 AI Agent 助手
  • AI 驱动的小程序开发:从零构建“打工了马”实战复盘
  • 双指针算法进阶:从三角形计数到四数之和
  • 通用人工智能平台功能解析与商业化应用场景指南
  • C++ 从零实现 TCP Socket 网络工具实战

相关免费在线工具

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online