Vue响应式原理与虚拟DOM实现步骤详细讲解

yizhihongxing

Vue响应式原理与虚拟DOM实现步骤详细讲解

1. Vue响应式原理

Vue的响应式原理核心是利用Object.defineProperty方法对数据进行拦截,当数据发生变化时,通知对应的界面进行更新。

1.1 监听对象

在Vue中对数据的监听由Observer对象负责,在Observer对象中使用Object.defineProperty方法对数据进行监听。在Observer对象的实现中,需要考虑如下几个问题:

  1. 如何递归观察对象或数组中的每一个属性。
  2. 如何判断一个变量是对象还是数组。
  3. 如何在数组中添加或删除元素时实时更新View。

示例1

我们在Vue的实例中定义一个data对象,在其中定义了两个属性,num和obj。

var vm = new Vue({
  data: {
    num: 1,
    obj: {
      name: 'John',
      age: 23
    }
  }
})

在Observer对象中,对数据进行递归监听,使得Vue能够监听到data对象的变化,并自动更新View。

function Observer(data) {
  this.data = data;
  this.observe(data);
}

Observer.prototype = {
  observe: function(data) {
    var self = this;
    if (!data || typeof data !== 'object') {
      return;
    }
    Object.keys(data).forEach(function(key) {
      self.defineReactive(data, key, data[key]);
    });
  },
  defineReactive: function(data, key, val) {
    var dep = new Dep();
    var childObj = observe(val);

    Object.defineProperty(data, key, {
      enumerable: true, /* 可枚举 */
      configurable: true, /* 可删除 */
      get: function() {
        if (Dep.target) {
          dep.depend();
        }
        return val;
      },
      set: function(newVal) {
        if (newVal === val) {
          return;
        }
        val = newVal;
        childObj = observe(newVal);
        dep.notify();
      }
    });
  }
};

function observe(value, vm) {
  if (!value || typeof value !== 'object') {
    return;
  }
  return new Observer(value);
}

function Dep() {
  this.subs = []; /* subscribers */
}

Dep.prototype = {
  addSub: function(sub) {
    this.subs.push(sub);
  },

  removeSub: function(sub) {
    var index = this.subs.indexOf(sub);
    if (index !== -1) {
      this.subs.splice(index, 1);
    }
  },

  depend: function() {
    Dep.target.addDep(this);
  },

  notify: function() {
    this.subs.forEach(function(sub) {
      sub.update();
    });
  }
};

Dep.target = null;

1.2 发布订阅模式

在Vue中,数据变化时会触发对应组件的重新渲染。而这个过程是通过订阅发布模式实现的,其中过程如下:

  1. 初始化时,Vue会为每个观察的对象创建一个Dep对象
  2. 当组件初始化完成后,Vue会对需要监听的属性(computed/watch data)强制求值,此时计算property.get
  3. Dep.target属性会被设置为当前运行中的Watcher对象
  4. 当获取到对象的值后,把Watcher对象添加到对应Dep对象的subs数组中
  5. 后续调用set方法时,observer会向dep.subs数组中的Watcher对象通知数据的更新。
  6. Watcher对象通知组件重新渲染

示例2

我们在Vue的实例中定义一个computed属性,以及一个Watch属性,其中computed属性的值将通过data.num和data.obj.age的计算得出,Watch属性会监听data.num的变化,并在num发生变化后调用回调函数。

var vm = new Vue({
  data: {
    num: 1,
    obj: {
      name: 'John',
      age: 23
    }
  },
  computed: {
    sum: function() {
      return this.num + this.obj.age;
    }
  },
  watch: {
    num: function(newVal, oldVal) {
      console.log('num changed:', newVal);
    }
  }
})

在Watcher对象中,对数据进行监听。

function Watcher(vm, expOrFn, cb) {
  this.vm = vm;
  expOrFn = expOrFn.trim();
  this.expOrFn = expOrFn;
  this.cb = cb;

  Dep.target = this;
  this.value = this.get();
  Dep.target = null;
}

