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

前端面试宝典---vue实现简化版

实现思路说明:

Vue 构造函数:

保存传入的配置选项和 data。
对 data 进行响应式处理,使用 Object.defineProperty 将 data 中的属性转换为 getter/setter。
创建 Compile 实例进行模板编译。
执行挂载钩子函数。

Dep 类:

用于收集依赖(Watcher 实例),并在数据变化时通知所有依赖更新。

Watcher 类:

订阅数据变化,当数据变化时调用回调函数。
在创建时触发一次 getter,将自己添加到 Dep 中。

Compile 类:

将真实 DOM 转换为文档片段,便于操作。
编译文档片段,处理指令和事件。
为每个绑定的数据创建 Watcher 实例,实现数据的响应式更新。

源码

// Vue 构造函数
class Vue {constructor(options) {// 保存传入的配置选项this.$options = options;// 保存 data 选项this.$data = options.data;// 对 data 进行响应式处理this.observe(this.$data);// 创建一个虚拟 DOM 实例new Compile(options.el, this);// 执行挂载钩子函数if (options.mounted) {options.mounted.call(this);}}// 对对象进行响应式处理observe(obj) {if (!obj || typeof obj !== 'object') {return;}// 遍历对象的每个属性Object.keys(obj).forEach(key => {// 对每个属性进行响应式转换this.defineReactive(obj, key, obj[key]);// 将属性代理到 Vue 实例上this.proxyData(key);});}// 定义响应式属性defineReactive(obj, key, val) {// 创建一个 Dep 实例,用于收集依赖const dep = new Dep();// 递归处理子对象this.observe(val);Object.defineProperty(obj, key, {enumerable: true,configurable: true,get() {// 如果当前有正在收集依赖的 Watcher,则将其添加到 Dep 中if (Dep.target) {dep.addSub(Dep.target);}return val;},set(newVal) {if (newVal === val) {return;}val = newVal;// 通知所有依赖更新dep.notify();}});}// 将 data 中的属性代理到 Vue 实例上proxyData(key) {Object.defineProperty(this, key, {get() {return this.$data[key];},set(newVal) {this.$data[key] = newVal;}});}
}// Dep 类,用于收集依赖和通知更新
class Dep {constructor() {// 存储所有依赖(Watcher 实例)this.subs = [];}// 添加依赖addSub(sub) {this.subs.push(sub);}// 通知所有依赖更新notify() {this.subs.forEach(sub => sub.update());}
}// Watcher 类,用于订阅数据变化
class Watcher {constructor(vm, expOrFn, cb) {this.vm = vm;this.cb = cb;this.expOrFn = expOrFn;// 保存当前 Watcher 实例到 Dep.targetDep.target = this;// 获取初始值,触发依赖收集this.value = this.get();Dep.target = null;}// 获取值get() {return this.vm[this.expOrFn];}// 更新操作update() {const oldValue = this.value;this.value = this.get();// 调用回调函数,传入新值和旧值this.cb.call(this.vm, this.value, oldValue);}
}// 模板编译类
class Compile {constructor(el, vm) {// 获取根元素this.$el = document.querySelector(el);this.$vm = vm;if (this.$el) {// 将真实 DOM 转换为文档片段this.$fragment = this.node2Fragment(this.$el);// 编译文档片段this.compile(this.$fragment);// 将编译后的文档片段插入到根元素中this.$el.appendChild(this.$fragment);}}// 将真实 DOM 转换为文档片段node2Fragment(el) {const frag = document.createDocumentFragment();let child;while (child = el.firstChild) {frag.appendChild(child);}return frag;}// 编译文档片段compile(el) {const childNodes = el.childNodes;Array.from(childNodes).forEach(node => {if (this.isElementNode(node)) {// 编译元素节点this.compileElement(node);} else if (this.isTextNode(node)) {// 编译文本节点this.compileText(node);}if (node.childNodes && node.childNodes.length > 0) {// 递归编译子节点this.compile(node);}});}// 编译元素节点compileElement(node) {const nodeAttrs = node.attributes;Array.from(nodeAttrs).forEach(attr => {const attrName = attr.name;const exp = attr.value;if (this.isDirective(attrName)) {const dir = attrName.substring(2);// 调用对应的指令处理函数this[dir] && this[dir](node, this.$vm, exp);}if (this.isEventDirective(attrName)) {const dir = attrName.substring(1);// 处理事件指令this.eventHandler(node, this.$vm, exp, dir);}});}// 编译文本节点compileText(node) {const reg = /\{\{(.*)\}\}/;const text = node.textContent;if (reg.test(text)) {const exp = RegExp.$1;// 更新文本节点内容this.update(node, this.$vm, exp, 'text');}}// 更新节点内容update(node, vm, exp, dir) {const updaterFn = this[dir + 'Updater'];updaterFn && updaterFn(node, vm[exp]);// 创建 Watcher 实例,订阅数据变化new Watcher(vm, exp, function (value) {updaterFn && updaterFn(node, value);});}// 文本更新函数textUpdater(node, value) {node.textContent = value;}// 输入框值更新函数modelUpdater(node, value) {node.value = value;}// v-text 指令处理函数text(node, vm, exp) {this.update(node, vm, exp, 'text');}// v-model 指令处理函数model(node, vm, exp) {this.update(node, vm, exp, 'model');node.addEventListener('input', e => {vm[exp] = e.target.value;});}// 事件处理函数eventHandler(node, vm, exp, dir) {const fn = vm.$options.methods && vm.$options.methods[exp];if (dir && fn) {node.addEventListener(dir, fn.bind(vm));}}// 判断是否为元素节点isElementNode(node) {return node.nodeType === 1;}// 判断是否为文本节点isTextNode(node) {return node.nodeType === 3;}// 判断是否为指令isDirective(attr) {return attr.indexOf('v-') === 0;}// 判断是否为事件指令isEventDirective(attr) {return attr.indexOf('@') === 0;}
}
http://www.xdnf.cn/news/165637.html

相关文章:

