1. How can we use this object to access properties defined in parameters such as data, props, and methods?

Principle:

Because VUE did the agency internally. If we use this to access a property, vue will automatically look for parameters such as data,props,methods, etc. So we’re going to see that properties defined in props, data can’t be defined any more and will throw a warning. The same goes for methods.

As anyone who has used Vue knows, Vue itself is a constructor, so our use is simply new Vue(). Let’s simulate the agent inside Vue with code

(partial source code: the vue project under SRC/core/instance/state. Js)

// Define an empty function
function noop() {}  
// Define a common attribute description object
const sharedPropertyDefinition = {
    enumerable: true.configurable: true.get: noop,
    set: noop
}
/** ** defines the proxy function * @target current object * @sourceKey is passed in the source, i.e. the name of the proxy object * @key accesses the property */
function proxy(target, sourceKey, key) {
    sharedPropertyDefinition.get = function proxyGetter() {
        This ['_data']['name'] returns this['_data']
        // target[key] => target[source][key]
        return target[sourceKey][key];
    }
    sharedPropertyDefinition.set = function proxySetter(val) {
        target[sourceKey][key] = val;
    }
    Object.defineProperty(target, key, sharedPropertyDefinition);
}

// constructor
function MyVue(options) {
    this._data = options.data || {};
    this._props = options.props || {};
    this._methods = options.methods || {};
    this.init(options);
}
MyVue.prototype.init = function(options) {
    initData(this, options.data);
    initProps(this, options.props);
    iniMethods(this, options.methods);
}

// Related methods
function initData(vm, dataObj) {
    Object.keys(dataObj).forEach(key= > proxy(vm, '_data', key));
}
function initProps(vm, propsObj) {
    Object.keys(propsObj).forEach(key= > proxy(vm, '_props', key));
}
function iniMethods(vm, methodsObj) {
    Object.keys(methodsObj).forEach(key= > proxy(vm, '_methods', key));
}
Copy the code

The code here is primarily an example and does not determine whether the attribute is duplicated.

Test code:

let myVm = new MyVue({
    data: {
        name: 'JK'.age: 25
    },
    props: {
        sex: 'man'
    },
    methods: {
        about() {
            console.log(`my Name is The ${this.name}, age is The ${this.age}, sex is The ${this.sex}`); }}}); myVm.name// 'JK'
myVm.age  / / 25
myVm.sex  // 'man'
myVm.about()  // my Name is JK, age is 25, sex is man
myVm.age = 24;  
Copy the code

The processing inside the specific Vue is more complex, and many boundary cases will be judged. For example, when data returns a function, it needs to be handled separately, for example, when props passes objects with default and type attributes, and so on.

2. How to implement a simple data responsive system

Vue’s data-responsive implementation relies on the Object.defineProperty API, which is why it doesn’t support IE8 and can’t hack.

It is said that Vue3.0 uses ES6 proxies and is written in TypeScript. Looking forward to it.

What does Vue do after changing data? If there is a complete set of processes, there are many, involving Watcher, render function, VNode, Dom Diff and so on.

The responsive system itself is based on the observer model, or publish/subscribe model. Publish/subscribe is like going to an agent to rent a house. The observer model, on the other hand, is like going directly to the village landlord to rent a house. Publish/subscribe mode has one more dispatch center (intermediary) than observer mode.

I’m just going to show you how to collect dependencies and how to notify changes in values.

(Source: vUE project SRC /core/ Observer /)

Throw out any other factors, and let’s implement a responsive prototype
// If an object is data
let data = {
    x: 1.y: 2
}
// We make this object responsive
for(const key in data) {
    Object.defineProperty(data, key, {
        get() {
            console.log('I got data's${key}`);
            return data[key]
        },
        set(val) {
            console.log('I set up data's${key}for${val}`); data[key] = val; }})}Copy the code

Throw this code into the browser, and get data.x, and you’ll see, uh-oh, how come the browser keeps output, why?

Return data[key]; return data[key]; return data[key]; return data[key]; So we’ll optimize the code later.

Next, we’re ingetTo collect dependencies,setInside trigger response

How do you collect dependencies and trigger responses? If you’re familiar with the observer pattern, you can immediately think of maintaining an array, pushing the corresponding function into the array every time you trigger get, and firing the corresponding function every time you set. Is it very similar to our custom event system, of course, Vue internal certainly not so simple.

// Define a watch function to get the handler that changes a value
// Target is a global variable used to store the corresponding function
let Target = null
function $watch (exp, fn) {
    // Set Target to fn
    Target = fn;
    // Read the field value and trigger the get function
    data[exp];
}

// Dep is referenced by closures in get and set and is not recycled
// Each key has its own deP
for(const key in data) {
    const dep = [];
    // Optimize the loop
    let val = data[key];
    Object.defineProperty(data, key, {
        get() {
            console.log('I got data's${key}`);
            // Collect dependencies
            dep.push(Target);
            return val;
        },
        set(newVal) {
            console.log('I set up data's${key}for${newVal}`);
            if (val === newVal) {
                return ;
            }
            val = newVal;
            // Trigger dependencies
            dep.forEach(fn= >fn()); }})}// Listen for data changes
$watch('x', () = >console.log('x is modified '));    // Print 'I got x of data'
data.x = 3;         // Print 'I set x of data to 3', x is changed
Copy the code

$watch ($watch, $watch, $watch, $watch, $watch, $watch, $watch, $watch, $watch, $watch, $watch, $watch, $watch, $watch, $watch, $watch, $watch, $watch, $watch, $watch So, to optimize, pass in a render function that fires the property’s get inside.

Full code:

let data = {
    x: 1.y: 2
}

// Target is a global variable used to store the corresponding function
let Target = null
function $watch (exp, fn) {
    // Set Target to fn
    Target = fn;
    // if exp is a function, execute it directly
    if (typeof exp === 'function') {
        exp();
        return;
    }
    // Read the field value and trigger the get function
    data[exp];
}

// Dep is referenced by closures in get and set and is not recycled
// Each key has its own deP
for(const key in data) {
    const dep = [];
    // Optimize the loop
    let val = data[key];
    Object.defineProperty(data, key, {
        get() {
            console.log('I got data's${key}`);
            // Collect dependencies
            dep.push(Target);
            return val;
        },
        set(newVal) {
            console.log('I set up data's${key}for${newVal}`);
            if (val === newVal) {
                return ;
            }
            val = newVal;
            // Trigger dependencies
            dep.forEach(fn= >fn()); }})}// Test the code
function render () {
    return document.write(` x:${data.x}; Y:${data.y}`)
}
$watch(render, render);
Copy the code

In fact, the internal processing of Vue is not so simple, such as the difference between array and object processing, object depth traversal, we have not considered here.

There are many more questions to learn:

How to avoid repeated collection of dependencies, how to parse and generate rendering functions based on the Template template, implementation of AST, internal parsing of V-ON, V-bind, V-for instructions, etc.

Vue.$set () {push, slice, etc.; push, slice, etc.;

After modifying the data, how to trigger the internal rendering of the corresponding DOM node.

reference

Vue technology insider.