Watcher.prototype = {
  update: function() {
    var value = this.get();
    var oldVal = this.value;
    if (value !== oldVal) {
      this.value = value;
      this.cb.call(this.vm, value, oldVal);
    }
  },
  get: function() {
    var value;
    try {
      value = this.vm.$data[this.expOrFn];
    } catch (e) {
      value = undefined;
    }
    return value;
  },
  addDep: function(dep) {
    dep.addSub(this);
  }
};

2. Vue虚拟DOM实现

Vue的虚拟DOM实现非常简单,其核心思想是:在操作真实DOM时,避免直接修改DOM而导致大量重排,而是采用虚拟DOM来计算出最少的需要修改的节点,并进行批量修改。

2.1 节点的类型

在Vue的虚拟DOM实现中,将真实DOM分为以下三种类型的节点:

  • 元素节点(Element): 代表一个HTML元素,比如div, p, ul等,包含标签名,属性,子节点等信息。
  • 文本节点(Text): 代表一段文本内容。
  • 占位符节点(Comment): 代表HTML注释,用于模版,介绍等。

示例3

我们假设需要把以下HTML元素渲染到页面中,其中标题部分为动态内容,其余部分为静态内容。

<div class="container">
  <h1>{{title}}</h1>
  <p>这是一个演示文本。</p>
  <ul>
    <li>测试1</li>
    <li>测试2</li>
    <li>测试3</li>
  </ul>
</div>

render函数中,我们使用虚拟DOM的方式对HTML进行解析。

function createElement(tagName, attrs, children) {
  return new VNode(tagName, attrs, children)
}

function createTextNode(text) {
  return new VText(text)
}

function createComment(text) {
  return new VComment(text)
}

function VNode(tagName, attrs, children) {
  this.type = 'Element';
  this.tagName = tagName;
  this.attrs = attrs || {};
  this.children = children || [];
}

function VText(text) {
  this.type = 'Text';
  this.text = text;
}

function VComment(text) {
  this.type = 'Comment';
  this.text = text;
}

function render(data) {
  var container = document.createElement('div');
  var titleNode = createTextNode(data.title);
  var p1 = createTextNode('这是一个演示文本。');
  var ul1 = createElement('ul', null, [
    createElement('li', null, [createTextNode('测试1')]),
    createElement('li', null, [createTextNode('测试2')]),
    createElement('li', null, [createTextNode('测试3')])
  ]);

  container.appendChild(createElement('h1', null, [titleNode]));
  container.appendChild(createElement('p', null, [p1]));
  container.appendChild(ul1);

  return container;
}

2.2 DOM的更新

在Vue的虚拟DOM实现中,更新DOM的过程发生在如下两个步骤中:

  1. 使用虚拟DOM计算出需要修改的节点
  2. 对需要修改的节点进行优化后,一次性修改DOM。

示例4

我们假设我们需要根据用户的操作在已渲染的HTML中动态更新title属性。

function update(data) {
  // 对比旧虚拟节点和新虚拟节点,得出需要更新的节点
  var patch = diff(previousVNode, newVNode);

  // 对需要更新的节点进行优化后,一次性修改DOM
  patch(rootDomNode);
}

结论

通过对响应式原理和虚拟DOM的分析,我们了解了Vue是如何实现数据的自动更新和性能的优化的。Vue的响应式原理核心是使用Object.defineProperty方法对数据进行监听,而虚拟DOM的核心思想是在操作真实DOM时,在避免直接修改DOM的前提下,采用虚拟DOM来计算出最少的需要修改的节点,并进行批量修改DOM的方式来实现性能优化。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Vue响应式原理与虚拟DOM实现步骤详细讲解 - Python技术站

(0)
上一篇 2023年5月28日
下一篇 2023年5月28日

