This is the third day of my participation in Gwen Challenge

One, foreword

In the previous part, the flow of Vue usage and data initialization was introduced

To review, it mainly involves the following core points:

  • InitMixin method: vue.prototype. _init
  • Vm.$options: Enables the options options to be shared on the VM instance
  • InitState method: Centralized processing of multiple data sources during Vue initialization
  • InitData method: initializes data

In this article, we continue to initialize data, hijacking of objects (single-layer hijacking of object attributes).


Second, Vue’s responsivity principle

Problem: Vue’s responsivity principle

The responsive principle of Vue

DefineProperty to add get and set methods to attributes, so as to achieve the hijacking of data operation… Data in the figure below:


Third, the hijacking of the object

1. Get data from initData

Data is in Options, which is shared by the VM instance

function initData(vm){
    let data = vm.$options.data;// Get the data passed in by the user during vUE initialization
    console.log("State. Js-initdata, data initialization operation", data)
}
Copy the code

2. Handle both cases of data

As mentioned in the previous article, data can be a function or an object

Therefore, the logic needs to process data once

// utils.js
export function isFunction(val){
  return typeof val == 'function'
}
Copy the code

If data is a function, you need to execute the object returned from the temporal

// If data is a function, execute data to get its return value
if(isFunction(data)){
  data = data(); // This is not a VM, but a window
}
Copy the code

Testing:

let vm = new Vue({
  el: '#app'.data() {
    console.log("Print what this refers to when the data function executes.")
    console.log(this)
    return { message: 'Hello Vue' } // data returns an object}});Copy the code

In this case, when the data function executes, this points to the window

3. Handle the orientation of this

In Vue, this should point to the current VM instance when the data function is executed

Therefore, bind this to the current VM instance when data is executed

if(isFunction(data)){
  data = data.call(vm);// Bind this to vm when data executes
}
Copy the code

Testing:

Simplify code: logical merging of data as objects or functions:

// Data can be functions or objects
// If data is a function, we need to let the data function execute and get the object it returns
// If data is an object, no processing is done
data = isFunction(data) ? data.call(vm) : data;
Copy the code

Note: Only data on the root instance can be an object; the component must be a function

4. Core module Observe: Observe data

The reactive principle of data is as follows:

Override all attributes on data with Object.defineProperty

This requires iterating through the data Object to get each attribute and redefining it one by one via Object.defineProperty

  • Observe module (Folder)
  • Observe method (entry)

Create the import file observe/index.js

// src/observe/index.js
export function observe(value) {
  console.log(value)
}
Copy the code

Introduce and use the observe method in state.js

Call the observe method to observe data and realize the response of data

import { observe } from "./observe";
import { isFunction } from "./utils";

export function initState(vm) {
    const opts = vm.$options;
    if(opts.data) { initData(vm); }}function initData(vm) {
    let data = vm.$options.data;
    data = isFunction(data) ? data.call(vm) : data;
    observe(data);  // Use observe to implement data response
}
Copy the code

After this processing, data must be an object

So, check data, and return if it’s not an object

// src/utils
/** * Check whether it is an object: The type is object and cannot be null *@param {*} val 
 * @returns * /
export function isObject(val) {
  return typeof val == 'object'&& val ! = =null
}
Copy the code
// src/observe/index.js
import { isObject } from ".. /utils";

export function observe(value) {

  // If value is not an object, we don't need to observe it, just return
  if(! isObject(value)){return; }}Copy the code

5, Observer class: observe the object

The logic of completion looks like this:

// src/observe/index.js
export function observe(value) {
  if(! isObject(value)){return;
  }

  // Observe the value object to realize the data response
  return new Observer(value);
}
Copy the code
Observer class: Iterates through Object properties, redefining properties in a data Object using Object.definePropertyCopy the code
// src/observe/index.js
class Observer {

  constructor(value){
    // If value is an Object, iterate over the attributes in the Object and redefine with Object.defineProperty
    this.walk(value); // Loop object properties
  }

  // Loop through the data Object, using object.keys to not loop through the prototype method
  walk(data){
    Object.keys(data).forEach(key= > { 
      // Use object.defineProperty to redefine attributes in a data ObjectdefineReactive(data, key, data[key]); }); }}/** * Redefine attributes in the data Object using Object.defineProperty. Vue2's performance bottleneck is also here due to object. defineProperty's low performance@param {*} Obj needs to define the object * for attributes@param {*} Key Specifies the name of the property defined for the object@param {*} Value Specifies the attribute value */ defined for the object
function defineReactive(obj, key, value) {
  Object.defineProperty(obj, key, {
    get(){ / / closures
      return value;	Obj [key] = obj[key];
    },
    set(newValue) {
      if (newValue === value) returnvalue = newValue; }})}Copy the code

At this point, all attributes in OBj are redefined via defineProperty, with get and set methods

Question: Why use classes instead of functions?

TODO

Object. DefineProperty = Object. DefineProperty = Object.

TODO

6, the test

In the initData method that data initializes,

Observe data through the observe method to realize the hijacking of data attributes

function initData(vm) {
    console.log("State. Js-initdata, data initialization operation")
    let data = vm.$options.data;
    data = isFunction(data) ? data.call(vm) : data;
    // Reactivity of data: iterate through the Object to get all attributes, and override all attributes in data with Object.defineProperty
    observe(data); // Observation data
    console.log(data)
}
Copy the code

Print the observed data object and check the execution effect:

At this point, the hijacking of the data attribute has been achieved, but only the first level of the attribute has been hijacked

7. Note: Install the plug-in

Observe does not automatically find the index entry file
import { observe } from "./observe";
// you need to specify index.js
import { observe } from "./observe/index";

// install package: @rollup/plugin-node-resolve
// npm i @rollup/plugin-node-resolve
Copy the code

Using plug-ins: Modify the rollup configuration file

import babel from 'rollup-plugin-babel'
import resolve from 'rollup-plugin-node-resolve'; // Import plug-ins

export default {
    input:'./src/index.js'.output: {file:'dist/vue.js'.format:'umd'.name:'Vue'.sourcemap:true,},plugins:[
        resolve(),  // Use plugins
        babel({
            exclude:'node_modules/**'}})]Copy the code

【Vue2. X source code learning notes 】 chapter 3 – object single-layer hijacking; This paper mainly introduces the Vue data initialization process, the single-layer hijacking of object attributes, several core points: data for the processing of functions and objects; The reference to this in data; Observer class for data observation;


Three, the end

This paper mainly introduces the Vue data initialization process, the single-layer hijacking of object attributes, several core points:

  • Data is the processing of functions and objects
  • The data function refers to this
  • Observer class for observing data
  • The walk method iterates through the data property.
  • DefineReactive method: Data hijacking is implemented using Object.defineProperty

Next, deep hijacking of object properties