簡易Vue

5379 ワード

げんりぶんせき
データを巡回することによって、データのために1つのパブリッシャーDepを作成して、そしてデータのgetとsetを修正して、setの中でパブリッシャーDepを利用して、サブスクライバWatcherにデータ更新を行うことを通知します(実際にはWatcherを呼び出す関数です)
コード#コード#
class Vue {    
    constructor(options) {
        if (typeof options !== 'object') {
            console.error('options is not a object')
            return
        }
        if (!'el' in options || !'data' in options) {
            console.error('options do not contain el and data')
        }
        this.el = typeof options.el == 'string' ?  options.el : 'body'
        
        let tempData = options.data
        this.data = {}
        //    
        for(let key in tempData){
            let temp = tempData[key]
            const dep = new Dep(key)  //     
            Object.defineProperty(this.data, key, {
                enumerable: true,
                configurable: true,
                get: function(){
                    return temp
                },
                set: function(val){
                	if(val != temp){ 
                    	temp = val
	                    dep.notify()  //    
                    }
                }
            })
        }
        this.root = null
        // this.html = null
        this.compile()
    }
    compile() {
        //     
        this.root = document.getElementById(this.el)
        // this.html = this.root.innerHTML //    
        this.innerText_handle(this.root) 
        this.attr_handle(this.root)
        this.compileChildren(this.root)
    }
    innerText_handle(e){
        //        
        const inner_text_Reg = /\{\{[a-zA-Z0-9]+\}\}/g
        let nodeList = e.childNodes
        for(let i = 0; i < nodeList.length; i++){
            if(nodeList[i].nodeType == 3){
                let node = nodeList[i]
                let value = node.nodeValue
                if(value != ''){
                    new Watcher(this, value, e)   //    
                }
                let arr = value.match(inner_text_Reg)
                if(arr){
                    arr.forEach(item => {
                        let name = item.split('{{')[1].split('}}')[0]
                        if(name in this.data){
                            node.nodeValue = value.replace(item, this.data[name])
                        }
                    })
                }
            }
        }
    }
    attr_handle(e){
        //     
        let attr = e.attributes
        let _this = this
        for(let i = 0; i < attr.length; i++){
            switch(attr[i].name){
                case 'v-text':
                    new Watcher(this, attr[i].value, e)
                    if(attr[i].value in this.data){
                        e.innerText = this.data[attr[i].value]
                    }
                    break
                case 'v-model':
                    if(e.nodeName != 'INPUT' && e.nodeName != 'TEXTAREA')
                        console.error('v-bind is valid')
                    new Watcher(this, attr[i].value, e)
                    if(attr[i].value in this.data){
                        e.value = this.data[attr[i].value]
                    }
                    e.addEventListener('input', function(){
                        _this.data[attr[i].value] = e.value
                    })
            }
        }
    }
    compileChildren(parent){
        //     
        let children = parent.children
        for(let i = 0; i < children.length; i++){
            this.innerText_handle(children[i])
            this.attr_handle(children[i])
            if(children[i].children !== 0){
                this.compileChildren.call(this, children[i])
            }
        }
    }
}
class Dep{
    static depMap = {}
    constructor(dataName){
        this.dataName = dataName  //data     
        this.subs = []
        Dep.depMap[dataName] = this
    }
    addSub(sub){
        //     
        this.subs.push(sub)
    }
    notify(){
        //         
        let subs = this.subs.slice()
        subs.forEach(item => {
            item.update()
        })
    }
}
class Watcher{
    constructor(vm, express, node){
        this.vm = vm  //vue   
        this.express = express  //   
        this.node = node  //   node  

        //     
        this.expressArr = []
        let str = this.express
        let data =  this.vm.data
        for(let key in data){
            if(str.indexOf(key) > -1){
                this.expressArr.push(key)
            }
        }
        this.addDep()
    }
    update(){
        //       
        this.node.innerText = this.get()
    }
    addDep(){
        //  
        //     Dep     depMap
        let arr = this.expressArr.slice()
        arr.forEach(item => {
            if(Object.keys(Dep.depMap).indexOf(item) > -1){
                Dep.depMap[item].addSub(this)
            }
        })
    }
    get(){
        //     
        let data = this.vm.data
        let _express = this.express
        let _expressArr = this.expressArr
        _expressArr.forEach(item => {
            _express = _express.replace(item, data[item])
        })
        _express = _express.replace('{{', '')
        _express = _express.replace('}}', '')
        return _express
    }
}