相关文章

  • vue-cli4.0多环境配置变量与模式详解

    下面就为大家详细讲解“vue-cli4.0多环境配置变量与模式详解”的完整攻略。 1. Vue-cli4.0多环境 1.1 什么是多环境? 在Vue开发中,我们会根据不同的环境(开发环境、测试环境、生产环境等)来区分不同的代码逻辑,比如可以使用不同的API服务。因此,我们需要进行多环境的配置。 1.2 多环境配置方法 Vue-cli4.0对于多环境配置提供了…

    Vue 2023年5月28日
    00
  • Vue.js实现立体计算器

    Vue.js实现立体计算器攻略 本文将详细介绍使用Vue.js实现立体计算器的步骤。我们的目标是通过Vue.js搭建一个可交互的立体计算器,支持用户输入高度、宽度、深度等参数,计算并呈现长方体、正方体和球体的体积、表面积等信息。本攻略将包括以下步骤: 搭建基础的Vue.js环境 设计计算器UI界面 实现计算器的基本逻辑 添加计算公式 总结 1. 搭建基础的V…

    Vue 2023年5月28日
    00
  • Vue计算属性实现成绩单

    下面就让我来详细讲解“Vue计算属性实现成绩单”的完整攻略。 什么是计算属性? 计算属性是一种Vue组件中的一个特殊属性,它的值是由多个数据属性计算得到的结果。它会根据依赖的属性值自动更新,而不必手动调用函数进行更新。 在Vue组件中,我们常常需要对多个数据属性进行一些计算,比如对学生的各科成绩进行累加并计算平均分。如果使用多个watch函数来监听数据变化,…

    Vue 2023年5月28日
    00
  • VUE v-bind 数据绑定的示例详解

    以下是“VUE v-bind 数据绑定的示例详解”的完整攻略: 标题 VUE v-bind 数据绑定的示例详解 简介 v-bind是VUE.js中用于属性绑定的指令,它可以用来动态地绑定一个或多个属性到一个表达式。在本文中,我们将详细演示v-bind的使用方法,并提供两个实例来说明它的用法。 正文 基本用法 首先,我们来介绍v-bind的基本用法。例如,我们…

    Vue 2023年5月28日
    00
  • vue组件生命周期钩子使用示例详解

    Vue组件生命周期钩子使用示例详解 在Vue组件的生命周期中,每个实例会从创建、挂载、更新、销毁四个不同的阶段。在这些不同的阶段中,我们可以通过使用生命周期钩子函数来控制组件的行为。本文将详细讲解Vue组件生命周期钩子函数的使用方法,并通过示例代码介绍其具体应用。 Vue组件生命周期钩子函数 Vue组件生命周期可以分为8个阶段,每个阶段都有对应的生命周期钩子…

    Vue 2023年5月27日
    00
  • Vue3中的模板语法和vue指令

    关于Vue3的模板语法和指令,我们需要从Vue3中的模板语法和指令特性入手,分别进行详细的讲解。 Vue3中的模板语法 在Vue3中,模板语法的书写方式与Vue2大致相同,仍然使用双大括号和v-bind等指令来进行模板渲染。 双大括号 双大括号是Vue3中最基本的模板语法,它用于将数据绑定到DOM元素中。例如: <div> 双大括号绑定数据:{{…

    Vue 2023年5月29日
    00
  • vue前端通过腾讯接口获取用户ip的全过程

    下面是详细讲解“vue前端通过腾讯接口获取用户ip的全过程”的完整攻略。 一、方案选择 在进行获取用户IP的操作时,我们可以通过调用第三方API对用户IP进行定位。由于腾讯云提供了一套稳定、准确的IP定位服务,我们可以选择调用腾讯云的IP定位API来获取用户IP。 二、调用腾讯IP定位API 腾讯IP定位API提供了两个版本:HTTP版本和HTTPS版本。我…

    Vue 2023年5月28日
    00
  • Vue中的v-for列表循环示例详解

    针对“Vue中的v-for列表循环示例详解”,下面给出完整的攻略: 一、什么是v-for? v-for是Vue.js提供的一个用于循环渲染页面的指令,它可以循环遍历数据,生成对应的DOM元素,并将其渲染到页面上。 常见场景: 在数据较多的情况下,使用v-for可以更加方便的渲染数据; 使用v-for可以控制生成的DOM元素,可以动态增删改变以及数据操作等。 …

    Vue 2023年5月29日
    00
合作推广
合作推广
分享本页
返回顶部