Today let us realize a simple version of vUE, the text is longer, the collection is suggested to see slowly, and finally ask for a thumbs up !!!!

Start Vue instance function

Let’s create vue. js file in SRC folder, this file is our vueJs entry file, I directly posted here after running the code can see the same content as me on the console, it means that you have opened the mysterious veil of Vue!

The Object.defineProperty() method is mainly used to implement two-way binding of data. If you don’t understand, refer to MDN

class Vue { 
    constructor(options) { 
        // Save attributes
        this.options = options || {}
        this.data = options.data || {}
        this.el = options.el && typeof options.el === 'string' ? document.querySelector(options.el) : options.el
        this._parxyData()
    }
    // The convenience object properties are injected into the vue instance
    _parxyData() { 
        Object.keys(this.data).forEach(item= > { 
            // We pass this instead of an object to inject properties into the instance
            Object.defineProperty(this, item, {
                get: () = > { 
                    return this.data[item]
                },
                // newValue is assigned when the value of the attribute changes
                set: (newValue) = > { 
                    if(newValue === this.data[item]){
                        return
                    }
                    this.data[item]=newValue
                }
            })
        })
    }
}
Copy the code

At this point our console should print out that the instance has been injected perfectly

Create observer function

The observer function is used to notify subscribers when data is updated

class Watcher {
    constructor(vm,key,cb){
        this.vm=vm;
        // Attributes in data
        this.key=key;
        // The callback function updates the attempt
        this.cb=cb;
        // Record the watcher object to the deP static property target
        Dep.target = this;
        // invoke addSub in the get method
        this.oldValue=vm[key];
        Dep.target=null
    }
    // Update the view when properties change
    update(){
        let newValue=this.vm[this.key]
        if(this.oldValue === newValue){
            return
        }
        this.cb(newValue)
    }
}
Copy the code

Create a compiler template function

The template functions we use to parse vUE syntax, and get values for user actions, tell observers to notify subscribers to update data, and render views

class Template {
    constructor(vm){
        this.el=vm.el
        this.vm=vm
        this.compile(this.el)
    }
    // Compile templates to handle text templates and element nodes
    compile(el){
        let childNode = el.childNodes
        Array.from(childNode).forEach(node= >{
            // Process text nodes
            if(this.isTextNode(node)){
                this.complieText(node)
            }else if(this.isElementNode(node)){
                this.compileElement(node)
            }

            // Determine if there are child nodes and recursively call yourself if there are
            if(node.childNodes&&node.childNodes.length! =0) {this.compile(node)
            }
        })
    }
    // Compile element node processing instructions
    compileElement(node){
        Array.from(node.attributes).forEach(attr= >{
            // Determine whether the command is timed
            let attrName = attr.name
            if(this.isDirective(attrName)){
                attrName =attrName.substr(2)
                let key = attr.value
                this.update(node,key,attrName)
            }
        })
    }
    update(node,key,attrName){
        let updateFn = this[attrName+'Updater']
        updateFn && updateFn.call(this,node,this.vm[key],key)
    }
    / / compile v - text
    textUpdater(node,value,key){
        node.textContent=value
        // Create a Watcher object
        new Watcher(this.vm,key,(newValue) = >{
            node.textContent = newValue
        })
    }
    // Compile the modeo directive
    modelUpdater(node,value,key){
        node.value=value
        // Create a Watcher object
        new Watcher(this.vm,key,(newValue) = >{
            node.value = newValue
        })
        // Implement bidirectional binding for node to register time
        node.addEventListener('input'.() = >{
            this.vm[key] = node.value
        })
    }
    // Compile the text node to handle the difference expression
    complieText(node){
        let reg = / \ {\ {(. +?) \} \} /
        let value = node.textContent
        if(reg.test(value)){
            let key = RegExp.$1.trim()
            node.textContent = value.replace(reg,this.vm[key])
            // Create a Watcher object
            new Watcher(this.vm,key,(newValue) = >{
                node.textContent = newValue
            })
        }
    }
    // Determine if element attributes are directives
    isDirective(attrName){
        return attrName.startsWith('v-')}// Determine whether the node is a text node
    isTextNode(node){
        return node.nodeType ===3
    }
    // Determine whether a node is an element node
    isElementNode(node){
        return node.nodeType ===1}}Copy the code

Once we’re done here we can see that the page data has been rendered

Create publish and subscribe mode

Publish and subscribe model

class Dep {
    constructor(){
        // Store all observers
        this.subs = []
    }
    // Add an observer
    addSub(sub){
        if(sub && sub.update){
            this.subs.push(sub)
        }
    }
    // Send notifications
    notify(){
        this.subs.forEach(sub= > {
            console.log('I'm gay Update',sub)
            sub.update()
        })
    }
}
class Observer { 
    constructor(data) { 
        this.kidnap(data)
    }
    // Iterate over all the attributes in data
    kidnap(data){
        // Determine whether the object is a time object
        if(! data ||typeofdata ! ='object') {return
        }
        // Iterate over all properties of the object
        Object.keys(data).forEach(key= >{
            this.defineReacttive(data,key,data[key])
        })
    }
    defineReacttive(obj,key,val){
        // Create a deP object
        let dep = new Dep()
        // If so, the object is converted to responsive data
        this.kidnap(val)
        Object.defineProperty(obj,key,{
            enumerable:true.configurable:true.get:() = >{
                // Collect dependencies and associate them with observers
                Dep.target && dep.addSub(Dep.target)
                return val
            },
            set:(newValue) = >{
                if(newValue === val){
                    return
                }
                val = newValue
                this.kidnap(val)
                // Data changes need to be notified
                dep.notify()
            }
        })
    }
}
Copy the code

5. Try new skills

So that’s pretty much everything we’ve got, so let’s try it out