许多前端JavaScript框架(例如Angular,React和Vue)都有自己的数据相应引擎。通过了解相应性及其工作原理,您可以提高开发技能并更有效地使用JavaScript框架。在视频和下面的文章中,我们构建了您在Vue源代码中看到的相同类型的Reactivity。
如果您观看此视频而不是阅读文章,请观看系列中的下一个视频,与Vue的创建者Evan You讨论反应性和代理。
💡 The Reactivity System
当你第一次看到它时,Vue的响应系统看起来很神奇。拿这个简单的Vue应用程序:
不知何故,Vue只知道如果价格发生变化,它应该做三件事:
更新我们网页上的价格值。
重新计算乘以price * quantity的表达式,并更新页面。
再次调用totalPriceWithTax函数并更新页面。
但是等等,你应该会觉得奇怪,当价格变化时,Vue如何知道要更新什么,以及它如何跟踪所有内容?
这不是JavaScript编程常规的工作方式。
如果你不明白,那我们试着看看常规的JavaScript是怎么运行的。例如,如果我运行此代码:
你觉得它打印什么?由于我们没有使用Vue,它将打印10。
在Vue,我们希望每当价格或数量更新时,总计都会得到更新。我们想要:
不幸的是,JavaScript是程序性的,而不是被动的,所以这在现实生活中不起作用。为了使数据变化得到相应,我们必须使用JavaScript来使事情表现不同。
⚠️ 问题
我们需要保存计算总数的方式,以便在价格或数量变化时重新运行。
✅ 解决方案
首先,我们需要一些方法告诉我们的应用程序,“我即将运行的代码,存储它,我可能需要你在另一个时间运行它。”然后我们将要运行代码,如果价格或数量变量得到更新,再次运行存储的代码。
请注意,我们在目标变量中存储了一个匿名函数,然后调用了一个记录函数。使用ES6箭头语法我也可以这样写:
请注意,我们在目标变量中存储了一个匿名函数,然后调用了一个记录函数。使用ES6箭头语法我也可以这样写:
记录的方法:
我们正在存储目标(在我们的例子中是{total = price * quantity}),所以我们可以稍后运行它。
这将遍历存储阵列中存储的所有匿名函数并执行它们中的每一个。
然后在我们的代码中,我们可以:
很简单吧?如果您需要阅读并尝试再次掌握它,这里的代码就完整了。仅供参考,如果您想知道原因,我会以特定的方式对此进行编码。
⚠️ 问题
我们可以根据需要继续记录目标,但是有一个更强大的解决方案可以扩展我们的应用程序。那就是一个负责维护目标列表的类,当我们需要它们重新运行时,这些目标列表会得到通知。
✅ 解决方法: 使用Class
我们可以开始解决这个问题的一种方法是将这种行为封装到它自己的Class中,这是一个实现标准编程观察者模式的依赖类。
因此,如果我们创建一个JavaScript类来管理我们的依赖项(它更接近Vue处理事物的方式),它可能看起来像这样:
让它运行:
它仍然有效,现在我们的代码感觉更可靠了。只有仍然感觉有点奇怪的是target()的设置和运行。
⚠️ 问题
我们将为每个变量设置一个Dep类,并且很好地封装了创建需要监视更新的匿名函数的行为。也许观察者功能可能是为了处理这种行为。
(这只是上面的代码)
我们可以改为:
✅ 解决方案:观察者功能
在我们的Watcher功能中,我们可以做一些简单的事情:
如您所见,watcher函数接受myFunc参数,将其设置为我们的全局目标属性,调用dep.depend()以将目标添加为订阅者,调用目标函数并重置目标。
现在,当我们运行以下内容时:
您可能想知道为什么我们将target实现为全局变量,而不是将其传递到我们需要的函数中。这有一个很好的理由,这将在我们的文章结尾处揭晓。
⚠️ 问题
我们有一个Dep类,但我们真正想要的是每个变量都有自己的Dep。在我们继续之前,先存储一下数据。
让我们假设我们的每个属性(价格和数量)都有自己的内部Dep类。
当我们运行时: