当前位置: 首页 > news >正文

手撸一个Vue(看不懂可以举报)

手撸一个Vue(看不懂可以举报)

作为编程讲师,我深知很多同学对Vue的响应式原理充满好奇却又望而却步。今天,我们就从零开始,一步一步“手撸”一个简化版的Vue。如果你看完还看不懂,欢迎举报我!但请相信,跟着代码走一遍,你会发现自己比想象中更强大。## 什么是Vue的核心?Vue的核心是“响应式数据”和“模板渲染”。简单来说,就是当数据变化时,视图自动更新。要实现这个,我们需要解决三个问题:1.如何监听数据变化?—— 使用Object.defineProperty(Vue2)或Proxy(Vue3)。2.如何知道哪些视图依赖了哪些数据?—— 通过“依赖收集”。3.数据变化时如何更新视图?—— 触发更新函数。我们先从最简单的版本开始,一步步完善。## 第一步:监听数据变化(基础版)我们先用Object.defineProperty实现数据监听。这个API可以拦截对象的读取和赋值操作。javascript// 监听对象的所有属性function observe(obj) { if (typeof obj !== 'object' || obj === null) return; Object.keys(obj).forEach(key => { let value = obj[key]; // 递归监听嵌套对象 observe(value); Object.defineProperty(obj, key, { get() { console.log(`读取属性 ${key}: ${value}`); return value; }, set(newVal) { if (newVal !== value) { console.log(`更新属性 ${key}: ${value} -> ${newVal}`); value = newVal; // 如果新值是对象,继续监听 observe(newVal); } } }); });}// 测试const data = { name: '小明', age: 18 };observe(data);data.name = '小红'; // 输出:更新属性 name: 小明 -> 小红console.log(data.name); // 输出:读取属性 name: 小红运行这段代码,你会发现每次读取或修改属性时,控制台都会打印信息。这就是Vue2响应式的底层基础。## 第二步:依赖收集(中级版)光监听数据还不够,我们需要知道“谁”依赖了这个数据。比如,模板中用到了{{ name }},那么当name变化时,就要重新渲染这个模板片段。这里我们引入一个“依赖管理器”——Dep(Dependency),每个属性都有一个Dep实例。当读取属性时,把当前的“观察者”(Watcher)加入依赖列表;当属性变化时,通知所有观察者更新。javascript// 依赖管理器class Dep { constructor() { this.subs = []; // 存储所有观察者 } // 添加观察者 addSub(watcher) { this.subs.push(watcher); } // 通知所有观察者 notify() { this.subs.forEach(watcher => watcher.update()); }}// 观察者(代表一个需要更新的视图片段)class Watcher { constructor(obj, key, callback) { this.obj = obj; this.key = key; this.callback = callback; // 在读取属性前,先把当前Watcher存入全局 Dep.target = this; // 触发getter,把当前Watcher加入依赖 obj[key]; Dep.target = null; } update() { console.log(`属性 ${this.key} 变化了,执行更新`); this.callback(); }}// 改造observe函数,为每个属性创建Depfunction observe(obj) { if (typeof obj !== 'object' || obj === null) return; Object.keys(obj).forEach(key => { let value = obj[key]; const dep = new Dep(); // 每个属性有自己的Dep observe(value); Object.defineProperty(obj, key, { get() { // 如果当前有Watcher在读取,就加入依赖 if (Dep.target) { dep.addSub(Dep.target); } return value; }, set(newVal) { if (newVal !== value) { value = newVal; observe(newVal); dep.notify(); // 通知所有观察者 } } }); });}// 测试const data = { name: '小明' };observe(data);// 创建一个观察者,依赖data.namenew Watcher(data, 'name', () => { console.log('视图更新:显示新名字');});data.name = '小红'; // 输出:属性 name 变化了,执行更新// 输出:视图更新:显示新名字现在,当data.name变化时,对应的观察者就会执行更新函数。这就是Vue依赖收集的雏形。## 第三步:实现微型Vue(高级版)现在,我们把上面的概念组合成一个微型Vue类。它接收一个选项对象,包含datael(挂载元素),并解析模板中的{{}}插值。javascriptclass MiniVue { constructor(options) { this.$options = options; this.$data = options.data; // 1. 监听数据 observe(this.$data); // 2. 编译模板 this.$el = document.querySelector(options.el); this.compile(this.$el); } // 编译模板,替换{{}}为数据 compile(node) { const childNodes = node.childNodes; childNodes.forEach(child => { if (child.nodeType === 3) { // 文本节点 const oldText = child.textContent; const reg = /\{\{(.*?)\}\}/g; // 匹配{{key}} if (reg.test(oldText)) { // 提取变量名 const key = RegExp.$1.trim(); // 创建观察者,数据变化时更新文本 new Watcher(this.$data, key, () => { child.textContent = oldText.replace(reg, this.$data[key]); }); // 初始化渲染 child.textContent = oldText.replace(reg, this.$data[key]); } } else if (child.nodeType === 1) { // 元素节点,递归编译 this.compile(child); } }); }}// 使用微型Vueconst app = new MiniVue({ el: '#app', data: { message: 'Hello Vue!', count: 0 }});// 3秒后修改数据,视图自动更新setTimeout(() => { app.$data.message = 'Hello MiniVue!';}, 3000);对应的HTML:html<div id="app"> <p>{{ message }}</p> <p>{{ count }}</p></div>运行这段代码,你会看到页面显示“Hello Vue!”,3秒后自动变成“Hello MiniVue!”。恭喜你,你已经手撸了一个微型Vue!## 第四步:进阶思考我们的微型Vue还有哪些不足?1.不支持数组Object.defineProperty无法监听数组索引变化,Vue2通过重写数组方法解决。2.不支持v-model等指令:真正的Vue有复杂的指令系统。3.性能优化:模板编译可以生成渲染函数,而不是直接操作DOM。4.计算属性和侦听器:需要额外的依赖管理。Vue3使用Proxy替代Object.defineProperty,可以原生监听数组和动态新增属性,性能也更好。## 总结通过本文,我们一步步实现了:-数据监听:使用Object.defineProperty拦截get/set。-依赖收集:Dep管理Watcher,在get时收集依赖,set时通知更新。-模板编译:解析{{}}插值,创建Watcher实现数据驱动视图。虽然只有100多行代码,但已经包含了Vue响应式系统的核心思想。现在你应该明白,Vue并不是什么黑魔法,而是一套优雅的设计模式。如果你跟着代码写了一遍还是不懂,欢迎举报我——但请带上你的代码,我们一起debug!编程路上,动手永远比空想更重要。下次面试官问你Vue原理,你可以自信地说:“我手撸过一个。”

