[译]JavaScript响应式的最佳解释

栏目: JavaScript · 发布时间: 6年前

内容简介:原文地址:许多前端JavaScript框架(例如Angular,React和Vue)都有自己的Reactivity引擎。通过了解响应式及其工作原理,您可以提高开发技能并更有效地使用JavaScript框架。在视频和下面的文章中,我们构建了您在Vue源代码中看到的相同类型的Reactivity。当你第一次看到它时,Vue的响应式系统看起来很神奇。拿这个简单的Vue应用程序来说:

原文地址: The Best Explanation of JavaScript Reactivity

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

:bulb:响应式系统

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

[译]JavaScript响应式的最佳解释
[译]JavaScript响应式的最佳解释
不知何故 Vue 只是知道如果 price

改变,它应该做三件事:

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

我们解决一个大问题是通常不会这样编程。例如,如果我运行如下代码:

[译]JavaScript响应式的最佳解释

你觉得它打印什么?由于我们没有使用Vue,它将打印出来 10

[译]JavaScript响应式的最佳解释

在Vue中,我们希望 total 随着 pricequantity 更新而更新。我们想要:

[译]JavaScript响应式的最佳解释

不幸的是,JavaScript是程序性的,而不是响应式的,所以这在现实中不起作用。为了实现 total 响应,我们必须使用JavaScript来使事情表现得与众不同。

:warning: 问题

我们需要保存如何计算得到 total ,这样可以在 pricequantity 更新时重新运行它,

:white_check_mark: 解决方案

首先,需要一些方法告诉我们的应用程序,“我即将运行的代码, 存储它 ,我可能需要在其他时间运行它。”然后我们开始运行代码,如果 pricequantity 变量得到更新,再次运行存储的代码。

[译]JavaScript响应式的最佳解释

我们可以通过记录函数来执行此操作,以便我们可以再次运行它。

[译]JavaScript响应式的最佳解释
请注意 ,我们在 target 变量中存储了一个匿名函数,然后调用一个 record

函数。使用ES6箭头语法我也可以这样写:

[译]JavaScript响应式的最佳解释
这个 record

函数定义很简单:

[译]JavaScript响应式的最佳解释
我们正在存储 target (在我们的例子中 { total = price * quantity } ),所以我们可以稍后运行它,可以通过一个 replay

函数来运行存储的所有内容。

[译]JavaScript响应式的最佳解释
这将遍历执行 storage

数组中存储的所有匿名函数。 然后在代码中,我们可以:

[译]JavaScript响应式的最佳解释

很简单吧?如果您需要阅读并尝试再次理解它,这里有完整的代码,仅供参考,如果您想知道原因,我会以特定的方式对此进行编码。

[译]JavaScript响应式的最佳解释
[译]JavaScript响应式的最佳解释

:warning: 问题

我们可以根据需要继续记录 target ,但是有一个更强大的解决方案可以扩展我们的应用程序。一个负责维护 target 列表的类,当需要它们重新运行时,这些 target 列表会得到通知。

:white_check_mark: 解决方案:依赖类

我们解决这个问题的一种方法是将这种行为封装到它自己的类中,这是一个实现标准观察者模式的依赖类。

因此,如果我们创建一个JavaScript类来管理我们的依赖项(它更接近Vue的处理方式),它可能看起来像这样:

[译]JavaScript响应式的最佳解释
请注意 ,我们现在存储匿名函数是 subscribers 而不是 storage 。我们现在调用的函数是 depend 而不是 record ,我们现在使用 notify 而不是 replay

。为了让这个运行:

[译]JavaScript响应式的最佳解释
它仍然有效,现在的代码感觉更可重用。唯一仍然感觉有点奇怪的是设置和运行 target

:warning: 问题

我们将为每个变量设置一个 Dep 类,并且很好地封装了创建需要监视更新的匿名函数的行为。也许一个 watcher 函数可能是为了处理这种行为。

所以不要这样调用:

[译]JavaScript响应式的最佳解释

(这只是上面的代码) 我们可以改为:

[译]JavaScript响应式的最佳解释

:white_check_mark: 解决方案:观察者函数

在我们的 Watcher 函数中,我们可以做一些简单的事情:

[译]JavaScript响应式的最佳解释
如您所见,该 watcher 函数接受一个 myFunc 参数,将其设置为全局 target 属性,调用 dep.depend()target 添加到订阅者 subscriber ,执行 target 函数,然后重置 target

函数。

现在,当我们运行以下内容时:

[译]JavaScript响应式的最佳解释
[译]JavaScript响应式的最佳解释

您可能想知道为什么我们将target设为全局变量,而不是将其传递到我们需要的函数中。这将在我们的文章结尾处解释。

