JavaScript响应式开发及其工作原理最佳解释

2018-09-2812:20:11WEB前端开发Comments2,353 views字数 3228阅读模式

许多前端JavaScript框架(例如Angular,React和Vue)都有自己的Reactivity引擎。通过了解响应式及其工作原理,您可以提高开发技能并更有效地使用JavaScript框架。在视频和下面的文章中,我们构建了您在Vue源代码中看到的相同类型的Reactivity。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

?响应式系统

当你第一次看到它时,Vue的响应式系统看起来很神奇。拿这个简单的Vue应用程序来说:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释
JavaScript响应式开发及其工作原理最佳解释

不知何故Vue只是知道如果price改变,它应该做三件事:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

  • 更新网页上price的值。
  • 重新计算乘法表达式price * quantity,并更新页面。
  • 再次调用函数totalPriceWithTax并更新页面。 但是等等,你是否会好奇,Vue如何知道price更改时所要更新的内容,以及它如何跟踪所有内容?
    JavaScript响应式开发及其工作原理最佳解释

    这并不是JavaScript通常的工作方式

我们解决一个大问题是通常不会这样编程。例如,如果我运行如下代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

你觉得它打印什么?由于我们没有使用Vue,它将打印出来10文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

在Vue中,我们希望total随着pricequantity更新而更新。我们想要:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

不幸的是,JavaScript是程序性的,而不是响应式的,所以这在现实中不起作用。为了实现total响应,我们必须使用JavaScript来使事情表现得与众不同。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

⚠ 问题

我们需要保存如何计算得到total,这样可以在pricequantity更新时重新运行它,文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

✅ 解决方案

首先,需要一些方法告诉我们的应用程序,“我即将运行的代码,存储它,我可能需要在其他时间运行它。”然后我们开始运行代码,如果pricequantity变量得到更新,再次运行存储的代码。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

我们可以通过记录函数来执行此操作,以便我们可以再次运行它。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

请注意,我们在target变量中存储了一个匿名函数,然后调用一个record函数。使用ES6箭头语法我也可以这样写:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

这个record函数定义很简单:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

我们正在存储target(在我们的例子中{ total = price * quantity }),所以我们可以稍后运行它,可以通过一个replay函数来运行存储的所有内容。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

这将遍历执行storage 数组中存储的所有匿名函数。 然后在代码中,我们可以:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

很简单吧?如果您需要阅读并尝试再次理解它,这里有完整的代码,仅供参考,如果您想知道原因,我会以特定的方式对此进行编码。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释
JavaScript响应式开发及其工作原理最佳解释

⚠ 问题

我们可以根据需要继续记录target,但是有一个更强大的解决方案可以扩展我们的应用程序。一个负责维护target列表的类,当需要它们重新运行时,这些target列表会得到通知。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

✅ 解决方案:依赖类

我们解决这个问题的一种方法是将这种行为封装到它自己的类中,这是一个实现标准观察者模式的依赖类。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

因此,如果我们创建一个JavaScript类来管理我们的依赖项(它更接近Vue的处理方式),它可能看起来像这样:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

请注意,我们现在存储匿名函数是subscribers而不是storage。我们现在调用的函数是depend而不是record,我们现在使用notify而不是replay。为了让这个运行:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

它仍然有效,现在的代码感觉更可重用。唯一仍然感觉有点奇怪的是设置和运行target文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

⚠ 问题

我们将为每个变量设置一个Dep类,并且很好地封装了创建需要监视更新的匿名函数的行为。也许一个watcher函数可能是为了处理这种行为。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

所以不要这样调用:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

(这只是上面的代码) 我们可以改为:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

✅ 解决方案:观察者函数

在我们的Watcher函数中,我们可以做一些简单的事情:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

如您所见,该watcher函数接受一个myFunc参数,将其设置为全局target属性,调用dep.depend()target添加到订阅者subscriber,执行target函数,然后重置target函数。现在,当我们运行以下内容时:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释
JavaScript响应式开发及其工作原理最佳解释

