Vue.js是如何作到数据响应的?

许多前端JavaScript框架(例如Angular,React和Vue)都有本身的_数据相应_引擎。经过了解相应性及其工做原理,您能够提升开发技能并更有效地使用JavaScript框架。在视频和下面的文章中,咱们构建了您在Vue源代码中看到的相同类型的Reactivity。前端

若是您观看此视频而不是阅读文章,请观看系列中的下一个视频,与Vue的建立者Evan You讨论反应性和代理。vue

💡 The Reactivity System

当你第一次看到它时,Vue的_响应系统_看起来很神奇。拿这个简单的Vue应用程序:编程

不知何故,Vue只知道若是价格发生变化,它应该作三件事:数组

  • 更新咱们网页上的价格值。
  • 从新计算乘以price * quantity的表达式,并更新页面。
  • 再次调用totalPriceWithTax函数并更新页面。

可是等等,你应该会以为奇怪,当价格变化时,Vue如何知道要更新什么,以及它如何跟踪全部内容?框架

这不是JavaScript编程常规的工做方式。ide

若是你不明白,那咱们试着看看常规的JavaScript是怎么运行的。例如,若是我运行此代码:函数

你以为它打印什么?因为咱们没有使用Vue,它将打印10。编码

在Vue,咱们但愿每当价格或数量更新时,总计都会获得更新。咱们想要:idea

不幸的是,JavaScript是程序性的,而不是被动的,因此这在现实生活中不起做用。为了使数据变化获得相应,咱们必须使用JavaScript来使事情表现不一样。spa

⚠️ 问题

咱们须要保存计算总数的方式,以便在价格或数量变化时从新运行。

✅ 解决方案

首先,咱们须要一些方法告诉咱们的应用程序,“我即将运行的代码,存储它,我可能须要你在另外一个时间运行它。”而后咱们将要运行代码,若是价格或数量变量获得更新,再次运行存储的代码。

请注意,咱们在目标变量中存储了一个匿名函数,而后调用了一个记录函数。使用ES6箭头语法我也能够这样写:

请注意,咱们在目标变量中存储了一个匿名函数,而后调用了一个记录函数。使用ES6箭头语法我也能够这样写:

记录的方法:

咱们正在存储目标(在咱们的例子中是{total = price * quantity}),因此咱们能够稍后运行它。

这将遍历存储阵列中存储的全部匿名函数并执行它们中的每个。

而后在咱们的代码中,咱们能够:

很简单吧?若是您须要阅读并尝试再次掌握它,这里的代码就完整了。仅供参考,若是您想知道缘由,我会以特定的方式对此进行编码。

⚠️ 问题

咱们能够根据须要继续记录目标,可是有一个更强大的解决方案能够扩展咱们的应用程序。那就是一个负责维护目标列表的类,当咱们须要它们从新运行时,这些目标列表会获得通知。

✅ 解决方法: 使用Class

咱们能够开始解决这个问题的一种方法是将这种行为封装到它本身的_Class_中,这是一个实现标准编程观察者模式的依赖类。

所以,若是咱们建立一个JavaScript类来管理咱们的依赖项(它更接近Vue处理事物的方式),它可能看起来像这样:

让它运行:

它仍然有效,如今咱们的代码感受更可靠了。只有仍然感受有点奇怪的是target()的设置和运行。

⚠️ 问题

咱们将为每一个变量设置一个Dep类,而且很好地封装了建立须要监视更新的匿名函数的行为。也许观察者功能多是为了处理这种行为。

(这只是上面的代码)

咱们能够改成:

✅ 解决方案:观察者功能

在咱们的Watcher功能中,咱们能够作一些简单的事情:

如您所见,watcher函数接受myFunc参数,将其设置为咱们的全局目标属性,调用dep.depend()以将目标添加为订阅者,调用目标函数并重置目标。

如今,当咱们运行如下内容时:

您可能想知道为何咱们将target实现为全局变量,而不是将其传递到咱们须要的函数中。
这有一个很好的理由,这将在咱们的文章结尾处揭晓。

⚠️ 问题

咱们有一个Dep类,但咱们真正想要的是每一个变量都有本身的Dep。在咱们继续以前,先存储一下数据。

让咱们假设咱们的每一个属性(价格和数量)都有本身的内部Dep类。

当咱们运行时:

因为访问了data.price值,我但愿price属性的Dep类将咱们的匿名函数(存储在目标中)推送到其订阅者数组(经过调用dep.depend())。因为访问了data.quantity,我还但愿quantity属性Dep类将此匿名函数(存储在目标中)推送到其订阅者数组中。

若是我有另外一个匿名函数,只访问data.price,我但愿只推送到价格属性Dep类。

我何时想要在价格订阅者上调用dep.notify()?我但愿在设订价格时调用它们。在文章的最后,我但愿可以进入控制台并执行:

咱们须要一些方法来挂钩数据属性(如价格或数量),因此当它被访问时咱们能够将目标保存到咱们的订阅者数组中,当它被更改时,运行存储在咱们的订阅者数组中的函数。

✅ 解决方案:Object.defineProperty()

咱们须要了解Object.defineProperty()函数,它是简单的ES5 JavaScript。它容许咱们为属性定义getter和setter函数。在我向您展现如何在Dep类中使用它以前,先简单展现一下改函数的用法。

如您所见,它只记录两行。可是,它实际上并无获取或设置任何值,由于咱们过分使用了该功能。咱们如今加回来吧。 get()指望返回一个值,而set()仍然须要更新一个值,因此让咱们添加一个internalValue变量来存储咱们当前的价格值。

既然咱们的get和set工做正常,您认为将打印到控制台的是什么?

所以,当咱们获取并设置值时,咱们能够得到通知。经过一些递归,咱们能够为数组中的全部项运行它

FYI,Object.keys(data)返回对象键的数组。

如今一切都有getter和setter,咱们在控制台上看到了这一点。

🛠 Putting both ideas together

当像这样的一段代码运行并得到价格的价值时,咱们但愿价格记住这个匿名函数(目标)。这样,若是价格变化,或者设置为新值,它将触发此函数以从新运行,由于它知道此行依赖于它。因此你能够这样想。

Get =>记住这个匿名函数,当咱们的值发生变化时,咱们会再次运行它。

Set =>运行保存的匿名函数,咱们的值刚改变。

或者就咱们的Dep Class而言

Price accessed (get) => 调用dep.depend()来保存当前目标

Price set => 在价格上调用dep.notify(),从新运行全部目标

让咱们结合这两个想法,并完成咱们的最终代码。

如今看看会发生什么。

正是咱们所但愿的!价格和数量都确实是获得了实时的响应的!只要价格或数量的价值获得更新,咱们的总代码就会从新运行。

Vue文档中的这个插图如今应该开始有意义了。

你看到那个漂亮的紫色数据圈了吗?看起来应该很眼熟!每一个组件实例都有一个从getter(红线)收集依赖项的服务观察器实例(蓝色)。当稍后调用设置程序时,它会通知监视程序,它将致使组件从新呈现。下面是我本身的一些注释的图片。

是的,如今是否是以为更有意义了。

显然,Vue作的可能更复杂更惊喜,但你如今知道了基础知识。

⏪ 总结:因此咱们学了什么?

  • 如何建立一个Dep类来收集依赖项(依赖)并从新运行全部依赖项(notify)。
  • 如何建立一个观察程序来管理咱们正在运行的代码,这些代码可能须要做为依赖项添加(target)。
  • 如何使用Object.defineProperty()建立getter和setter。
相关文章
相关标签/搜索