新闻中心

如何实现一个轻量级的MVVM框架

2025-11-01
浏览次数:
返回列表
实现轻量级MVVM需核心四步:1. 用Proxy劫持数据实现响应式;2. 遍历DOM解析{{}}和v-model指令;3. 编译时收集依赖,将更新函数存入watchers;4. 数据变化时触发回调,通知所有依赖更新视图。

如何实现一个轻量级的mvvm框架

实现一个轻量级的MVVM框架,核心在于数据绑定、响应式更新和视图与模型的分离。不需要依赖庞大的库,只需抓住几个关键机制:数据劫持、模板解析、依赖收集和视图更新。下面一步步说明如何从零构建一个极简但可用的 MVVM 框架。

数据劫持与响应式系统

要让数据变化能自动触发视图更新,必须监听数据的变化。在现代 J*aScript 中,可以使用 ProxyObject.defineProperty 来实现属性的 getter 和 setter 劫持。

这里以 Proxy 为例,因为它更简洁且支持数组和动态属性:

const observe = (data, callback) => { return new Proxy(data, { get(target, key, receiver) { return Reflect.get(target, key, receiver); }, set(target, key, value, receiver) { const oldValue = target[key]; const result = Reflect.set(target, key, value, receiver); if (oldValue !== value) { callback(key, value); // 通知变化 } return result; } }); };

这个函数将传入的对象包装成响应式对象,每次修改属性时都会调用回调函数。

模板解析与指令识别

MVVM 的核心是把数据渲染到 DOM。我们需要解析模板中的绑定语法,比如 {{ message }} 或自定义指令如 v-model

遍历 DOM 节点,查找文本节点中的插值表达式或元素上的指令:

const compile = (el, data, updateView) => { const childNodes = el.childNodes; childNodes.forEach(node => { if (node.nodeType === 3) { // 文本节点 const text = node.textContent; const reg = /\{\{(.+?)\}\}/g; if (reg.test(text)) { const key = RegExp.$1.trim(); updateView(node, key); // 初次更新 // 数据变化时重新执行 updateView } } else if (node.nodeType === 1) { // 元素节点 const attrs = node.attributes; Array.from(attrs).forEach(attr => { if (attr.name === 'v-model') { const key = attr.value; node.value = data[key]; node.addEventListener('input', () => { data[key] = node.value; }); updateView(node, key, true); // 绑定 input 更新 } }); } if (node.childNodes.length) { compile(node, data, updateView); // 递归子节点 } }); };

依赖收集与视图更新

当数据变化时,需要知道哪些视图依赖了该数据。可以在初始化编译时记录每个绑定对应的 DOM 节点和更新逻辑。

狼群淘客 免费开源淘宝客程序 狼群淘客 免费开源淘宝客程序

狼群淘客系统基于canphp框架进行开发,MVC结构、数据库碎片式缓存机制,使网站支持更大的负载量,结合淘宝开放平台API实现的一个淘宝客购物导航系统采用php+mysql实现,任何人都可以免费下载使用 。狼群淘客的任何代码都是不加密的,你不用担心会有任何写死的PID,不用担心你的劳动成果被窃取。

狼群淘客 免费开源淘宝客程序 0 查看详情 狼群淘客 免费开源淘宝客程序

定义一个简单的更新函数:

const updateView = (node, key, isInput = false) => { if (isInput) { node.addEventListener('input', e => { vm.$data[key] = e.target.value; }); } // 初始化和后续更新都通过此函数 const updateFn = () => { if (isInput) { node.value = vm.$data[key]; } else { node.textContent = vm.$data[key]; } }; // 立即执行一次 updateFn(); // 将更新函数注册为该 key 的订阅者(简化版) vm.$watchers[key].push(updateFn); };

在数据劫持的回调中通知所有订阅者更新:

this.$watchers = {}; Object.keys(this.$data).forEach(key => { this.$watchers[key] = []; }); // 在 observe 的 callback 中: callback = (key, value) => { this.$watchers[key].forEach(fn => fn()); };

整合成 MVVM 类

把以上逻辑封装成一个类:

class TinyMVVM { constructor(options) { this.$el = options.el; this.$data = options.data(); this.$watchers = {}; Object.keys(this.$data).forEach(key => { this.$watchers[key] = []; }); // 响应式化数据 this.$data = observe(this.$data, (key, value) => { this.$watchers[key].forEach(fn => fn()); }); // 编译模板 const el = document.querySelector(this.$el); compile(el, this.$data, (node, key, isInput) => { const updateFn = () => { if (isInput) { node.value = this.$data[key]; } else { node.textContent = this.$data[key]; } }; updateFn(); this.$watchers[key].push(updateFn); }); } }

使用方式:

new TinyMVVM({ el: '#app', data() { return { message: 'Hello Tiny MVVM!' }; } });

HTML 示例:

{{ message }}

基本上就这些。一个轻量级 MVVM 的核心就是数据劫持 + 模板解析 + 视图更新。虽然没有 Vue 那样完整的虚拟 DOM 和编译优化,但理解这些原理有助于掌握响应式框架的本质。不复杂但容易忽略细节,比如递归编译、事件绑定和依赖管理。

以上就是如何实现一个轻量级的MVVM框架的详细内容,更多请关注其它相关文章!


# 轻量  # 会昌饮料厂网络营销推广  # 上海珍岛网站建设  # 网网站内部优化知识  # 商场购物营销推广策略  # 刷关键词排名方式  # 镇江网站关键词优化案例  # 马鞍山网站推广技术  # 耳机营销推广文案简短  # 融创向新力推广营销  # 复用  # 服务端  # 新和  # 遍历  # 如何实现  # 开源  # 绑定  # 淘宝  # 回调  # 递归  # proxy  # 回调函数  # app  # node  # html  # java  # javascript  # vue  # mvvm框架  # 抖音网站设计推广方案 


相关栏目: 【 科技资讯46185 】 【 网络学院92790


相关推荐: HTML元素状态管理:根据DIV内容动态启用/禁用按钮  苹果手机如何防止被恶意App追踪  Node.js CSV 数据处理:基于字段空值条件过滤整条记录的策略  qq邮箱日历功能怎么用_创建日程与会议邀请的技巧  邮政快递包裹最新位置 邮政快递实时追踪入口  百度网盘网页版入口 百度网盘网页版官方登录网址  mcjs网页版流畅运行 mcjs低配电脑畅玩入口  sublime怎么预览Markdown渲染效果_Markdown Preview插件 for sublime教程  电脑IP地址怎么查 查看本机IP地址的几种方法  不会效仿卡普空!《铁拳》制作人澄清:不采取赛事付费|直播|  excel如何生成目录 excel一键生成工作表目录超链接  QQ官网正版登录链接 QQ在线登录入口最新  Flexbox布局实践:实现粘性导航栏与底部固定页脚  蛙漫官网漫画入口地址_蛙漫在线畅读无广告弹窗  地铁跑酷免费秒玩入口链接 地铁跑酷小游戏免费秒玩网站  在哪找SublimeJ远程工具_SFTP插件配置教程  Python多线程中正确使用sigwait处理SIGALRM信号  Shopware订单对象中获取产品自定义字段的正确方法  MAC的“快捷指令”怎么同步到iPhone_MAC利用iCloud同步所有设备的自动化指令  Lar*el的路由模型绑定怎么用_Lar*el Route Model Binding简化控制器逻辑  sublime如何只显示或隐藏特定类型文件_sublime侧边栏文件过滤  如何在复杂的电商平台中优雅地管理共享资源并确保正确重定向,使用spryker-shop/resource-share-page模块助你一臂之力  PHP URL参数传递与500错误调试指南  2026年发布! 美少女养成动作RPG《神剑少女战记》发布实机演示  mysql密码锁定怎么解锁_mysql密码锁定解锁后修改密码步骤  抖音创作助手登录入口_抖音创作辅助工具官网直达  css滚动区域卡顿如何改善_css滚动问题用will-change优化渲染  可靠CSGO开箱平台解析 CSGO开箱网合集  163邮箱登录密码 163邮箱忘记密码找回  微信网页版官方入口直达 微信网页版网页版登录使用方法  漫蛙2(台版)官方入口地址 漫蛙2(台版)正版漫画网页端  Mac怎么锁定备忘录_Mac备忘录加密设置教程  EMS快递官网app_中国邮政速递物流手机客户端  俄罗斯Yandex免登录入口_Yandex搜索引擎官网一键直达  大象笔记网页版入口 印象笔记网页版登录入口  PHP表单数据传递:如何通过隐藏输入字段获取动态ID  NRF24L01数据传输深度解析:解决大载荷接收异常与分包策略  曝R星经典之作开发图 设计简陋但信息密集!  Python模块化编程:有效管理依赖与避免循环引用  菜鸟取件码是什么怎么查 最全查询渠道汇总  sublime如何优雅地处理行尾空格_sublime自动清理多余空白字符配置  微信怎么把收藏的内容分类管理 微信收藏内容标签分类方法  Fabric Mod开发:在1.19.3+版本中正确添加自定义物品并管理物品组  KFC套餐升级怎么获取优惠代码_KFC套餐升级活动与优惠代码获取方法  在Typer应用中优雅地处理和重组任意命令行参数  大麦的“候补”是什么意思 大麦候补购票规则【详解】  汽车之家官方网站官网入口_汽车之家网页版直接进入  在WordPress中通过REST API获取BasicAuth保护的远程文章  深入理解字体排版:Adobe光学字偶距与CSS字偶距的差异与实现  CSS布局中意外空白:解决padding-top导致的顶部间距问题 

搜索