从 Renderless 架构到 WebAgent:我的 OpenTiny 前端智能化实战之路

从 Renderless 架构到 WebAgent:我的 OpenTiny 前端智能化实战之路

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

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

直到我参加了 OpenTiny NEXT 前端智能化系列直播,听到老师讲 AI AgentWebMCP 的时候,我突然意识到:这不就是我一直在等的那个契机吗?

传统的组件开发模式是:开发者定义好所有功能,用户只能被动接受。但在 AI 时代,组件应该是"可对话"的——用户说"我想把这个弹窗调大一点",AI 就能理解意图并调用相应的 API。

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

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

2.1 第一次扫描源码的震撼

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

在这里插入图片描述


在这里插入图片描述

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

怎么理解这个架构?我用大白话解释一下:

传统架构就像一个厨师,既要负责炒菜(业务逻辑),又要负责摆盘(UI 渲染)。结果就是换个盘子(比如从 PC 端换到移动端)就得重新学一遍炒菜。

Renderless 架构则把厨师分成两个角色:

  • 逻辑厨师:只负责炒菜,不管摆盘(对应 vue.ts 文件)
  • 摆盘师傅:只负责摆盘,不会炒菜(对应 pc.vuemobile.vue 文件)

这样做的好处是什么?AI 友好的秘密就在这里

AI 最擅长的是生成纯逻辑代码(炒菜),但不太理解复杂的 HTML/CSS(摆盘)。Renderless 架构正好让 AI 专注于它擅长的部分,这就大大提高了 AI 生成代码的准确性。

2.2 实现 resizable 的核心思路

第一步:理解 OpenTiny 的组件结构

在动手之前,我得先搞清楚 TinyVue 的 DialogBox 是怎么组织的。通过查看源码,我发现它的文件结构是这样的:

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

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

这就是 OpenTiny 的第一个 Web 能力:一次开发,多端复用。

操作如下:
环境搭建
安装 Node.js 18+

安装 pnpm 9+

$ npm install -g pnpm

使用 CLI 创建低代码平台

Create low-code platform

$ npx @opentiny/tiny-engine-cli@latest create-platform

Enter the low-code platform directory

$ cd

Install dependencies

$ pnpm install

第二步:设计 resizable 的数据流

要实现拖拽缩放,我需要设计一套完整的数据流。让我用流程图来说明:

用户按下鼠标 → 记录起始位置和初始尺寸 → 监听鼠标移动 → 计算偏移量 → 更新尺寸 → 触发事件 ↓ ↓ 开始 resize 结束 resize 

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

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

为什么要记录这么多状态?

想象一下你拉橡皮筋的过程:你需要记住起点在哪里(startX/startY),橡皮筋原来的长度(startWidth/startHeight),然后才能根据当前手的位置计算出新的长度。resize 也是同样的道理。

第三步:实现核心的三个函数

在 OpenTiny 的 Renderless 架构中,我把 resizable 逻辑封装成了三个函数。这三个函数的设计思路非常清晰:

1. handleResizeStart(开始拖拽)

consthandleResizeStart=(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(拖拽中)

consthandleResizeMove=(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 })}

这里体现了 OpenTiny 的第二个 Web 能力:精确的事件控制。

注意看我使用了 Math.max(state.minWidth, ...),这是为了防止用户把窗口缩得太小。这种边界检查在实际开发中非常重要,能避免很多用户体验问题。

3. handleResizeEnd(结束拖拽)

