简易Vue

原理分析

通过遍历数据, 为数据创建一个发布器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
    }
}


你可能感兴趣的:(js,Vue)