当前位置:首页 > VUE

模拟vue实现原理

2026-02-19 21:52:24VUE

模拟 Vue 实现原理

Vue.js 的核心原理包括数据响应式、虚拟 DOM、模板编译等。以下是一个简化版的模拟实现,重点展示数据响应式和虚拟 DOM 的基本概念。

数据响应式实现

通过 Object.definePropertyProxy 实现数据劫持,监听数据变化并触发更新。

class Observer {
  constructor(data) {
    this.walk(data);
  }

  walk(data) {
    if (!data || typeof data !== 'object') return;
    Object.keys(data).forEach(key => {
      this.defineReactive(data, key, data[key]);
    });
  }

  defineReactive(obj, key, val) {
    const dep = new Dep();
    this.walk(val); // 递归处理嵌套对象
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get() {
        Dep.target && dep.addSub(Dep.target);
        return val;
      },
      set(newVal) {
        if (newVal === val) return;
        val = newVal;
        dep.notify(); // 通知所有订阅者更新
      }
    });
  }
}

class Dep {
  constructor() {
    this.subs = [];
  }

  addSub(sub) {
    this.subs.push(sub);
  }

  notify() {
    this.subs.forEach(sub => sub.update());
  }
}

Dep.target = null; // 全局唯一 Watcher

虚拟 DOM 与 Diff 算法

虚拟 DOM 是一个轻量级的 JavaScript 对象,描述真实 DOM 结构。Diff 算法用于高效更新 DOM。

class VNode {
  constructor(tag, data, children, text) {
    this.tag = tag;
    this.data = data;
    this.children = children;
    this.text = text;
  }
}

function createElement(tag, data, children) {
  return new VNode(tag, data, children, undefined);
}

function createTextNode(text) {
  return new VNode(undefined, undefined, undefined, text);
}

function patch(oldVnode, vnode) {
  if (!oldVnode) {
    // 首次渲染
    return createElm(vnode);
  } else {
    // Diff 更新
    if (oldVnode.tag === vnode.tag) {
      const el = (vnode.el = oldVnode.el);
      updateProps(el, oldVnode.data, vnode.data);
      updateChildren(el, oldVnode.children, vnode.children);
    } else {
      // 替换节点
      const parentEl = oldVnode.el.parentNode;
      parentEl.removeChild(oldVnode.el);
      parentEl.appendChild(createElm(vnode));
    }
  }
}

function createElm(vnode) {
  if (vnode.text) {
    return document.createTextNode(vnode.text);
  }
  const el = document.createElement(vnode.tag);
  updateProps(el, {}, vnode.data);
  vnode.children.forEach(child => {
    el.appendChild(createElm(child));
  });
  vnode.el = el;
  return el;
}

模板编译

将模板字符串转换为渲染函数,通常涉及解析、优化和代码生成三个阶段。

function compile(template) {
  const ast = parse(template); // 解析为 AST
  optimize(ast); // 静态节点标记
  const code = generate(ast); // 生成渲染函数代码
  return new Function(`with(this){return ${code}}`);
}

// 示例:简单插值解析
function parse(template) {
  const tokens = template.split(/(\{\{\w+\}\})/);
  const children = [];
  tokens.forEach(token => {
    if (token.match(/\{\{\w+\}\}/)) {
      const key = token.slice(2, -2);
      children.push({
        type: 'interpolation',
        key
      });
    } else if (token.trim()) {
      children.push({
        type: 'text',
        value: token
      });
    }
  });
  return { children };
}

整合 Watcher 与更新机制

Watcher 作为观察者,连接响应式数据和视图更新。

模拟vue实现原理

class Watcher {
  constructor(vm, expOrFn, cb) {
    this.vm = vm;
    this.getter = parsePath(expOrFn);
    this.cb = cb;
    this.value = this.get();
  }

  get() {
    Dep.target = this;
    const value = this.getter.call(this.vm, this.vm);
    Dep.target = null;
    return value;
  }

  update() {
    const oldValue = this.value;
    this.value = this.get();
    this.cb.call(this.vm, this.value, oldValue);
  }
}

function parsePath(path) {
  const segments = path.split('.');
  return function(obj) {
    for (let i = 0; i < segments.length; i++) {
      if (!obj) return;
      obj = obj[segments[i]];
    }
    return obj;
  };
}

完整流程示例

class MiniVue {
  constructor(options) {
    this.$options = options;
    this._data = options.data;
    new Observer(this._data);
    this._proxyData();
    this.$compile = compile(options.template);
    new Watcher(this, 'render', this._update.bind(this));
  }

  _proxyData() {
    Object.keys(this._data).forEach(key => {
      Object.defineProperty(this, key, {
        get() {
          return this._data[key];
        },
        set(newVal) {
          this._data[key] = newVal;
        }
      });
    });
  }

  render() {
    return this.$compile.call(this);
  }

  _update() {
    const vnode = this.render();
    patch(this._vnode, vnode);
    this._vnode = vnode;
  }
}

以上代码展示了 Vue 核心原理的简化实现,实际 Vue 源码更为复杂,包含更多优化和边界处理。

标签: 原理vue
分享给朋友:

相关文章

vue懒加载实现难吗

vue懒加载实现难吗

vue懒加载的实现难度 Vue懒加载的实现并不复杂,核心逻辑是通过动态导入(Dynamic Imports)和路由配置或组件异步加载完成。以下是具体实现方法: 路由懒加载实现 在Vue Router…

vue实现贴图

vue实现贴图

Vue 实现贴图功能 在Vue中实现贴图功能通常涉及将图片动态添加到指定位置或元素上。以下是几种常见实现方式: 使用v-bind动态绑定图片 通过数据驱动的方式动态更换图片源: <templ…

vue实现计算

vue实现计算

Vue 实现计算的方法 在 Vue 中实现计算功能可以通过多种方式完成,主要包括计算属性(computed)、方法(methods)和侦听器(watch)。以下是具体的实现方法: 计算属性(Comp…

vue实现模糊

vue实现模糊

Vue实现模糊搜索的方法 在Vue中实现模糊搜索功能通常需要结合输入框和列表渲染,通过监听输入内容动态过滤数据。以下是几种常见实现方式: 使用计算属性实现 计算属性适合处理需要响应式更新的搜索逻辑:…

vue怎么实现onclick

vue怎么实现onclick

在Vue中实现点击事件(类似原生onclick)可以通过多种方式完成,以下是常见方法: 使用 v-on 指令 Vue提供了v-on指令绑定DOM事件,简写为@。例如绑定点击事件: <butt…

vue实现菜单定位

vue实现菜单定位

实现菜单定位的方法 在Vue中实现菜单定位功能,可以通过监听滚动事件或使用Intersection Observer API来判断当前显示的菜单项,并高亮对应的导航链接。以下是几种常见的实现方式:…