consthandleResizeEnd=()=>{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> 

为什么是 8 个方向?

这是为了给用户最大的自由度。可以只拉宽度(东西方向)、只拉高度(南北方向),或者同时拉宽高(东南、东北等对角线方向)。每个方向的 cursor 样式也不同,给用户明确的视觉反馈。

这里体现了 OpenTiny 的第三个 Web 能力:灵活的 UI 定制。

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

2.3 踩坑实录:那些让我头疼的问题

坑 1:内存泄漏的教训

刚开始实现时,我犯了一个低级错误——忘记清理事件监听器。结果就是组件销毁后,mousemove 事件还在触发,导致内存泄漏。

后来我添加了 onBeforeUnmount 钩子来兜底:

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

OpenTiny 的第四个 Web 能力:完善的生命周期管理。

Vue 提供了完整的生命周期钩子,让我们能在合适的时机做清理工作。这是编写高质量组件的基本功。

坑 2:CSS 单位的精度问题

一开始我用 offsetWidth 获取尺寸,发现会有小数位丢失的问题。比如实际宽度是 500.7px,offsetWidth 返回 501。

解决方案是用 getComputedStyle

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

为什么会这样?

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

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

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

我最开始的做法是写两套逻辑:

// 鼠标事件 element.addEventListener('mousedown', handleMouseDown)// 触摸事件 element.addEventListener('touchstart', handleTouchStart)

但这样代码量直接翻倍!后来我发现了一个更好的方案——Pointer Events

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

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

这样代码量直接减少了 40%!这就是 OpenTiny 的第五个 Web 能力:拥抱 Web 标准。

三、思考:从 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 就知道:

  • ✅ 可以调用 resize 方法
  • ✅ 可以修改 widthheight
  • ❌ 不能在全屏状态下缩放(因为有 isFull 限制)

这才是真正的"人机对话"基础。

构建代码如下:

在这里插入图片描述

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 小时就完成了,而且代码质量更高(因为 AI 不会漏掉边界检查)。

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 时代的基础设施添砖加瓦,这本身就是件很酷的事情,不是吗?

与所有在前端智能化路上探索的朋友共勉。


参考资源

项目源码

Read more

机器人 - 关于MIT电机模式控制

目录 一、MIT电机模式简单介绍 1.1 简单介绍 1.2 MIT模式的控制参数 1.3 使用场景 二、调试时建议 2.1 调试 2.2 问题定位 一、MIT电机模式简单介绍 1.1 简单介绍 Mixed Integrated Torque为一种混合控制模式,在同一帧CAN数据里包含 位置、速度、扭矩三类的闭环指令。驱动器里面把位置环、速度环、前馈扭矩相加,得到一个参考电流,然后再交给电流环完成精准扭矩输出。 1.2 MIT模式的控制参数 参数含义取值范围(常见)说明kp位置比例系数(刚度)0 ~ 500 (单位视驱动器而定)kp = 0 时位置环失效,

74个低空无人机AI算法详解,总体精度达90%,公安执法、消防应急、水利、林业、能源电力、城建、市政、城管、工程、农业、生态

74个低空无人机AI算法详解,总体精度达90%,公安执法、消防应急、水利、林业、能源电力、城建、市政、城管、工程、农业、生态

公安执法 一、人员智能识别与管控 聚焦人员相关的身份、行为、状态识别,核心服务于治安防控、人群管理、突发事件处置,是公安基层执法的核心应用方向: 1. 人员识别/计数:支持复杂场景(人群聚集、遮挡、移动)下的人员精准检测与数量统计,实时反馈人群密度,为大型活动安保、人群聚集风险管控提供数据支撑; 2. 人员异常聚焦识别:识别人员突然聚集、徘徊、逃窜、翻越护栏等异常行为,快速锁定可疑区域,触发执法预警; 3. 打架斗殴识别:精准检测肢体冲突、推搡、殴打等暴力行为,毫秒级触发预警并定位事发位置,助力执法人员快速处置,减少冲突升级; 4. 重点人员监控识别:对接公安重点人员数据库,通过人脸识别算法实现低空移动场景下的重点人员精准匹配与轨迹追踪,支持跨区域、动态化管控; 5. 人员属性识别:识别人员性别、年龄段、衣着特征、是否携带疑似管制器具 / 大件物品等属性信息,

oicq 完整教程:10 分钟学会配置和部署你的第一个 QQ 机器人

oicq 完整教程:10 分钟学会配置和部署你的第一个 QQ 机器人 【免费下载链接】oicqTencent QQ Bot Library for Node.js 项目地址: https://gitcode.com/gh_mirrors/oi/oicq 想要快速搭建一个功能强大的 QQ 机器人吗?oicq 是一个基于 Node.js 的腾讯 QQ 机器人开发库,让你能够轻松实现自动化消息处理、群管理、好友互动等功能。本终极指南将带你从零开始,在短短 10 分钟内完成你的第一个 QQ 机器人的配置和部署!🚀 📦 前置准备与环境搭建 在开始之前,你需要确保系统已安装 Node.js v14 或更高版本。这是运行 oicq

无人机数据集汇总无人机航拍各个方面检测分割数据集合集

本数据集集合了面向无人机视觉任务的大规模、多场景、多目标标注数据资源,涵盖了地理环境、智慧城市、基础设施巡检、农业生产、公共安全与灾害监测等多个关键领域。数据主要以两种主流格式提供:适用于目标检测的VOC/YOLO格式与适用于像素级语义分割的LabelMe格式,为算法开发与模型训练提供了高度结构化的标注支持。 在地理与农业监测方面,包含田地、道路、森林、水体等地理要素的分割数据集,以及作物病害、杂草识别、农田农机、牛羊牲畜等农业目标的检测数据,支持精准农业与生态研究。智慧城市与交通领域提供了丰富的城市街道场景数据,涵盖行人、车辆、交通标志、占道经营、消防通道、广告牌等目标的检测与分割,助力城市智能化管理。基础设施巡检是另一重点,覆盖电力线、光伏板、桥梁、铁路、风力发电机等设备的缺陷与异常检测,以及工地车辆、施工人员、物料垃圾的识别,满足工业自动化巡检需求。在灾害与安全监控中,包含滑坡、洪水、火灾烟雾、河道垃圾、违规建筑等应急场景的检测与分割数据,同时提供了溺水人员、海上救援、军事目标等特殊任务的专项数据集。此外,