奕玖科技 > 新闻中心 > 技术文章

Vue双向绑定原理 什么是vue的双向绑定

来源: 奕玖科技 瘦死的猪 | 2023/3/2 14:38:13

Vue.js是一款非常流行的前端框架,它使用了双向数据绑定技术,使得我们可以非常方便地将视图和数据进行绑定。在Vue中,我们可以通过v-model指令将表单元素和数据对象绑定在一起,当表单元素的值发生变化时,数据对象中对应的属性也会随之更新。这个双向绑定的机制是Vue.js的一个重要特性,也是它被广泛使用的原因之一。

20230205638111977195671431.jpg

本文将从Vue.js的数据响应式原理、观察者模式、依赖收集、虚拟DOM等几个方面来解析Vue的双向绑定原理,希望能对读者有所帮助。

一、数据响应式原理

Vue.js的数据响应式原理,是通过ES5的Object.defineProperty方法来实现的。它可以监测数据的变化,并在数据变化时自动更新视图。具体来说,Vue.js将每一个data中的属性转换成getter/setter,然后在getter和setter中进行依赖收集和派发更新。

我们可以通过下面这个简单的例子来了解一下Vue.js的数据响应式原理:

<p id="App">
  <p>{{ message }}</p>
</p>
Var vm = new Vue({
  el: '#app',
  data: {
    message: 'Hello, Vue!'
  }
});

在这个例子中,我们定义了一个Vue实例,并将其挂载到id为app的元素上。在data中定义了一个属性message,它的初始值为'Hello, Vue!'。在模板中使用了{{ message }}语法将message的值渲染到了页面上。

那么当我们修改message的值时,Vue.js是如何更新视图的呢?接下来我们将从getter、setter、依赖收集和派发更新四个方面来分析。

1.getter

在Vue.js中,每一个data中的属性都被转换成了getter/setter。那么什么是getter呢?getter是一个函数,它会在读取属性值时被调用。在Vue.js中,getter会负责收集依赖。

在上面的例子中,我们读取了message的值,并将它渲染到了模板中。当我们使用{{ message }}语法时,Vue.js会调用message的getter方法来获取它的值。这时候,Vue.js就会将这个依赖收集起来,以便在message的值发生变化时能够及时地更新视图。

2.setter

与getter相对应的是setter,setter会在修改属性值时被调用。在Vue.js中,setter会负责派发更新。

在上面的例子中,如果我们想要修改message的值,可以通过vm.message = 'Hello, World!'来实现。当我们修改message的值

时,Vue.js会自动调用message的setter方法,然后通过依赖收集中收集的依赖来更新视图。

3.依赖收集

在Vue.js中,依赖收集是通过一个Dep类来实现的。Dep类有两个方法:addSub和notify。其中,addSub方法用于添加依赖,notify方法用于通知依赖更新。

在getter方法中,我们会将当前的watcher对象存储到Dep类的subs数组中,以便在数据变化时能够及时地通知它们进行更新。这个watcher对象就是当前的渲染Watcher,它会在数据变化时更新视图。

4.派发更新

当我们在setter方法中修改属性值时,Vue.js会调用Dep类的notify方法来通知依赖进行更新。在notify方法中,Vue.js会遍历subs数组,然后依次调用每个watcher的update方法。这个update方法会触发渲染Watcher的重新渲染,从而更新视图。

二、观察者模式

Vue.js的数据响应式原理基于观察者模式。在观察者模式中,有两个重要的角色:观察者和被观察者。被观察者会在状态发生变化时通知观察者进行更新。

在Vue.js中,被观察者就是数据对象,而观察者则是渲染Watcher。当数据发生变化时,被观察者会通知渲染Watcher进行更新。

下面是一个简单的例子,它展示了Vue.js中观察者模式的使用:

<p id="app">
  <p>{{ message }}</p>
</p>
var data = {
  message: 'Hello, Vue!'
};
 
function observe(obj) {
  Object.keys(obj).forEach(function(key) {
    defineReactive(obj, key, obj[key]);
  });
}
 
function defineReactive(obj, key, val) {
  var dep = new Dep();
 
  Object.defineProperty(obj, key, {
    get: function() {
      if (Dep.target) {
        dep.addSub(Dep.target);
      }
      return val;
    },
    set: function(newVal) {
      val = newVal;
      dep.notify();
    }
  });
}
 
function updateView() {
  console.log('视图更新了!');
}
 