您可能想知道为什么我们将target设为全局变量,而不是将其传递到我们需要的函数中。这将在我们的文章结尾处解释。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

⚠ 问题

我们有一个单独的Dep class,但我们真正想要的是每个变量都有自己的Dep。在继续之前,将数据设为对象属性。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

假设每个属性(pricequantity)都有自己的内部Dep类。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

现在我们执行时:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

由于访问了data.price的值,我希望price属性的Dep类将存储在target的匿名函数推送到其subscriber 数组(通过调用dep.depend())。由于data.quantity被访问,我还希望quantity属性Dep类将存储在target的匿名函数推送到其subscriber 数组中。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

如果我有另一个匿名函数,只是data.price被访问,我希望它只是推送到price属性Dep类。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

pricesubscribers什么时候调用dep.notify()?我希望在price设置时调用它们。在文章的最后,我希望能够进入控制台并执行:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

我们需要一些方式来挂钩数据属性(如pricequantity),所以当它被访问时,我们可以保存target到我们的 subscribers数组中,当它被更改时,运行存储在 subscribers数组中的函数。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

✅ 解决方案:Object.defineProperty()

我们需要了解Object.defineProperty()函数,它是简单的ES5 JavaScript。它允许我们为属性定义gettersetter函数。在我向您展示如何在Dep类中使用它之前,将向您展示最基本的用法。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释
JavaScript响应式开发及其工作原理最佳解释

如您所见,它只记录两行。但是,它实际上没有getset任何值,因为我们过度使用了该功能。我们现在加回来吧。get()期望返回一个值,set()仍然需要更新一个值,所以让我们添加一个internalValue变量来存储我们当前的price值。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

既然我们的getset工作正常,您认为将打印到控制台的是什么?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

因此,当我们getset值时,我们可以获得通知。通过一些递归,我们可以为数据数组中的所有项运行它,对吧?Object.keys(data)返回对象键的数组。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

现在一切都有gettersetter,我们在控制台上看到了这一点。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

? 将两种想法放在一起

JavaScript响应式开发及其工作原理最佳解释

当像这样的一段代码运行并getprice值时,我们想要price记住这个匿名函数(target)。这样,如果price被更改,或者set为新值,它将触发此函数以重新运行,因为它知道此行依赖于它。Get =>记住当前匿名函数,当我们的值发生变化时,会再次运行它。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

Set =>运行保存的匿名函数,我们的值随之改变。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

或者就我们的Dep Class而言文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

Price accessed (get) =>调用dep.depend()以保存当前target文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

Price set =>调用dep.notify()price,重新运行全部targets文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

让我们结合这两个想法,并完成我们的最终代码。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

现在看看我们执行时会发生什么。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

正是我们所希望的!pricequantity确实都响应了!每当值pricequantity更新时,全部的代码都会重新运行。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

Vue文档中的这个插图现在应该开始有意义了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

你看到那个美丽的紫色数据圈getters and setters了吗?看起来应该很熟悉!每个组件实例都有一个watcher实例(蓝色),它从getter(红线)收集依赖项。稍后调用setter时,它会通知watcher导致组件重新渲染。注释之后的图如下。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

JavaScript响应式开发及其工作原理最佳解释

是的,这现在不是更有意义吗?文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

显然,Vue如何做到这一点更复杂,但你现在知道了基础知识。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

⏪ 那么我们学到了什么?

  • 如何创建一个Dep类来收集依赖项(depend)并重新运行所有依赖项(notify)。
  • 如何创建一个Watcher程序来管理正在运行的代码,这些代码可能需要作为依赖项(target)被添加。
  • 如何使用Object.defineProperty()创建gettersetter

作者:sunshine杨小咩
来源:掘金文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/5907.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/gcs/5907.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定