当前位置:首页 > VUE

手动实现vue视图

2026-01-18 12:27:38VUE

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

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

响应式数据绑定

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

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;
    }
  }
}

模板编译

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

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中实现继承可以通过多种方式模拟类继承的效果,以下是常见的几种方法: 使用mixins实现逻辑复用 通过Vue的mixins机制共享组件选项,达到类似继承的效果: const BaseC…

vue 实现pc

vue 实现pc

Vue 实现 PC 端应用的核心要点 Vue.js 是一个渐进式框架,适合构建 PC 端单页应用(SPA)或复杂的前端界面。以下是实现 PC 端应用的关键方法和注意事项: 项目初始化与工程化配置 使…

vue实现选区

vue实现选区

Vue 实现选区的基本方法 在Vue中实现选区功能通常涉及DOM操作和事件处理。以下是几种常见的方法: 使用原生JavaScript的Selection API 通过window.getSelec…

vue实现app

vue实现app

Vue 实现 App 的常见方法 使用 Vue 开发移动端 App 主要有两种主流方案:混合开发(Hybrid)和原生渲染。以下是具体实现方式和工具链: 混合开发方案(WebView 嵌入) 通过…

vue实现过程

vue实现过程

Vue 的实现过程 Vue 的实现过程可以分为多个核心模块,包括响应式系统、虚拟 DOM、模板编译等。以下是 Vue 的主要实现过程分析。 响应式系统 Vue 的响应式系统基于 Object.de…

vue实现高亮

vue实现高亮

Vue 实现文本高亮的方法 在 Vue 中实现文本高亮可以通过多种方式完成,以下是几种常见的方法: 方法一:使用 v-html 指令 通过 v-html 指令可以动态插入 HTML 内容,将需…