vue.js - Vue.js react 性如何在幕后工作?

标签 vue.js vuejs2 vue-component

基本上,当我有一个组件时,我们称它为“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 源代码,有两个非常好的资源可用于此主题:
  • Vue 掌握 Advanced component类(class)
  • FrontendMaster的Advanced Vue.js Features from the Ground Up by Evan You

  • 如果您对简单虚拟 DOM 实现的内部结构感到好奇,请查看 Jason Yu 的博客文章。

    Building a Simple Virtual DOM from Scratch

    关于vue.js - Vue.js react 性如何在幕后工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54476619/

    相关文章:

    vue.js - 访问兄弟中 v-for 循环的索引? (VueJS)

    javascript - 如何获取组件中的列表值?

    vue.js - vue组件注册和路由

    vue.js - Vue - 从子组件关闭对话框

    vue.js - 如何为 Vue Apollo 导入 Apollo Client 3?

    javascript - 如何将 Prop 传递给组件的故事?

    vue.js - Keep-alive 在单个路由上而不是路由器 View 中的所有路由

    vue.js - VueJS和数据表

    vuejs2 - 暂存构建配置 - 带有开发数据库的生产代码

    javascript - Vue.js 命名为 javascript 钩子(Hook)