vue实现数据劫持

213 阅读1分钟

1.首先新建一个html在创建一个Kvue.js

    <div id="app">
		{{ message }}
		<p>{{message}}</p>
		<input type="text" k-model="name">{{name}}
		<textarea k-model="age" name="" id="" cols="30" rows="10"></textarea>{{age}}
	</div>
	<script>
		let vm = new Kvue({
			el:'#app',
			data:{
				message: '数据展示',
				name: '李四',
				age: 20
			}
		})
	</script>

2.然后在Kvue.js新建一个class类

    class Kvue {
	constructor(options){
		this.$options = options;
		this._data = options.data;
		this.observer(this._data)
		this.compile(options.el)
	}
	observer (data){
		Object.keys(data).forEach(key=>{
			let value = data[key];
			let dep = new Dep();
			Object.defineProperty(data,key,{
				enumerable: true,
				configurable: true,
				get (){
					if (Dep.target){
						dep.addSub(Dep.target);
					}
					return value
				},
				set (newValue){
					if (newValue !== value)
					value = newValue
					dep.notify(newValue);
				}
			})
		})
	}
	compile (el){
		let element = document.querySelector(el);
		this.compileNdoe(element)
	}
	compileNdoe(element){
		let node = element.childNodes;
		Array.from(node).forEach(node=>{
			if (node.nodeType == 3){
				// 处理文本
				let nodeContext = node.textContent;
				let reg = /\{\{\s*(\S*)\s*\}\}/;
				if (reg.test(nodeContext)){
					node.textContent = this._data[RegExp.$1];
					new Watcher(this,RegExp.$1,newValue=>{
						// 视图更新
						node.textContent = newValue;
					});
				}
			} else if (node.nodeType == 1){
				let attrs = node.attributes;
				Array.from(attrs).forEach(attr=>{
					let attrName = attr.name;
					let attrValue = attr.value;
					if (attrName.indexOf('k-') == 0){
						attrName = attrName.substr(2);
						if (attrName == 'model'){
							node.value = this._data[attrValue]
							console.log(node.value)
						}
						node.addEventListener('input',e=>{
							this._data[attrValue] = e.target.value;
						})
						new Watcher(this,attrValue,newValue =>{
							node.value = newValue;
						})
					}
				})
				// 处理标签
			}
			if (node.childNodes.length > 0){
				this.compileNdoe(node);
			}
		})
	}
}
// 发布者
class Dep {
	constructor(){
		this.subs = []
	}
	addSub(sub){
		this.subs.push(sub)
	}
	notify(newValue){
		this.subs.forEach(v=>{
			v.update(newValue);
		})
	}
}
//订阅者
class Watcher {
	constructor(vm,exp,cb){
		Dep.target = this;
		vm._data[exp];
		this.cb = cb;
		Dep.target = null;
	}
	update(newValue){
		this.cb(newValue);
	}
}

3.通过Object.defineProperty来监听数据变化,通过获取dom元素来实现数据绑定,最后通过发布者和订阅者来驱动视图更新。