function Watcher(vm, exp, cb) {
  this.cb = cb;
  this.vm = vm;
  this.exp = exp;
  this.value = this.get();
}
 
Watcher.prototype.get = function() {
  Dep.target = this;
  var value = this.vm[this.exp];
  Dep.target = null;
  return value;
};
 
Watcher.prototype.update = function() {
  var value = this.get();
  if (this.value !== value) {
    this.value = value;
    this.cb.call(this.vm);
  }
};
 
function Dep() {
  this.subs = [];
}
 
Dep.prototype.addSub = function(sub) {
  this.subs.push(sub);
};
 
Dep.prototype.notify = function() {
  this.subs.forEach(function(sub) {
    sub.update();
  });
};
observe(data);
new Watcher
(data, 'message', updateView);
data.message = 'Hello, World!'; // 视图更新了!

在上面的例子中,我们使用了观察者模式来实现数据的响应式。在observe函数中,我们遍历了data对象的所有属性,然后对每个属性调用了defineReactive函数来实现数据的响应式。在defineReactive函数中,我们创建了一个Dep类的实例dep,并在getter方法中收集了依赖,然后在setter方法中派发更新。

在Watcher函数中,我们创建了一个渲染Watcher,并将其存储到Dep.target中。在调用get方法时,我们会触发数据的getter方法,并将当前的渲染Watcher存储到dep的subs数组中,然后返回属性值。在调用setter方法时,我们会触发数据的setter方法,并调用dep的notify方法来通知依赖进行更新。在update方法中,我们会重新获取属性值,并检查是否与之前的值相同,如果不同则调用回调函数进行更新。

三、双向绑定

除了单向绑定外,Vue.js还支持双向绑定。双向绑定指的是数据的变化可以自动同步到视图,而视图的变化也可以自动同步到数据。在Vue.js中,双向绑定是通过v-model指令来实现的。

下面是一个简单的例子,它展示了Vue.js中双向绑定的使用:

<p id="app">
  <input type="text" v-model="message">
  <p>{{ message }}</p>
</p>
var vm = new Vue({
  el: '#app',
  data: {
    message: 'Hello, Vue!'
  }
});

在上面的例子中,我们使用了v-model指令来实现双向绑定。当用户在输入框中输入文本时,数据对象的message属性会自动更新。而当数据对象的message属性发生变化时,输入框中的文本也会自动更新。

在实现双向绑定时,Vue.js使用了一个叫做v-model的指令。v-model指令会在输入框的input事件中将输入框的值同步到数据对象中。而当数据对象的值发生变化时,v-model指令会将新的值同步到输入框中。

下面是v-model指令的简单实现:

Vue.directive('model', {
  bind: function(el, binding, vnode) {
    var value = binding.value;
    var prop = el.tagName === 'INPUT' ? 'value' : 'textContent';
    el[prop] = value;
 
    el.addEventListener('input', function(event) {
      vnode.context[binding.expression] = event.target[prop];
    });
  },
  update: function(el, binding, vnode) {
    var value = binding.value;
    var prop = el.tagName === 'INPUT' ? 'value' : 'textContent';
    el[prop] = value;
  }
});

在上面的实现中,我们定义了一个名为model的指

令,并在bind方法中将输入框的值初始化为数据对象的值。然后在输入框的input事件中,我们将输入框的值同步到数据对象中。在update方法中,我们将数据对象的值同步到输入框中。这样,就实现了v-model指令的双向绑定功能。

四、虚拟DOM

在前面的例子中,我们已经看到了Vue.js是如何实现数据的响应式和双向绑定的。然而,当数据发生变化时,Vue.js需要重新渲染视图。重新渲染视图的过程涉及到大量的DOM操作,这会导致性能问题。为了解决这个问题,Vue.js使用了虚拟DOM。

虚拟DOM是一个轻量级的JavaScript对象,它描述了真实DOM的结构和属性。在Vue.js中,每当数据发生变化时,Vue.js会创建一个新的虚拟DOM树,并将其与之前的虚拟DOM树进行比较。然后,Vue.js会找出两棵树之间的差异,并根据差异更新真实的DOM树。

Vue.js中的虚拟DOM具有以下特点:

1.轻量级:虚拟DOM是一个轻量级的JavaScript对象,它不需要进行任何的DOM操作。

2.可移植性:虚拟DOM是与平台无关的,它可以在任何JavaScript环境中运行。

3.快速:由于虚拟DOM不需要进行DOM操作,因此它比直接操作DOM要快得多。

