current position:Home>[read the source code of ruokawa vision x] issue 23 | why can vue2 this directly obtain data and methods

[read the source code of ruokawa vision x] issue 23 | why can vue2 this directly obtain data and methods

2022-04-29 20:33:45There's nothing left

This article participated in by official account @ Ruokawa vision   Weekly source code reading activity initiated , Click to learn more and participate together .

Interpretation principle

new Vue The general trend of :

image.png

initMethods

Realization this obtain methods The principle is methods The method traverses out , Mount to current vm For instance

Seeing this, there is not much doubt in logic , however bind It takes more time to understand , Start to understand that functions hang directly to vm Yes ,vm Calling method , that this It must point to vm, Don't think bind Force binding this Why , Here's an interesting review this, Has tried the arrow function 、 Constructors 、 Closures and so on , None of the findings affected this Point to (this I haven't seen it for a while , Ha ha ha )

I read some answers in Chuange's discussion area , There is no way to delve into this problem , Finally, I would like to thank brother Chuan for his answer .

The conclusion is that bind Force binding this You can avoid this Lost problems , such as :

  1. template in @click Bound events , It's useless here this.someFunc Called , And from the test results ,template There is no extra processing in the compilation process this
  2. deconstruction , No, this May directly report an error , For example, use this way in business :const { validate } = this.$refs["formRef"]
function initMethods (vm, methods) {
  var props = vm.$options.props;
  for (var key in methods) {
    //  No function will warn 
    if (process.env.NODE_ENV !== 'production') {
      if (typeof methods[key] !== 'function') {
        warn(
          "Method \"" + key + "\" has type \"" + (typeof methods[key]) + "\" in the component definition. " +
          "Did you reference the function correctly?",
          vm
        );
      }
      //  Unable to join  props  The property name on the has the same name 
      if (props && hasOwn(props, key)) {
        warn(
          ("Method \"" + key + "\" has already been defined as a prop."),
          vm
        );
      }
      //  Can't use  $  perhaps  _  start 
      if ((key in vm) && isReserved(key)) {
        warn(
          "Method \"" + key + "\" conflicts with an existing Vue instance method. " +
          "Avoid defining component methods that start with _ or $."
        );
      }
    }
    //  hold  methods  Hang all functions under to  vm  For instance 
    //  The next step will be to  data  All properties of are linked to  vm  For instance , therefore  this  Access to  data  and  methods Properties and methods in 
    vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm);
  }
}
 Copy code 

initData

Handle data than methods It's a lot more complicated , Handle traversal mount to vm On , Also need to do responsive processing

summary :

  1. Handle data front , First turn off the dependency collector , The initialization phase does not deal with dependencies
  2. adopt Object.defineProperty Handle _data attribute , And hang the attribute to vm On , Ensure the timely update of data
  3. to data Object recursively creates an observer instance
function getData (data, vm) {
  // #7573 disable dep collection when invoking data getters =>  Disable... When calling the data collector dep collect 
  // pushTarget  There should be a parameter , Here is  undefined, After being passed in, the current observable object is  undefined
  pushTarget();
  try {
    //  good heavens , Don't look at the source code here , I really don't know the form of function  data  One more parameter 
    return data.call(vm, vm)
  } catch (e) {
    handleError(e, vm, "data()");
    return {}
  } finally {
    //  Pop from the collector stack  push  Of  undefined, That is to open  dep  Collection function 
    popTarget();
  }
}
 Copy code 
