当前位置:首页 > VUE

手动实现vue视图

2026-01-18 12:27:38VUE

手动实现 Vue 视图的核心逻辑

Vue 的核心是数据驱动的视图渲染,通过响应式系统和虚拟 DOM 实现高效更新。以下是手动实现简化版 Vue 视图的关键步骤:

响应式数据绑定

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

手动实现vue视图

function observe(data) {
  if (!data || typeof data !== 'object') return;
  Object.keys(data).forEach(key => {
    let value = data[key];
    const dep = new Dep(); // 依赖收集器
    observe(value); // 递归劫持嵌套对象
    Object.defineProperty(data, key, {
      get() {
        Dep.target && dep.addSub(Dep.target); // 收集当前 Watcher
        return value;
      },
      set(newVal) {
        if (newVal === value) return;
        value = newVal;
        observe(newVal); // 新值为对象时继续劫持
        dep.notify(); // 触发更新
      }
    });
  });
}

依赖收集与发布订阅

实现 Dep(依赖收集器)和 Watcher(观察者):

class Dep {
  constructor() {
    this.subs = [];
  }
  addSub(sub) {
    this.subs.push(sub);
  }
  notify() {
    this.subs.forEach(sub => sub.update());
  }
}

class Watcher {
  constructor(vm, exp, cb) {
    this.vm = vm;
    this.exp = exp;
    this.cb = cb;
    Dep.target = this;
    this.value = this.get(); // 触发 getter 收集依赖
    Dep.target = null;
  }
  get() {
    return this.exp.split('.').reduce((obj, key) => obj[key], this.vm);
  }
  update() {
    const newValue = this.get();
    if (newValue !== this.value) {
      this.cb(newValue);
      this.value = newValue;
    }
  }
}

模板编译

将模板转换为渲染函数,解析指令和插值表达式:

手动实现vue视图

function compile(el, vm) {
  const fragment = document.createDocumentFragment();
  let child;
  while (child = el.firstChild) {
    fragment.appendChild(child);
  }

  function replace(node) {
    const reg = /\{\{(.*?)\}\}/g;
    if (node.nodeType === 3) {
      const text = node.textContent;
      if (reg.test(text)) {
        const exp = RegExp.$1.trim();
        new Watcher(vm, exp, value => {
          node.textContent = text.replace(reg, value);
        });
        node.textContent = text.replace(reg, vm[exp]);
      }
      return;
    }
    if (node.nodeType === 1) {
      Array.from(node.attributes).forEach(attr => {
        if (attr.name.startsWith('v-')) {
          const exp = attr.value;
          if (attr.name === 'v-model') {
            node.value = vm[exp];
            new Watcher(vm, exp, value => {
              node.value = value;
            });
            node.addEventListener('input', e => {
              vm[exp] = e.target.value;
            });
          }
        }
      });
    }
    Array.from(node.childNodes).forEach(replace);
  }

  replace(fragment);
  el.appendChild(fragment);
}

虚拟 DOM 与 Diff 算法

实现简化版虚拟 DOM 和差异比对:

function createElement(tag, props, children) {
  return { tag, props, children };
}

function diff(oldNode, newNode) {
  if (oldNode.tag !== newNode.tag) {
    return node => {
      const newEl = document.createElement(newNode.tag);
      oldNode.el.parentNode.replaceChild(newEl, oldNode.el);
      return newEl;
    };
  }
  const patches = [];
  const propsPatches = diffProps(oldNode.props, newNode.props);
  if (propsPatches) {
    patches.push(node => {
      Object.entries(propsPatches).forEach(([k, v]) => {
        node.setAttribute(k, v);
      });
    });
  }
  const childrenPatches = diffChildren(oldNode.children, newNode.children);
  if (childrenPatches.length) {
    patches.push(node => {
      childrenPatches.forEach((patch, i) => {
        patch(node.childNodes[i]);
      });
    });
  }
  return node => patches.forEach(patch => patch(node));
}

初始化入口

整合以上模块实现迷你 Vue 类:

class MiniVue {
  constructor(options) {
    this.$options = options;
    this.$data = options.data();
    observe(this.$data);
    Object.keys(this.$data).forEach(key => {
      Object.defineProperty(this, key, {
        get() {
          return this.$data[key];
        },
        set(newVal) {
          this.$data[key] = newVal;
        }
      });
    });
    compile(options.el, this);
  }
}

使用示例

<div id="app">
  <input v-model="message">
  <p>{{ message }}</p>
</div>

<script>
  new MiniVue({
    el: document.getElementById('app'),
    data() {
      return { message: 'Hello Vue' };
    }
  });
</script>

关键优化方向

  1. 批量异步更新:通过 nextTick 实现更新队列
  2. 组件系统:支持嵌套组件和生命周期
  3. 指令扩展:实现 v-forv-if 等复杂指令
  4. 性能监控:添加渲染耗时统计

以上实现展示了 Vue 的核心原理,实际工程中还需处理边缘情况和性能优化。

标签: 视图vue
分享给朋友:

相关文章

vue为啥能实现监听

vue为啥能实现监听

Vue 实现监听的原理 Vue 的监听能力主要基于其响应式系统,该系统通过数据劫持和依赖收集实现。当数据变化时,系统能自动触发视图更新或执行回调函数。 核心机制:Object.definePrope…

vue实现防抖节流

vue实现防抖节流

Vue 中实现防抖(Debounce) 防抖的核心是延迟执行函数,在连续触发时取消前一次的调用,只执行最后一次。适用于输入框搜索、窗口大小调整等场景。 方法1:使用 Lodash 的 _.debo…

vue前端分页怎么实现

vue前端分页怎么实现

前端分页实现方法 在Vue中实现前端分页通常涉及数据分割和页面控制逻辑。以下是常见的实现方式: 使用计算属性分页 通过计算属性对数据进行切片处理: computed: { paginatedD…

vue实现画圆弧并着色

vue实现画圆弧并着色

在 Vue 中实现画圆弧并着色 使用 Canvas API 绘制圆弧 在 Vue 组件的 mounted 钩子中,通过 Canvas API 绘制圆弧。创建一个 canvas 元素并获取其上下文:…

vue实现上移下移插件

vue实现上移下移插件

实现上移下移功能的 Vue 插件 在 Vue 中实现列表项的上移和下移功能,可以通过自定义指令或组件封装的方式完成。以下是两种常见实现方式: 基于数组操作的通用方法 通过操作数组索引实现元素位置交…

vue实现用户信息删除

vue实现用户信息删除

Vue 实现用户信息删除功能 在 Vue 中实现用户信息删除功能通常涉及前端界面交互和后端 API 调用。以下是实现步骤: 前端界面设计 创建删除按钮或操作项,通常使用 @click 事件触发删除操…