原文地址:The Best Explanation of JavaScript Reactivityjavascript
许多前端JavaScript框架(例如Angular,React和Vue)都有本身的Reactivity引擎。经过了解响应式及其工做原理,您能够提升开发技能并更有效地使用JavaScript框架。在视频和下面的文章中,咱们构建了您在Vue源代码中看到的相同类型的Reactivity。前端
当你第一次看到它时,Vue的响应式系统看起来很神奇。拿这个简单的Vue应用程序来讲: vue
Vue
只是知道若是
price
改变,它应该作三件事:
price
的值。price * quantity
,并更新页面。totalPriceWithTax
并更新页面。 可是等等,你是否会好奇,Vue如何知道price
更改时所要更新的内容,以及它如何跟踪全部内容?
咱们解决一个大问题是一般不会这样编程。例如,若是我运行以下代码: java
你以为它打印什么?因为咱们没有使用Vue,它将打印出来10
。react
在Vue中,咱们但愿total
随着price
或quantity
更新而更新。咱们想要:编程
不幸的是,JavaScript是程序性的,而不是响应式的,因此这在现实中不起做用。为了实现total
响应,咱们必须使用JavaScript来使事情表现得不同凡响。数组
咱们须要保存如何计算获得total
,这样能够在price
或quantity
更新时从新运行它,框架
首先,须要一些方法告诉咱们的应用程序,“我即将运行的代码,存储它,我可能须要在其余时间运行它。”而后咱们开始运行代码,若是price
或quantity
变量获得更新,再次运行存储的代码。 函数
target
变量中存储了一个匿名函数,而后调用一个
record
函数。使用ES6箭头语法我也能够这样写:
record
函数定义很简单:
target
(在咱们的例子中
{ total = price * quantity }
),因此咱们能够稍后运行它,能够经过一个
replay
函数来运行存储的全部内容。
storage
数组中存储的全部匿名函数。 而后在代码中,咱们能够:
咱们能够根据须要继续记录target
,可是有一个更强大的解决方案能够扩展咱们的应用程序。一个负责维护target
列表的类,当须要它们从新运行时,这些target
列表会获得通知。编码
咱们解决这个问题的一种方法是将这种行为封装到它本身的类中,这是一个实现标准观察者模式的依赖类。
所以,若是咱们建立一个JavaScript类来管理咱们的依赖项(它更接近Vue的处理方式),它可能看起来像这样:
subscribers
而不是
storage
。咱们如今调用的函数是
depend
而不是
record
,咱们如今使用
notify
而不是
replay
。为了让这个运行:
target
。
咱们将为每一个变量设置一个Dep
类,而且很好地封装了建立须要监视更新的匿名函数的行为。也许一个watcher
函数多是为了处理这种行为。
因此不要这样调用:
在咱们的Watcher
函数中,咱们能够作一些简单的事情:
watcher
函数接受一个
myFunc
参数,将其设置为全局
target
属性,调用
dep.depend()
将
target
添加到订阅者
subscriber
,执行
target
函数,而后重置
target
函数。
如今,当咱们运行如下内容时:
咱们有一个单独的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
数组中的函数。
咱们须要了解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如何作到这一点更复杂,但你如今知道了基础知识。
Dep
类来收集依赖项(depend
)并从新运行全部依赖项(notify
)。Watcher
程序来管理正在运行的代码,这些代码可能须要做为依赖项(target
)被添加。Object.defineProperty()
建立getter
和setter
。