Myvue.js is mainly used to listen for property changes

class MYvue {
    constructor(options) {
        this.$options = options;
        this.$data = options.data;
        // Data hijacking
        this.observe(this.$data);
        this.$el = options.el;
        // contains the Watcher creation
        new Complie(options.el, this)}// Data hijacking
    observe(data) {
        if(! data ||typeofdata ! ='object') {
            return;
        }
        Object.keys(data).forEach((key) = > {
            // Make the data observable
            / / this. $data. The test change
            this.defineReactive(data, key, data[key]);
            // This. Test agent changes
            this.proxyData(key); })}// Make the data observable
    defineReactive(obj, key, value) {
        var dept = new Dep();
        //value indicates the recursive traversal of the object
        this.observe(value);

        Object.defineProperty(obj, key, {
            get() {
                // The property was read and the subscription will need to be added in the future
                console.log('I'm being read.');
                // Dep. Target (the current watcher object is stored in Dep's deps)
                Dep.target && dept.addDep(Dep.target)
                return value;
            },
            set(newVal) {
                if (newVal == value) {
                    return;
                }
                value = newVal;
                console.log(key + 'Property is better, he's updating the value' + newVal);
                // If I am changed, I will inform here in the future
                dept.notify(); // Change the data and let Watcher's update method execute
            },
            enumerable: true.configurable: true})}// Delegate attributes from data to vue instances
    proxyData(key) {
        Object.defineProperty(this, key, {
            get() {
                return this.$data[key]
            },
            set(newVal) {
                this.$data[key] = newVal; }}}})//Dep is used to manage wather, the manager role
class Dep {
    constructor() {
        // Store all dependencies (watcher), one watcher for one attribute
        this.deps = [];
    }
    // Collect subscribers
    addDep(sub) {
        this.deps.push(sub);
    }
    // Notify subscription updates
    notify() {
        this.deps.forEach((sub) = > {
            // You need to update
            sub.update() Update is a function inside watchr}}})// Listener object
class Watcher {
    constructor(vm, key, cb) {
        // vm :Vue instantiates the object
        // key: the attribute to listen on
        // cb: is the update function bound by Watccher
        this.vm = vm;
        this.key = key;
        this.cb = cb;
        Dep.target = this;// This refers to Watcher itself

        this.vm[key] // Trigger the getter to add the dependency
        Dep.target = null;

    }
    update() {
        console.log('Your properties are about to be updated');
        this.cb.call(this.vm, this.vm[this.key]); }}Copy the code

Complie.js rerenders HTML with attribute changes

class Complie {
    constructor(el, vm) {
        // Iterate over the node
        this.$el = document.querySelector(el);
        this.$vm = vm;
        / / compile
        if (this.$el) {
            // Convert content to fragment
            this.$fragment = this.node2Fragment(this.$el);

            // Perform compilation
            this.replaceTemplate(this.$fragment)
            // Append the compiled HTML results to $el
            this.$el.appendChild(this.$fragment); }}node2Fragment(el) {
        CreateDocumentFragment is used to create a virtual DOM node
        var frag = document.createDocumentFragment();
        let child;
        // Move all elements of el to frag
        while (el.firstChild && (child = el.firstChild)) {
            frag.appendChild(child);
        }
        return frag;
    }
    // Replace the contents of the EL facade
    // Replace the contents of el
    replaceTemplate(frag) {
        // console.log('frag.childNodes',frag.childNodes);
        var childNodes = Array.from(frag.childNodes);
        if (childNodes.length == 0) return;
        childNodes.forEach(node= > {
            let txt = node.textContent;
            let reg = / \ {\ {(. *?) \}\}/g; // Regular match {{}}

            // The read-only node.nodeType attribute indicates the type of the Node
            The nodeType attribute can be used to distinguish between different types of nodes, such as elements, text, and comments.
            {{}}}
            if (node.nodeType === 3 && reg.test(txt)) {
                // console.log(RegExp.$1); // The first group to be matched is a.b, c
                let arr = RegExp.$1.split('. ');
                let valTest = this.$vm;
                arr.forEach(key= > {
                    valTest = valTest[key]; / / such as this. A.
                });
                // Use the trim method to trim the leading and trailing Spaces
                node.textContent = txt.replace(reg, valTest).trim();

                // Listen for changes, added in step 2
                // Add two more parameters to Watcher to take the new value (newVal) to the callback function
                new Watcher(this.$vm, RegExp. $1,newVal= > {
                    // Data is updated when properties change
                    node.textContent = txt.replace(reg, newVal).trim();
                });
            }

            // Two-way binding of V-modle data
            if (node.nodeType === 1) { // Element node
                let nodeAttr = Array.from(node.attributes); // Get all the attributes in the DOM, an array of classes
                nodeAttr.length > 0 && nodeAttr.forEach(attr= > {
                    let name = attr.name; // v-model type
                    let exp = attr.value; // c text
                    if (name.includes('v-')) {
                        node.value = this.$vm[exp]; / / this. C to 2
                    }
                    // oninput .onclick
                    node.addEventListener('input'.e= > {
                        let newVal = e.target.value;
                        // assign a new value to this.c
                        // Set is called, and notify is called. Watcher update is called
                        this.$vm[exp] = newVal;
                    });
                });
            }

            // If there are children, continue recursively with the replaceTemplate
            if (node.childNodes && node.childNodes.length) {
                this.replaceTemplate(node); }}); }}Copy the code

use

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, Initial scale=1.0"> <title>Document</title> </head> <script SRC ="./ myvue.js "></script> <script SRC = ". / the compile. Js "> < / script > < body > < div id =" app "> < h1 > {{song}} < / h1 > < p > theme song for the {{album. The theme}} < / p > < p > <span> composer {{singer}} et al. </span><em>qq</em></p> <input type="text" v-model="song"> </div> <script> '# app' data: {song: 'fly ah fly ah, album: {name:' ten thousand ', the theme: "carelessly"}, singer: 'arrival'}}); </script> </body> </html>Copy the code