Table of Contents
Dep
Watcher
Observer
Core Process
Code
Home Web Front-end Vue.js What is the responsiveness principle of Vue?

What is the responsiveness principle of Vue?

Jul 29, 2021 pm 05:42 PM
javascript vue.js front end

I have read a lot of articles on Vue principles recently. With the help of these articles, I have tried many times to understand the source code of Vue myself. Finally, I think it’s time to output the content myself. I hope I can familiarize everyone with Vue from a different perspective than other articles.

What is the responsiveness principle of Vue?

Dep

1

2

3

4

var Dep = function Dep() {

  this.id = uid++

  this.subs = []

}

Copy after login

The meaning of Dep is naturally dependency (that is, dependence, a noun in the computer field).

Just like writing a node.js program, dependencies from the npm warehouse are often used. In Vue, dependencies specifically refer to reactively processed data. As will be mentioned later, one of the key functions of reactive processing is defineReactive, which is mentioned in many Vue principle articles.

After Dep is bound to each responsive data, the responsive data will become a dependency (noun). When introducing Watcher below, it will be mentioned that responsive data may be watched, computed, or in the template. Use 3 cases of dependence (verb).

#subs

There is a subs attribute under the Dep object, which is an array. It is easy to guess that it means the subscriber list. Subscribers may be watch functions, computed functions, or view update functions.

Watcher

Watcher is the subscriber mentioned in Dep (not to be confused with the Observer observer later).

Because the function of Watcher is to respond to Dep updates in a timely manner, just like the subscription push of some apps. If you (Watcher) subscribe to certain information (Dep), you will be reminded to read when the information is updated.

deps

Similar to Dep having the subs attribute, the Watcher object also has the deps attribute. This constitutes a many-to-many relationship between Watcher and Dep. The reason for recording each other is that when one party is cleared, the related objects can be updated in time.

How to generate Watcher

The watch, computed, and rendering templates mentioned many times above generate Watcher, which are all concise and easy to understand in the Vue source code:

  • mountComponent's vm._watcher = new Watcher(vm, updateComponent, noop);
  • initComputed's watchers[key] = new Watcher(vm, getter || noop, noop, computedWatcherOptions)
  • $watcher's var watcher = new Watcher(vm, expOrFn, cb, options);

Observer

Observer is an observer, he is responsible for recursively observing (or processing) reactive objects (or arrays). In the printed example, you can notice that the reactive objects will have an __ob__, which is proof of what has been observed. Observers are not as important as Dep and Watcher above, just a little understanding is enough.

walk

Observer.prototype.walk is the core method of recursive processing during Observer initialization, but this method is used to process objects, and there is also Observer.prototype.observeArray Process arrays.

Core Process

According to the relationship between the above concepts, how to match them and how to achieve responsive data update?

First set our goal: naturally when the data is updated, the view will be automatically refreshed to display the latest data.

This is the relationship between Dep and Watcher mentioned above. The data is Dep, and Watcher triggers the page rendering function (this is the most important watcher).

But a new question arises, how does Dep know that any Watchers depend on him?

Vue adopts a very interesting method:

  • Before running the Watcher's callback function, first write down what the current Watcher is (through Dep.target)

  • If responsive data is used in the running callback function, the getter function of the responsive data will inevitably be called.

  • In the getter function of the responsive data You can write down the current Watcher and establish the relationship between Dep and Watcher

  • After that, when the responsive data is updated, the setter function of the responsive data will inevitably be called

  • Based on the previously established relationship, the callback function corresponding to the Watcher can be triggered in the setter function

Code

The above logic is in the defineReactive function middle. There are many entrances to this function. Let’s talk about the more important observe function first.

In the observe function, a new Observer object will be created, in which Observer.prototype.walk is used to process the values ​​in the object one by one in a responsive manner, and the defineReactive function is used.

Because the defineReactive function is so important and not long, it is more convenient to post it here directly.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

function defineReactive(obj, key, val, customSetter, shallow) {

  var dep = new Dep()

  depsArray.push({ dep, obj, key })

  var property = Object.getOwnPropertyDescriptor(obj, key)

  if (property && property.configurable === false) {

    return

  }

 

  // cater for pre-defined getter/setters

  var getter = property && property.get

  var setter = property && property.set

 

  var childOb = !shallow && observe(val)

  Object.defineProperty(obj, key, {

    enumerable: true,

    configurable: true,

    get: function reactiveGetter() {

      var value = getter ? getter.call(obj) : val

      if (Dep.target) {

        dep.depend()

        if (childOb) {

          childOb.dep.depend()

          if (Array.isArray(value)) {

            dependArray(value)

          }

        }

      }

      return value

    },

    set: function reactiveSetter(newVal) {

      var value = getter ? getter.call(obj) : val

      // 后半部分诡异的条件是用于判断新旧值都是 NaN 的情况

      if (newVal === value || (newVal !== newVal && value !== value)) {

        return

      }

      // customSetter 用于提醒你设置的值可能存在问题

      if ('development' !== 'production' && customSetter) {

        customSetter()

      }

      if (setter) {

        setter.call(obj, newVal)

      } else {

        val = newVal

      }

      childOb = !shallow && observe(newVal)

      dep.notify()

    },

  })

}

