[译]JavaScript响应式的最佳解释
栏目: JavaScript · 发布时间: 7年前
内容简介:原文地址:许多前端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应用程序来说:
Vue 只是知道如果
price
改变,它应该做三件事:
- 更新网页上
price的值。 - 重新计算乘法表达式
price * quantity,并更新页面。 - 再次调用函数
totalPriceWithTax并更新页面。 但是等等,你是否会好奇,Vue如何知道price更改时所要更新的内容,以及它如何跟踪所有内容? 这并不是JavaScript通常的工作方式
我们解决一个大问题是通常不会这样编程。例如,如果我运行如下代码:
你觉得它打印什么?由于我们没有使用Vue,它将打印出来 10 。
在Vue中,我们希望 total 随着 price 或 quantity 更新而更新。我们想要:
不幸的是,JavaScript是程序性的,而不是响应式的,所以这在现实中不起作用。为了实现 total 响应,我们必须使用JavaScript来使事情表现得与众不同。
:warning: 问题
我们需要保存如何计算得到 total ,这样可以在 price 或 quantity 更新时重新运行它,
:white_check_mark: 解决方案
首先,需要一些方法告诉我们的应用程序,“我即将运行的代码, 存储它 ,我可能需要在其他时间运行它。”然后我们开始运行代码,如果 price 或 quantity 变量得到更新,再次运行存储的代码。
我们可以通过记录函数来执行此操作,以便我们可以再次运行它。
target 变量中存储了一个匿名函数,然后调用一个
record
函数。使用ES6箭头语法我也可以这样写:
record
函数定义很简单:
target (在我们的例子中
{ total = price * quantity } ),所以我们可以稍后运行它,可以通过一个
replay
函数来运行存储的所有内容。
storage
数组中存储的所有匿名函数。 然后在代码中,我们可以:
很简单吧?如果您需要阅读并尝试再次理解它,这里有完整的代码,仅供参考,如果您想知道原因,我会以特定的方式对此进行编码。
:warning: 问题
我们可以根据需要继续记录 target ,但是有一个更强大的解决方案可以扩展我们的应用程序。一个负责维护 target 列表的类,当需要它们重新运行时,这些 target 列表会得到通知。
:white_check_mark: 解决方案:依赖类
我们解决这个问题的一种方法是将这种行为封装到它自己的类中,这是一个实现标准观察者模式的依赖类。
因此,如果我们创建一个JavaScript类来管理我们的依赖项(它更接近Vue的处理方式),它可能看起来像这样:
subscribers 而不是
storage 。我们现在调用的函数是
depend 而不是
record ,我们现在使用
notify 而不是
replay
。为了让这个运行:
target
。
:warning: 问题
我们将为每个变量设置一个 Dep 类,并且很好地封装了创建需要监视更新的匿名函数的行为。也许一个 watcher 函数可能是为了处理这种行为。
所以不要这样调用:
(这只是上面的代码) 我们可以改为:
:white_check_mark: 解决方案:观察者函数
在我们的 Watcher 函数中,我们可以做一些简单的事情:
watcher 函数接受一个
myFunc 参数,将其设置为全局
target 属性,调用
dep.depend() 将
target 添加到订阅者
subscriber ,执行
target 函数,然后重置
target
函数。
现在,当我们运行以下内容时:
您可能想知道为什么我们将target设为全局变量,而不是将其传递到我们需要的函数中。这将在我们的文章结尾处解释。
:warning: 问题
我们有一个单独的 Dep class ,但我们真正想要的是每个变量都有自己的 Dep 。在继续之前,将数据设为对象属性。
price 和
quantity )都有自己的内部
Dep
类。
现在我们执行时:
data.price 的值,我希望
price 属性的
Dep 类将存储在
target 的匿名函数推送到其
subscriber 数组(通过调用
dep.depend() )。由于
data.quantity 被访问,我还希望
quantity 属性
Dep 类将存储在
target 的匿名函数推送到其
subscriber
数组中。
data.price 被访问,我希望它只是推送到
price 属性
Dep
类。
price 的
subscribers 什么时候调用
dep.notify() ?我希望在
price
设置时调用它们。在文章的最后,我希望能够进入控制台并执行:
price 或
quantity ),所以当它被访问时,我们可以保存
target 到我们的
subscribers 数组中,当它被更改时,运行存储在
subscribers
数组中的函数。
:white_check_mark: 解决方案:Object.defineProperty()
我们需要了解 Object.defineProperty() 函数,它是简单的ES5 JavaScript。它允许我们为属性定义 getter 和 setter 函数。在我向您展示如何在 Dep 类中使用它之前,将向您展示最基本的用法。
如您所见,它只记录两行。但是,它实际上没有 get 或 set 任何值,因为我们过度使用了该功能。我们现在加回来吧。 get() 期望返回一个值, set() 仍然需要更新一个值,所以让我们添加一个 internalValue 变量来存储我们当前的 price 值。
既然我们的 get 和 set 工作正常,您认为将打印到控制台的是什么?
get 并
set
值时,我们可以获得通知。通过一些递归,我们可以为数据数组中的所有项运行它,对吧?
Object.keys(data) 返回对象键的数组。
现在一切都有 getter 和 setter ,我们在控制台上看到了这一点。
将两种想法放在一起
price 值时,我们想要
price 记住这个匿名函数
(target) 。这样,如果
price 被更改,或者
set
为新值,它将触发此函数以重新运行,因为它知道此行依赖于它。
Get=>记住当前匿名函数,当我们的值发生变化时,会再次运行它。
Set=>运行保存的匿名函数,我们的值随之改变。
或者就我们的 Dep Class 而言
Price accessed (get)=>调用 dep.depend() 以保存当前 target
Price set=>调用 dep.notify() 给 price ,重新运行全部 targets
让我们结合这两个想法,并完成我们的最终代码。
现在看看我们执行时会发生什么。
正是我们所希望的! price 和 quantity 确实都响应了!每当值 price 或 quantity 更新时,全部的代码都会重新运行。
Vue文档中的这个插图现在应该开始有意义了。
你看到那个美丽的紫色数据圈 getters and setters 了吗?看起来应该很熟悉!每个组件实例都有一个 watcher 实例(蓝色),它从 getter (红线)收集依赖项。稍后调用 setter 时,它会 通知 watcher 导致组件重新渲染。注释之后的图如下。
是的,这现在不是更有意义吗?
显然,Vue如何做到这一点更复杂,但你现在知道了基础知识。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Design Accessible Web Sites
Jeremy Sydik / Pragmatic Bookshelf / 2007-11-05 / USD 34.95
It's not a one-browser web anymore. You need to reach audiences that use cell phones, PDAs, game consoles, or other "alternative" browsers, as well as users with disabilities. Legal requirements for a......一起来看看 《Design Accessible Web Sites》 这本书的介绍吧!