vue之mvvm模式实现原理

MVVM的设计模式是vue的核心思想,本文主要记录了实现思路。
index.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>vue-demo</title>
</head>
<body>
	<div id="vue-app">
        <input type="text" k-model="word">{{word}}
		<p><p>{{word}}</p></p>
	</div>

	<script src="./index.js"></script>
	<script>
		var vm = new Kue({
			el: '#vue-app',
			data: {
				word: 'Hello World!'
			}
        });
	</script>
</body>
</html>

index.js文件:

class Kue {
    constructor(options){
        this.$options = options
        //数据绑定
        this._data = options.data
        this.observer(options.data)
        this.compile(options.el)
    }
    //1,如何实现模板解析:找到{{}}中的属性,根据属性去data中找对应的属性值从而赋值给对应节点。
    compile(el){
        const nodeElement = document.querySelector(el)
        this.compileFun(nodeElement)
    }
    compileFun(element){
        const nodeChildren = element.childNodes
        // console.log(nodeChildren)
        Array.from(nodeChildren).forEach((node) => {
            if(node.nodeType == 3){
                //1,文本
                //思路:文本,利用正则匹配{{}}
                let nodeContent = node.textContent;
                const reg = /\{\{\s*(\S*)\s*\}\}/;
                if(reg.test(nodeContent)){
                    node.textContent = this._data[RegExp.$1]
                    new Watcher(this,RegExp.$1, newValue => {
                        node.textContent = newValue
                    });
                }
                
            }else if(node.nodeType == 1){
               //2,标签。分为2种情况。
                //对于标签中的{{}}值
                let nodeContent = node.textContent;
                const reg = /\{\{\s*(\S*)\s*\}\}/;
                if(reg.test(nodeContent)){
                    node.textContent = this._data[RegExp.$1]
                    new Watcher(this,RegExp.$1, newValue => {
                        node.textContent = newValue
                    });
                }
                //对于标签中的指令值即属性值 以k-model指定为例
                let attrs = node.attributes;
                Array.from(attrs).forEach((attr) => {
                    let attrName = attr.name;
                    let attrValue = attr.value;
                    if(attrName.indexOf('k-') == 0){
                        node.value = this._data[attrValue]
                        attrName = attrName.substr(2)
                        if(attrName == 'model'){
                            node.value = this._data[attrValue]
                        }
                        //从v到m层
                        //3,如何实现页面文本发生变化,从而data中数据发生变化
                        node.addEventListener('input',e => {
                            this._data[attrValue] = e.target.value
                        })
                        new Watcher(this,attrValue,newValue => {
                            node.value = newValue
                        })
                    }
                })
            }
            if (node.childNodes.length > 0) {
                this.compileFun(node);
            }
        })
    }

    //2,如何实现m到v变化: 当data中的数据发生变化时,视图层对应也会随之变化。利用发布订阅模式。

    //监听器:用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新
    observer(data){
        //思路:劫持data中的所有属性,使用Object.defineproperty
        Object.keys(data).forEach((key) => {
            let value = data[key];
            let dep = new Dep();//创建dep对象,即消息订阅器
            Object.defineProperty(data , key , {
                configurable: true,
                enumerable: true,
                get: function () {
                    if (Dep.target) {
                        dep.addSub(Dep.target);
                    }
                    return value
                },
                set: function (newValue) {
                    
                    //数据发生变化,告诉订阅者watcher去更新
                    dep.notify(newValue)
                    console.log("set", newValue);
                }
            })
        })
    }
}

//发布订阅模式。
//因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,
//然后在监听器Observer和订阅者Watcher之间进行统一管理的。
class Dep{
    constructor(){
        this.subs = []
    }
    addSub(sub){
        this.subs.push(sub)
    }
    //通知去更新消息
    notify(newValue){
        this.subs.forEach(sub => {
            sub.update(newValue);
        })
    }
}
//实现一个订阅者Watcher,连接Observer和Compile。
//可以订阅并收到每个属性的变化通知并执行指令绑定的相应函数,从而更新视图。
class Watcher{
    constructor(vm,exp,cb){
        Dep.target = this
        vm._data[exp]
        this.cb = cb
        Dep.target = null 
    }
    update(newValue){
        console.log("更新了", newValue);
        this.cb(newValue);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_43004614/article/details/90065561