下面是一个简单的例子,它展示了Vue.js中虚拟DOM的使用:

<p id="app">
  <p>{{ message }}</p>
  <button v-on:click="changeMessage">Change Message</button>
</p>
var vm = new Vue({
  el: '#app',
  data: {
    message: 'Hello, Vue!'
  },
  methods: {
    changeMessage: function() {
      this.message = 'Hello, World!';
    }
  }
});

在上面的例子中,当用户点击按钮时,数据对象的message属性会发生变化。然后Vue.js会创建一个新的虚拟DOM树,并将其与之前的虚拟DOM树进行比较。然后,Vue.js会找出两棵树之间的差异,并根据差异更新真实的DOM树。

在Vue.js中,虚拟DOM的实现是由一个名为VNode的类来实现的。VNode类的实例包含了一个DOM节点的所有属性,例如标签名、属性、子节点等。下面是一个简单的VNode类的实现:

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

在上面的实现中,我们定义了一个VNode类,它具有tag、data、children和text四个属性。tag属性表示虚拟DOM节点的标签名,data属性表示虚拟DOM节点的属性,children属性表示虚拟DOM节点的子节点,text属性表示虚拟DOM节点的文本内容。

在Vue.js中,每个组件都有一个对应的虚拟DOM树。当组件的状态发生变化时,Vue.js会创建一个新的虚拟DOM树,并将其与之前的虚拟DOM树进行比较。然后,Vue.js会找出两棵树之间的差异,并根据差异更新真实的DOM树。

在Vue.js中,使用虚拟DOM的好处是可以提高应用程序的性能。由于虚拟DOM不需要进行任何的DOM操作,因此它比直接操作DOM要快得多。此外,Vue.js还提供了一些优化技术,例如DOM Diff算法、异步更新等,这些技术可以进一步提高应用程序的性能。

五、总结

Vue.js是一个流行的JavaScript框架,它提供了一系列强大的功能,例如数据的响应式、模板语法、组件化、双向绑定和虚拟DOM等。这些功能使得Vue.js非常适合构建现代化的Web应用程序。

在Vue.js中,数据的响应式是通过Object.defineProperty()函数和getter/setter函数实现的。这使得Vue.js可以监听数据的变化,并在数据发生变化时自动更新视图。

Vue.js的模板语法是一种类似于HTML的语法,它允许开发者将数据绑定到视图中。在Vue.js中,开发者可以使用v-bind指令将数据绑定到视图的属性中,使用v-on指令将事件绑定到视图中,使用v-if和v-for指令控制视图的渲染等。

Vue.js的组件化是一种将页面分解成小的、可重用的组件的方法。在Vue.js中,每个组件都由自己的模板、脚本和样式组成。组件可以通过props属性接收父组件传递过来的数据,通过emit方法向父组件发送消息。

Vue.js的双向绑定是通过v-model指令实现的。在Vue.js中,v-model指令将表单控件的值绑定到数据对象的属性中。当表单控件的值发生变化时,数据对象的属性也会发生变化。反之亦然。

Vue.js的虚拟DOM是一种轻量级的JavaScript对象,它描述了真实DOM的结构和属性。在Vue.js中,每当数据发生变化时,Vue.js会创建一个新的虚拟DOM树,并将其与之前的虚拟DOM树进行比较。然后,Vue.js会找出两

棵树之间的差异,并根据差异更新真实的DOM树。这种技术可以提高应用程序的性能。

Vue.js还提供了一些其他的优化技术,例如DOM Diff算法、异步更新等。DOM Diff算法是一种用于比较两个虚拟DOM树之间差异的算法,它可以避免不必要的DOM操作,从而提高应用程序的性能。异步更新是一种将DOM操作推迟到下一个事件循环周期的方法,它可以减少DOM操作的次数,从而提高应用程序的性能。

总的来说,Vue.js是一个非常优秀的JavaScript框架,它具有强大的功能和出色的性能。使用Vue.js可以轻松地构建现代化的Web应用程序,并提供优秀的用户体验。


栏目导航
相关文章
文章标签
关于我们
公司简介
企业文化
资质荣誉
服务项目
高端网站定制
微信小程序开发
SEO排名推广
新闻动态
行业新闻
技术学院
常见问题
联系我们
联系我们
人才招聘
联系方式
Q Q:24722
微信:24722
电话:13207941926
地址:江西省抚州市赣东大道融旺国际3栋
Copyright©2008-2022 抚州市奕玖科技有限公司 备案号:赣ICP备2022010182号-1