Nik*_*ili 5 vue.js vue-component vuejs2
基本上,当我有一个组件时,我们将其称为“ TransportComponenet.vue”,在该组件中,我有一个data(),而我的属性是carId,transportId。vue所做的是使这些属性具有吸气剂和吸气剂。假设在这个componenet的观点中,我输入{{carId + transportId}}并且也是{{carId * transportId}}。
据我所知,Vue会进入我的视图,查看它们,无论我有吸气剂({{carId+ transportId}}或{{carId * transportId}})都是吸气剂。因此,vue到来并将它们注册在组件的观察程序中。当我在某个地方使用诸如setter之类的设置器时this.carId = 5。Vue为此属性执行setter函数,然后重新评估先前在观察器中保存的函数(getter)。这是正确的假设吗?
我不明白的是Dep类和Watcher类之间存在什么关系?我知道他们俩都扮演着重要角色。我真的很乐意整个解释“哪件事发生在何时何地以及为什么”。
Pra*_*ant 16
反应性是状态和DOM之间的自动同步。这就是像Vue和React这样的视图库试图在其核心中完成的工作。他们以自己的方式做到这一点。
我认为Vue的反应系统有两个方面。硬币的一面是DOM更新机制。让我们先研究一下。
假设您有一个带有模板的组件,例如:
<template>
<div>{{ foo }}</div>
</template>
<script>
export default {
data() {
return {foo: 'bar'};
}
}
</script>
Run Code Online (Sandbox Code Playgroud)
该模板将转换为渲染功能。这是在使用vue-loader构建期间发生的。上面模板的渲染功能如下所示:
function anonymous(
) {
with(this){return _c('div',[_v(_s(foo))])}
}
Run Code Online (Sandbox Code Playgroud)
渲染功能在浏览器上运行,执行后返回Vnode(虚拟节点)。虚拟节点只是代表实际DOM节点(DOM节点的蓝图)的简单JavaScript对象。上面的render函数在执行时将返回如下内容:
{
tag: 'div',
children: ['bar']
}
Run Code Online (Sandbox Code Playgroud)
Vue然后根据该Vnode蓝图创建实际的DOM节点,并将其放入DOM中。
稍后,我们假设foo的值发生了变化,并且渲染函数又以某种方式再次运行。它将给出一个不同的Vnode。然后,Vue将新的Vnode与旧的Vnode进行比较,并仅将所需的必要更改修补到DOM中。
这为我们提供了一种机制,可以有效地更新DOM,以获取组件的最新状态。如果每次在组件的任何状态(数据,道具等)发生变化时调用组件的渲染函数,我们都有完整的反应系统。
这就是Vue反应性硬币的另一面出现的地方。这就是反应性吸气剂和吸气剂。
如果您还不了解Object.defineProperty API,这将是一个不错的时机。因为Vue的反应性系统依赖于此API。
TLDR;它允许我们使用自己的getter和setter函数覆盖对象的属性访问和分配。
当Vue实例化组件时,它将遍历数据和props的所有属性,并使用来重新定义它们Object.defineProperty。
实际上,它为每个data和props属性定义了getter和setter方法。这样,它将覆盖该属性的点访问(this.data.foo)和分配(this.data.foo = someNewValue)。因此,只要这两个动作发生在该属性上,我们的覆盖就会被调用。因此,我们有办法对它们进行处理。我们将稍等一下。
同样,为每个属性创建一个新的Dep()类实例。这就是所谓的Dep,因为每个数据或道具属性可以是DEP endency到组件的渲染功能。
但是首先,重要的是要知道每个组件的render函数都在watcher中被调用。因此,观察者具有与之关联的组件的render函数。Watcher也用于其他目的,但是当它监视组件的render函数时,它是render watcher。监视程序将自己分配为当前正在运行的监视程序,该监视程序可在全局范围内访问(在Dep.target静态属性中),然后运行组件的render函数。
现在我们回到反应性吸气剂和吸气剂。运行渲染功能时,将访问状态属性。例如this.data.foo。这将调用我们的getter覆盖。调用getter时,将dep.depend()被调用。这将检查是否在中分配了当前正在运行的监视程序Dep.target,如果存在,则将其指定为此dep对象的订户。dep.depend()之所以这样称呼,是因为我们要watcher依赖dep。
_______________ _______________
| | | |
| | subscribes to | |
| Watcher | --------------> | Dep |
| | | |
|_____________| |_____________|
Run Code Online (Sandbox Code Playgroud)
与...相同
_______________ _______________
| | | |
| Component | subscribes to | it's |
| render | --------------> | state |
| function | | property |
|_____________| |_____________|
Run Code Online (Sandbox Code Playgroud)
稍后,当状态属性得到更新时,将调用setter,并且关联的dep对象将新值通知其订阅者。订阅者是观察者,他们了解渲染功能,这就是当状态改变时组件渲染功能自动被调用的方式。
这使反应系统完整。我们有一种方法可以在状态改变时调用组件的render函数。一旦发生这种情况,我们就有一种有效地更新DOM的方法。
Vue通过这种方式在state属性和render函数之间创建了关系。当状态属性更改时,Vue确切知道要执行哪个渲染函数。这确实可以很好地扩展,并且基本上消除了开发人员手中的性能优化责任。无论组件树有多大,开发人员都不必担心组件的过度渲染。为了防止这种情况,React例如提供PureComponent或shouldComponentUpdate。在Vue中,这是没有必要的,因为当任何状态发生变化时,Vue都会确切地知道要重新渲染哪个组件。
但是现在我们知道了Vue如何使事物具有反应性,我们可以考虑一种优化事物的方法。假设您有一个博客文章组件。您可以从后端获取一些数据,并使用Vue组件在浏览器中显示它们。但是,无需使博客数据具有响应性,因为它很可能不会更改。在这种情况下,我们可以告诉Vue通过冻结对象来跳过使此类数据具有反应性。
export default {
data: () => ({
list: {}
}),
async created() {
const list = await this.someHttpClient.get('/some-list');
this.list = Object.freeze(list);
}
};
Run Code Online (Sandbox Code Playgroud)
Oject.freeze会禁用对象的可配置性。您无法使用再次重新定义该对象的属性Object.defineProperty。因此,Vue 跳过了此类对象的整个反应性设置工作。
此外,您自己浏览Vue源代码,关于此主题有两个非常好的资源:
如果您对简单的虚拟DOM实现的内部结构感到好奇,请查看Jason Yu的博客文章。