Copy after login

First of all, each responsive value is a "dependency", so in the first step, we use the ability of closure to create a Dep for each value. (With Vue 3, there is no need for closures)

Then look at the three core parameters:

  • obj The object where the value currently needs to be processed responsively

  • key value key

  • val current value

This value may have been defined before own getters and setters, so when doing Vue’s responsive processing, the original getters and setters are processed first.

As mentioned above in the core process, the getter function will establish the relationship between Dep and Watcher, specifically relying on dep.depend().

Below are several methods for Dep and Watcher to call each other:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

Dep.prototype.depend = function depend() {

  if (Dep.target) {

    Dep.target.addDep(this)

  }

}

Watcher.prototype.addDep = function addDep(dep) {

  var id = dep.id

  if (!this.newDepIds.has(id)) {

    this.newDepIds.add(id)

    this.newDeps.push(dep)

    if (!this.depIds.has(id)) {

      dep.addSub(this)

    }

  }

}

Dep.prototype.addSub = function addSub(sub) {

  this.subs.push(sub)

}

Copy after login

通过这几个函数,可以领略到了 Dep 和 Watcher 错综复杂的关系……不过看起来迂回,简单来说,其实做的就是上面说的互相添加到多对多列表。

你可以在 Dep 的 subs 找到所有订阅同一个 Dep 的 Watcher,也可以在 Watcher 的 deps 找到所有该 Watcher 订阅的所有 Dep。

但是里面还有一个隐藏问题,就是 Dep.target 怎么来呢?先放一放,后会作出解答。先接着看看 setter 函数,其中的关键是 dep.notify()。

1

2

3

4

5

6

7

Dep.prototype.notify = function notify() {

  // stabilize the subscriber list first

  var subs = this.subs.slice()

  for (var i = 0, l = subs.length; i < l; i++) {

    subs[i].update()

  }

}

Copy after login

不难理解,就是 Dep 提醒他的订阅者列表(subs)里的所有人更新,所谓订阅者都是 Watcher,subs[i].update() 调用的也就是 Watcher.prototype.update。

那么来看一下 Watcher 的 update 做了什么——

1

2

3

4

5

6

7

8

9

Watcher.prototype.update = function update() {

  if (this.lazy) {

    this.dirty = true

  } else if (this.sync) {

    this.run()

  } else {

    queueWatcher(this)

  }

}

Copy after login

在这里我觉得有两个点比较值得展开,所以挖点坑

The above is the detailed content of What is the responsiveness principle of Vue?. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot Article Tags

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

PHP and Vue: a perfect pairing of front-end development tools PHP and Vue: a perfect pairing of front-end development tools Mar 16, 2024 pm 12:09 PM

PHP and Vue: a perfect pairing of front-end development tools

Questions frequently asked by front-end interviewers Questions frequently asked by front-end interviewers Mar 19, 2024 pm 02:24 PM

Questions frequently asked by front-end interviewers

Simple JavaScript Tutorial: How to Get HTTP Status Code Simple JavaScript Tutorial: How to Get HTTP Status Code Jan 05, 2024 pm 06:08 PM

Simple JavaScript Tutorial: How to Get HTTP Status Code

Is Django front-end or back-end? check it out! Is Django front-end or back-end? check it out! Jan 19, 2024 am 08:37 AM

Is Django front-end or back-end? check it out!

What is a front-end modular ESM? What is a front-end modular ESM? Feb 25, 2024 am 11:48 AM

What is a front-end modular ESM?

How to get HTTP status code in JavaScript the easy way How to get HTTP status code in JavaScript the easy way Jan 05, 2024 pm 01:37 PM

How to get HTTP status code in JavaScript the easy way

Exploring Go language front-end technology: a new vision for front-end development Exploring Go language front-end technology: a new vision for front-end development Mar 28, 2024 pm 01:06 PM

Exploring Go language front-end technology: a new vision for front-end development

The transformation path from PHP back-end to front-end development The transformation path from PHP back-end to front-end development Mar 12, 2024 pm 06:45 PM

The transformation path from PHP back-end to front-end development

See all articles