基本上,当我有一个组件时,我们称它为“TransportComponenet.vue”,在该组件中,我有一个 data(),我的属性是 carId、transportId。 vue 所做的是为这些属性创建 getter 和 setter。假设在这个组件的 View 中,我输入 {{carId + transportId}}
还有 {{carId * transportId}}
.
据我所知,Vue 进入我的 View ,查看它们,无论我有 getter( {{carId+ transportId}}
或 {{carId * transportId}}
)的地方都是 getter。所以 vue 来了,并将它们注册到组件的 watcher 中。当我在某处使用 setter 时,例如 this.carId = 5
. Vue 为此属性执行 setter 函数,并重新评估之前保存在 watcher 中的函数(getter)。这是正确的假设吗?
我不明白的是 Dep 类和 Watcher 类之间存在什么关系?我知道他们都扮演着重要的角色。我真的很尊重“什么东西去哪里,什么时候,为什么去”的整个解释。
最佳答案
react 性是状态和 DOM 之间的自动同步。这就是像 Vue 和 React 这样的 View 库试图在它们的核心中做的事情。他们以自己的方式做到这一点。
我认为 Vue 的 react 系统有两个方面。硬币的一方面是 DOM 更新机制。让我们先来看看。
假设您有一个带有模板的组件,例如:
<template>
<div>{{ foo }}</div>
</template>
<script>
export default {
data() {
return {foo: 'bar'};
}
}
</script>
这个模板被转换成渲染函数。这发生在使用 vue-loader 的构建期间.上面模板的渲染函数看起来像:
function anonymous(
) {
with(this){return _c('div',[_v(_s(foo))])}
}
渲染函数在浏览器上运行,执行时返回一个 Vnode(虚拟节点)。虚拟节点只是一个简单的 JavaScript 对象,它代表了实际的 DOM 节点,即 DOM 节点的蓝图。上面的渲染函数在执行时会返回如下内容:
{
tag: 'div',
children: ['bar']
}
然后 Vue 从这个 Vnode 蓝图中创建实际的 DOM 节点并将其放入 DOM 中。
以后再说
foo
的值发生变化,渲染函数以某种方式再次运行。它将提供不同的 Vnode。然后 Vue 将新的 Vnode 与旧的 Vnode 进行比较,并仅将必要的更改修补到 DOM 中。这为我们提供了一种机制来有效地更新 DOM,从而获取组件的最新状态。如果每次组件的渲染函数在其任何状态(数据、 Prop 等)发生变化时被调用,我们就有了完整的 react 性系统。
这就是 Vue react 性硬币的另一面。那就是 react 性 getter 和 setter。
这将是了解的好时机 Object.defineProperty API,如果您还没有意识到这一点。因为 Vue 的 react 系统依赖于这个 API。
TLDR;它允许我们使用我们自己的 getter 和 setter 函数来覆盖对象的属性访问和分配。
当 Vue 实例化你的组件时,它会遍历你 data 的所有属性。和 props并使用
Object.defineProperty
重新定义它们.它实际上做的是,它defines getters and setters对于每个数据和 Prop 属性。通过这样做,它会覆盖该属性的点访问 (this.data.foo) 和赋值 (this.data.foo = someNewValue)。因此,每当在该属性上发生这两个操作时,就会调用我们的覆盖。所以我们有一个钩子(Hook)可以对它们做些什么。我们稍后会回到这个话题。
此外,对于每个属性 new Dep()创建类实例。它叫
Dep
因为每个 data 或 props 属性都可以是 深度 组件的渲染功能。但首先,重要的是要知道每个组件的渲染函数都会被调用 within a watcher .所以观察者有一个关联组件的渲染函数。 Watcher 也用于其他目的,但是当它在监视组件的渲染功能时,它是 render watcher .观察者将自己分配为 current running watcher ,某处可全局访问(在 Dep.target 静态属性中),然后运行组件的 render function .
现在我们回到响应式(Reactive) getter 和 setter。当您运行渲染函数时,将访问状态属性。例如。
this.data.foo
.这会调用我们的 getter 覆盖。当调用 getter 时,dep.depend()
叫做。这将检查 Dep.target
中是否分配了当前正在运行的观察者,如果是,它会将该观察者分配为该 dep 对象的订阅者。它叫dep.depend()
因为我们正在制作 watcher
取决于 dep
._______________ _______________
| | | |
| | subscribes to | |
| Watcher | --------------> | Dep |
| | | |
|_____________| |_____________|
这与
_______________ _______________
| | | |
| Component | subscribes to | it's |
| render | --------------> | state |
| function | | property |
|_____________| |_____________|
稍后,当 state 属性更新时,setter 被调用,关联的 dep 对象将新值通知其订阅者。订阅者是感知渲染函数的观察者,这就是组件渲染函数在其状态改变时自动调用的方式。
这使得 react 系统完整。我们有一种方法可以在组件的状态改变时调用它的渲染函数。一旦发生这种情况,我们有一种方法可以有效地更新 DOM。
通过这种方式,Vue 在状态属性和渲染函数之间创建了关系。 Vue 确切知道当状态属性更改时要执行哪个渲染函数。这可以很好地扩展,并且基本上从开发人员手中消除了一类性能优化责任。无论组件树有多大,开发人员都无需担心组件的过度渲染。为了防止这种情况, react 例如提供 PureComponent 或 shouldComponentUpdate。在 Vue 中,这不是必需的,因为 Vue 确切地知道当任何状态发生变化时要重新渲染哪个组件。
但是现在我们知道了 Vue 是如何让事情变得响应式(Reactive)的,我们可以想办法稍微优化一下。假设您有一个博客文章组件。您从后端获取一些数据并使用 Vue 组件将它们显示在浏览器上。但是博客数据不需要是被动的,因为它很可能不会改变。在这种情况下,我们可以告诉 Vue 通过卡住对象来跳过使此类数据具有反应性。
export default {
data: () => ({
list: {}
}),
async created() {
const list = await this.someHttpClient.get('/some-list');
this.list = Object.freeze(list);
}
};
Oject.freeze除其他外,禁用对象的可配置性。您不能使用
Object.defineProperty
再次重新定义该对象的属性。 .所以 Vue skips整个 react 性设置适用于此类对象。此外,通过自己查看 Vue 源代码,有两个非常好的资源可用于此主题:
如果您对简单虚拟 DOM 实现的内部结构感到好奇,请查看 Jason Yu 的博客文章。
Building a Simple Virtual DOM from Scratch
关于vue.js - Vue.js react 性如何在幕后工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54476619/