:warning: 问题

我们有一个单独的 Dep class ,但我们真正想要的是每个变量都有自己的 Dep 。在继续之前,将数据设为对象属性。

[译]JavaScript响应式的最佳解释
假设每个属性( pricequantity )都有自己的内部 Dep

类。

[译]JavaScript响应式的最佳解释

现在我们执行时:

[译]JavaScript响应式的最佳解释
由于访问了 data.price 的值,我希望 price 属性的 Dep 类将存储在 target 的匿名函数推送到其 subscriber 数组(通过调用 dep.depend() )。由于 data.quantity 被访问,我还希望 quantity 属性 Dep 类将存储在 target 的匿名函数推送到其 subscriber

数组中。

[译]JavaScript响应式的最佳解释
如果我有另一个匿名函数,只是 data.price 被访问,我希望它只是推送到 price 属性 Dep

类。

[译]JavaScript响应式的最佳解释
pricesubscribers 什么时候调用 dep.notify() ?我希望在 price

设置时调用它们。在文章的最后,我希望能够进入控制台并执行:

[译]JavaScript响应式的最佳解释
我们需要一些方式来挂钩数据属性(如 pricequantity ),所以当它被访问时,我们可以保存 target 到我们的 subscribers 数组中,当它被更改时,运行存储在 subscribers

数组中的函数。

:white_check_mark: 解决方案:Object.defineProperty()

我们需要了解 Object.defineProperty() 函数,它是简单的ES5 JavaScript。它允许我们为属性定义 gettersetter 函数。在我向您展示如何在 Dep 类中使用它之前,将向您展示最基本的用法。

[译]JavaScript响应式的最佳解释
[译]JavaScript响应式的最佳解释

如您所见,它只记录两行。但是,它实际上没有 getset 任何值,因为我们过度使用了该功能。我们现在加回来吧。 get() 期望返回一个值, set() 仍然需要更新一个值,所以让我们添加一个 internalValue 变量来存储我们当前的 price 值。

[译]JavaScript响应式的最佳解释

既然我们的 getset 工作正常,您认为将打印到控制台的是什么?

[译]JavaScript响应式的最佳解释
因此,当我们 getset

值时,我们可以获得通知。通过一些递归,我们可以为数据数组中的所有项运行它,对吧?

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

[译]JavaScript响应式的最佳解释

现在一切都有 gettersetter ,我们在控制台上看到了这一点。

[译]JavaScript响应式的最佳解释

将两种想法放在一起

[译]JavaScript响应式的最佳解释
当像这样的一段代码运行并 get price 值时,我们想要 price 记住这个匿名函数 (target) 。这样,如果 price 被更改,或者 set

为新值,它将触发此函数以重新运行,因为它知道此行依赖于它。

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

Set=>运行保存的匿名函数,我们的值随之改变。

或者就我们的 Dep Class 而言

Price accessed (get)=>调用 dep.depend() 以保存当前 target

Price set=>调用 dep.notify()price ,重新运行全部 targets

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

[译]JavaScript响应式的最佳解释

现在看看我们执行时会发生什么。

[译]JavaScript响应式的最佳解释

正是我们所希望的! pricequantity 确实都响应了!每当值 pricequantity 更新时,全部的代码都会重新运行。

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

[译]JavaScript响应式的最佳解释

你看到那个美丽的紫色数据圈 getters and setters 了吗?看起来应该很熟悉!每个组件实例都有一个 watcher 实例(蓝色),它从 getter (红线)收集依赖项。稍后调用 setter 时,它会 通知 watcher 导致组件重新渲染。注释之后的图如下。

[译]JavaScript响应式的最佳解释

是的,这现在不是更有意义吗?

显然,Vue如何做到这一点更复杂,但你现在知道了基础知识。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

《Unity3D网络游戏实战(第2版)》

《Unity3D网络游戏实战(第2版)》

罗培羽 / 机械工业出版社 / 2019-1-1 / 89.00元

详解Socket编程,搭建稳健的网络框架;解决网游中常见的卡顿、频繁掉线等问题;探求适宜的实时同步算法。完整的多人对战游戏案例,揭秘登录注册、游戏大厅、战斗系统等模块的实现细节。 想要制作当今热门的网络游戏,特别是开发手机网络游戏,或者想要到游戏公司求职,都需要深入了解网络游戏的开发技术。本书分为三大部分,揭示网络游戏开发的细节。 第一部分“扎基础”(1-5章) 介绍TCP网络游......一起来看看 《《Unity3D网络游戏实战(第2版)》》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

URL 编码/解码
URL 编码/解码

URL 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具