http://www.jsqmd.com/news/1298660/

相关文章:

  • 大彩串口屏开发速通:从零到一实现STM32人机交互
  • 腾讯Kotlin专项面经:扩展函数原理、委托属性、Type-safe Builders、协程Scope
  • Qoder CLI:本地部署AI编程助手,平替Claude Code的完整指南
  • 网站部署实战:从Nginx手动配置到宝塔面板可视化部署
  • Unity中Jar包更新失效的根源解析与系统性解决方案
  • 单链表反转:从迭代、递归到头插、栈辅助,四种核心算法详解
  • 光伏逆变器无功优化与Matlab实现
  • 基于Hadoop大数据的电子健康信息记录分析系统的设计与实现大数据分析系统(源码+lw+部署文档+讲解等)
  • 多模态融合与细粒度OCR:解析医疗器械注册证识别核心技术
  • Xilinx 7系列FPGA配置实战:从模式选择到调试排坑全解析
  • 【AI副业成本拆解实战手册】:20年技术专家亲测的7大隐形成本与3个月回本路径
  • 抖音直播数据采集终极指南:3分钟掌握实时弹幕抓取技术
  • Windows压缩工具革命:NanaZip如何重塑你的文件管理体验
  • Word VBA批量盖章签名自动化:告别手动操作,提升办公效率
  • 基于AT89C52单片机的简易电子琴设计与Proteus仿真实现
  • 二极管核心应用电路解析:从整流、钳位到保护与检波
  • 智慧校园的数字孪生实践:从教学到管理的全面赋能
  • LangChain与LangGraph对比:LLM应用开发框架选择指南
  • 在Android Termux环境部署ALHacking安全工具集的完整指南
  • 5分钟上手Vosk:Python离线语音识别从环境配置到实战应用
  • 应急指挥中心的视频方案选型,要注意什么?
  • 酷安UWP桌面版终极指南:在Windows上畅享酷安社区的最佳体验
  • 基于Hadoop大数据的b站热门视频的数据分析与研究大数据分析系统(源码+lw+部署文档+讲解等)
  • Voronoi图核心性质剖析:从凸性、空圆到对偶性,掌握空间划分的算法基石
  • 广德市防水补漏_2026安徽东南部三省通衢城市漏水维修流程教程与五大正规团队推荐 - 雨婺虹房屋维修
  • 2026年山东离心过滤器生产厂家:技术实力、过滤精度与智能制造实力分析 - 优企名品
  • Footprint Tool 3基础操作指南:从数据导入到结果分析的完整流程
  • 练练算子优化就有机会获得40W奖金?
  • Voronoi图核心概念与性质详解:从空间划分到算法应用
  • CTF文件上传漏洞攻防:双重验证绕过与.phtml Webshell实战