function initData (vm) {
  var data = vm.$options.data;
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {};
  // data  The function must  return  An object , Otherwise, warn 
  if (!isPlainObject(data)) {
    data = {};
    process.env.NODE_ENV !== 'production' && warn(
      'data functions should return an object:\n' +
      'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
      vm
    );
  }
  // proxy data on instance
  var keys = Object.keys(data);
  var props = vm.$options.props;
  var methods = vm.$options.methods;
  var i = keys.length;
  while (i--) {
    var key = keys[i];
    // data  Medium  key  Name and  methods  If it is repeated in, the console will warn 
    if (process.env.NODE_ENV !== 'production') {
      if (methods && hasOwn(methods, key)) {
        warn(
          ("Method \"" + key + "\" has already been defined as a data property."),
          vm
        );
      }
    }
    // data  Medium  key  Name and  props  If it is repeated in, the console will warn 
    if (props && hasOwn(props, key)) {
      process.env.NODE_ENV !== 'production' && warn(
        "The data property \"" + key + "\" is already declared as a prop. " +
        "Use prop default value instead.",
        vm
      );
    } else if (!isReserved(key)) { // key  A name cannot be named with  $  perhaps  _  start 
      //  adopt  Object.defineProperty  Handle  _data  attribute , And hang the attribute to  vm  On 
      proxy(vm, "_data", key);
    }
  }
  
  // observe data
  //  to  data  Create an observer instance , And added to the dependent collection ,set  Property will trigger the dependency notify function 
  //  If  data  The attribute in is an object , A new observer instance will be created recursively )
  //  If it is an array, it will traverse each item and create an observer instance , however  defineProperty  Can only act on functions , Direct assignment through array index value cannot trigger dependency collection 
  observe(data, true /* asRootData */);
}
 Copy code 

Implement a simple version

For reference only

//  To create the agent 
function proxy(target, source, key) {
  Object.defineProperty(target, key, {
    configurable: true,
    enumerable: true,
    get() { 
      console.log(" Read out key:"+key);
      return this[source][key];
    },
    set(value) {
      console.log(" Set up key:"+key+" The value is :"+value);

      this[source][key] = value;
     },
  })
}

//  The observer 
 function observe(value) {
  if (!value || typeof value !== "object") {
    return;
  }
  // return new Observer(value, vm);
}
function getData() {
//  It will be closed temporarily dep collect 
  //  Force binding  this  by  vm, And pass in parameters 
  return vm.$options.data.call(vm,vm)
 }

function initData(vm) {
  var data = vm.$options.data;
  //  obtain  data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {};
  
  
  for (const key in data) {
  //  adopt  proxy  take  data  The properties on the proxy to  vm  On 
    proxy(vm, "_data", key);
  }
  //  to  data  Create an observer instance 
  observe(data);
}

function initMethods(methods, vm) {
  for (const key in methods) {
    if (typeof methods[key] !== "function") return "ERROR";
    //  You have to use  bind  Force binding  this
    vm[key] = methods[key].bind(vm);
  }
}

function Vue(options) {
  this.$options = options;
  this._init(options);
}

Vue.prototype._init = function (options) {
  const vm = this;
  vm._data=options.data;
  initData(vm);
  initMethods(options.methods, vm);
};

const _vm = new Vue({
  data: {
    msg: "hello",
    name: " Zhao si ",
  },
  methods: {
    say() {
      console.log("say" + this.msg);
    },
    setMsg(msg) { 
      this.msg = msg;
    }
  },
});

_vm.say();
_vm.setMsg("hello world");
_vm.say();



 Copy code 

summary

Read the source code for the first time , Because I haven't used it vscode Debugging function of , Although I have seen brother Chuan's novice tutorial , But start debugging vue I was still confused when I was . Another is to use cli New project generated , There will be render Processing of , After adjusting for a long time, I didn't get to the point .


new Vue({
  render: h => h(App),
}).$mount('#app')
 Copy code 

Refer to other students' articles , Just know that this writing can make the whole debugging process smoother , More agile :

new Vue({
  data: {
    msg: "Hello World",
  },
  methods: {
    changeMsg() { 
      this.msg="Hello Vue.js";
    }
  },
}).$mount("#app");
 Copy code 

copyright notice
author[There's nothing left],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2022/04/202204292033398918.html

Random recommended