  • PCL点云处理之基于SAC-IA和ICP的点云配准完整流程(二百四十七)
  • 2025.04.26-美团春招笔试题-第一题
  • java中的Selector详解
  • Qt开发:QSettings的介绍和使用
  • JDK环境变量
  • 备忘录模式 (Memento Pattern)
  • Java 自定义TCP协议:【特点编码字符串<=>字节<=>特点编码16进制】16进制字符串和编码的转换 (各种编码通过字节向16进制的互转)| XOR计算
  • dubbo 异步化实践
  • 【MFA】✈️集成谷歌TOTP实现MFA多因素认证
  • 数组的多种声明方式:类型标注与泛型数组
  • 做大模型应用所需的一点点基础数学理论
  • 扩展和自定义 asammdf 库:满足特定需求的解决方案
  • 文章记单词 | 第46篇(六级)
  • 深度学习中的预训练与微调:从基础概念到实战应用全解析
  • Threejs中顶视图截图
  • javase和java有什么区别
  • spring响应式编程系列:异步生产数据
  • 第八课四则运算 设计运算器
  • 三维重建(二十)——思路整理与第一步的进行
  • 2025上海车展| 和芯星通发布覆盖车载全场景的产品方案
  • [Windows] 易剪媒 v0.0.8 绿色版 —— 跨平台AI批量自动剪辑视频工具
  • 罗技Flow跨电脑控制
  • 三菱PLC软元件 定时器 计数器 状态继电器 编码器
  • Volcano 进阶实战 (二) - (网络拓扑/负载感知)调度
  • 深入解析Dify中的文本清洗处理器:CleanProcessor详解
  • 玩转Pygame绘图:从简单图形到炫酷精灵
  • 构造函数有哪些种类?
  • 敦普水性低温烤漆:金属涂装80℃烘烤的防护体系
  • 牛客:BM1 反转链表
  • 利用 functools.lru_cache 优化递归算法