This is the 22nd day of my participation in Gwen Challenge

One, foreword

The previous part mainly introduced the process analysis of Vue dependency collection:

In this article, Vue relies on the implementation of collection


Two, the Watcher part

1. The essence of Watcher

The previous analysis shows that:

  • Vm. _render: Calls the render method
  • Vm. _update: Updates the virtual node to the page

In essence, vm._update(vm._render()) triggers an update of the view

    let vm = new Vue({
      el: '#app'.data() {
        return { name: "Brave" , age: 123}}}); vm.name ="Brave Wang";   // Data changes
    vm._update(vm._render()); // View update
Copy the code

In Vue, data updates:

  • Each data has a DEP property: watcher, the view-rendering function that records the component or page that uses the data
  • Multiple Watchers stored in the DEP property will be notified when data changes, in this case observer mode
  • Watcher here is the equivalent ofvm._update(vm._render())

2, Extract view update logic watcher

Extract the view rendering logic into callable functions:

export function mountComponent(vm) {
  // Extract into a callable function
  let updateComponent = () = >{
    vm._update(vm._render());  
  }
  updateComponent();
}
Copy the code

The final goal is to have the updateComponent method invoked through Watcher

Create the Watcher class

Data changes, views update, so Watcher should be a responsive module

Create watcher class:

// src/observe/watcher.js

class Watcher {
  constructor(vm, fn, cb, options){
    this.vm = vm;
    this.fn = fn;
    this.cb = cb;
    this.options = options;

    this.getter = fn; // fn is the page rendering logic
    this.get();
  }

  get(){
    this.getter();  // Call the page rendering logic}}export default Watcher;
Copy the code

Insert updateComponent into the Watcher class. How do you call updateComponent from Watcher

export function mountComponent(vm) {
  let updateComponent = () = >{
    vm._update(vm._render());  
  }

  // Render watcher: Each component has a Watcher
  new Watcher(vm, updateComponent, () = >{
    console.log('Watcher-update')},true)}Copy the code

4. The need to rely on collections

When data changes, it is hijacked into the set method of Object.defineProperty

So, if the view update logic is called at this point, can we do “data changes, view updates”?

// src/observe/index.js#defineReactive
Object.defineProperty(obj, key, {
    get() {
      return value;
    },
    set(newValue) {
      if (newValue === value) return
      vm._update(vm._render());  // View updates are triggered when data changesobserve(newValue); value = newValue; }})Copy the code

Doing so, though, allows you to update the view

However, there is a serious problem:

  • At this point, since all reactive data is modified into the set method,
  • Changes that cause data not being used by the view can also trigger page updates,
  • That is, doing so triggers unnecessary view updates

Therefore, during the view rendering process, the data used needs to be recorded, and only changes to that data trigger view updates

To do this, you need to do dependency collection. You need to create a DEP for the property to collect the render Watcher


Third, Dep

1. Create Dep class

As mentioned earlier:

  • Each data has a DEP attribute that holds the corresponding Watcher;
  • There may also be multiple DEPs in each Watcher

So, the Dep class should have a method to add watcher; The Watcher class should also have a method to add deP;

When data changes, all watchers in the data DEP property are notified to perform view updates, applying observer mode

To indicate the uniqueness of a Dep, a unique ID is added each time a new Dep is added.

// src/observe/dep.js

let id = 0;

class Dep {
  constructor(){
    this.id = id++;
    this.subs = [];
  }
  // Save data to render watcher
  depend(){
    this.subs.push(Dep.target)
  }
}

Dep.target = null;  // Static attributes

export default Dep
Copy the code

2. Add the DEP attribute to the attribute

Object.defineproperty adds attributes to each data, at which point deP is added to the attributes:

function defineReactive(obj, key, value) {
  observe(value);
  let dep = new Dep();  // Add a deP for each attribute
  Object.defineProperty(obj, key, {
    get() {
      return value;
    },
    set(newValue) {
      if (newValue === value) returnobserve(newValue); value = newValue; }})}Copy the code

When the view is rendered, it uses the get method in Watcher, vm._update(vm._render()).

At this point, using the single-threaded nature of JS, the current render watcher is recorded before rendering

class Watcher {
  constructor(vm, fn, cb, options){
    this.vm = vm;
    this.fn = fn;
    this.cb = cb;
    this.options = options;

    this.getter = fn;
    this.get();
  }
  get(){
    Dep.target = this;  // Record the watcher to dep.target before triggering the view render
    this.getter();      // Call the page rendering logic
    Dep.target = null;  // Clear the Watcher record after rendering}}export default Watcher
Copy the code

During view rendering, data values such as vm.name will be triggered

At this point, go to the get method in Object.defineProperty

So if the dep. target in the get method has a value (that is, the current watcher), let the Dep of the data remember to render the watcher

function defineReactive(obj, key, value) {
  observe(value);
  let dep = new Dep();
  Object.defineProperty(obj, key, {
    get() {
      if(Dep.target){
        dep.depend();
      }
      return value;
    },
    set(newValue) {
      if (newValue === value) returnobserve(newValue); value = newValue; }})}Copy the code

This way, the DEP remembers all the render watcher and does not trigger the view update when the data is updated that is not involved in the view rendering


Four, the end

In this paper, DEP and Watcher are related

  • The necessity of relying on collection is introduced.
  • The functions of Watcher and Dep are introduced.
  • The implementation of Watcher class and Dep class;
  • How Watcher and Dep are related;

Next up, view updates


Maintenance logs:

20210801: Modify directory structure to separate Watcher and Dep parts; Update the abstract;