SOURCE

console 命令行工具 X clear

                    
>
console
function proxys(data, observebal) {
	if (!data || typeof data !== 'object') {
		return;
	}
	Object.keys(data).forEach((key, index) => {
		let val = data[key];
		proxys(val, observebal);
		Object.defineProperty(data, key, {
			configurable: true,
			enumerable: true,

			get: function() {
				console.log('get~~~!');
				Observebal.observer && observebal.addObserver(Observebal.observer);
				return val;
			},
			set: function(newValue) {
				console.log('set~~~!');
				proxys(newValue, observebal);
				val = newValue;
				observebal.notify();
			},
		});
	});
}

class Observebal {
	constructor(data) {
		proxys(data, this);
		this.observerIds = {};
		this.observers = [];
	}

	addObserver(observer) {
		if (!this.observerIds.hasOwnProperty(observer.id)) {
			this.observers.push(observer);
			this.observerIds[observer.id] = observer;
		}
	}
	notify() {
		this.observers.forEach((observer, index) => {
			observer.update();
		});
	}
}
//全局方法
Observebal.observer = null;
var globalObserverId = 0;
/*
	todo
	this.$expr = expr; 
*/
class Observer {
	constructor(vm, expr, cb) {
		this.$vm = vm;
		this.$expr = expr;
		this.$cb = cb;
		this.id = globalObserverId++;
		//加入队列
		this.getter(this.$vm, this.$expr);
	}
	update() {
		//todo 这边的getter要todo
		this.$cb(this.getter(this.$vm, this.$expr));
	}
	getter(vm, expr) {
		var exps = expr.split('.');
		//只要不进行ob.fff的修改就可以了
		let obj = vm;
		for (var i = 0, len = exps.length; i < len; i++) {
			if (!obj) return;
			obj = obj[exps[i]];
		}
		return obj;
	}
}
let vm2 = { a: 1, b: 2 };
let observal = new Observebal(vm2);
let observer1 = new Observer(vm2, 'a', value => {
	console.log('observer1', value);
});
let observer2 = new Observer(vm2, 'b', value => {
	console.log('observer2', value);
});
let value;
Observebal.observer = observer1;
value = vm2.a;
Observebal.observer = observer2;
value = vm2.a;
vm2.a = 22222;

class Compile {
	constructor(vm) {
		this.$vm = vm;
		let ele = this.$vm.$options.ele;
		let fragement = this.parseNode(ele);
		this.walkNodes(fragement);
		//初始化
		ele.appendChild(fragement);
	}
	parseNode(node) {
		let fragement = document.createDocumentFragment();
		let child;
		while ((child = node.firstChild)) {
			fragement.appendChild(child);
		}
		return fragement;
	}
	walkNodes(node) {
		let isElementNode = function(node) {
			return node.nodeType == 1;
		};

		let isTextNode = function(node) {
			return node.nodeType == 3;
		};
		if (isTextNode(node)) {
			let isMatch = /\{\{(.*)\}\}/.test(node.textContent || node.value);
			isMatch && this.bindCallBack(node, 'txt', this.$vm, RegExp.$1);
		}
		if (isElementNode(node)) {
			//this.bindCallBack(node,'')
			this.parseAttributes(node);
		}
		let childNodes = node.childNodes;
		for (let i = 0; i < childNodes.length; i++) {
			this.walkNodes(childNodes[i]);
		}
	}
	eventHandler(node, vm, exp, dir) {
		var eventType = dir.split(':')[1],
			fn = vm.$options.methods && vm.$options.methods[exp];

		if (eventType && fn) {
			node.addEventListener(eventType, fn.bind(vm), false);
		}
	}
	getVmVal(vm, exp) {
		var val = vm;
		exp = exp.split('.');
		exp.forEach(function(k) {
      //反复更新指针,反复执行getter,不执行setter
			val = val[k];
		});
		return val;
	}
	setVmVal(vm, exp, newValue) {
		var val = vm;
		exp = exp.split('.');
		exp.forEach(function(k, i) {
			// 非最后一个key,更新val的值
			if (i < exp.length - 1) {
        //反复执行getter
				val = val[k];
			} else {
        //最后再执行setter
				val[k] = newValue;
			}
		});
	}
	bindCallBack(node, type, vm, exp) {
		let updater = updator[type + 'Updater'];

		//todo exp 解析
		//初始化
		updater(node, this.getVmVal(vm, exp));
		//监听回调
		Observebal.observer = new Observer(vm, exp, newValue => {
			updater(node, newValue);
		});

		if (type == 'model') {
			node.addEventListener(
				'input',
				e => {
					this.setVmVal(vm, exp, e.target.value);
				},
				false
			);
		}

		//加入数组
		return this.getVmVal(vm, exp);
	}
	parseAttributes(node) {
		var nodeAttrs = node.attributes;

		Array.prototype.slice.call(nodeAttrs).forEach(attr => {
			var attrName = attr.name;
			if (attrName.indexOf('v-') == 0) {
				var exp = attr.value;
				var type = attrName.substring(2);
				// 事件指令
				if (type.indexOf('on') === 0) {
					this.eventHandler(node, this.$vm, exp, type);
				} else {
					// 普通指令
					this.bindCallBack(node, type, this.$vm, exp);
				}

				node.removeAttribute(attrName);
			}
		});
	}
}
var updator = {
	txtUpdater: function(node, value) {
		node.textContent = typeof value == 'undefined' ? '' : value;
	},

	htmlUpdater: function(node, value) {
		node.innerHTML = typeof value == 'undefined' ? '' : value;
	},

	classUpdater: function(node, value, oldValue) {
		var className = node.className;
		className = className.replace(oldValue, '').replace(/\s$/, '');

		var space = className && String(value) ? ' ' : '';

		node.className = className + space + value;
	},

	modelUpdater: function(node, value, oldValue) {
		node.value = typeof value == 'undefined' ? '' : value;
	},
};
function MVVM(options) {
	this.$options = options || {};
	var data = (this._data = this.$options.data);
	var me = this;

	// 数据代理
	// 实现 vm.xxx -> vm._data.xxx
	Object.keys(data).forEach(function(key) {
		me._proxyData(key);
	});
	//把computed的值提前的vm的一级维度
	this._initComputed();

	//命令数据为可以观察的
	new Observebal(data);
	// observe(data, this);

	this.$compile = new Compile(this);
}

MVVM.prototype = {
	$watch: function(key, cb, options) {
    //加入队列
		Observebal.observer = new Observer(this, key, cb);
	},
   //这里如果defineProperty定义了_data的key的话就会有循环引用
	_proxyData: function(key, setter, getter) {
		var me = this;
		setter =
			setter ||
			Object.defineProperty(me, key, {
				configurable: false,
				enumerable: true,
				get: function proxyGetter() {
					return me._data[key];
				},
				set: function proxySetter(newVal) {
					me._data[key] = newVal;
				},
			});
	},

	_initComputed: function() {
		var me = this;
		var computed = this.$options.computed;
		if (typeof computed === 'object') {
			Object.keys(computed).forEach(function(key) {
				Object.defineProperty(me, key, {
					get: typeof computed[key] === 'function' ? computed[key] : computed[key].get,
					set: function() {},
				});
			});
		}
	},
};
      var vm = new MVVM({
            ele: document.getElementById('mvvm-app'),
            data: {
                someStr: 'hello ',
                className: 'btn',
                htmlStr: '<span style="color: #f00;">red</span>',
                child: {
                    htmlStr: 'few',
                    someStr: 'World !'
                }
            },

            computed: {
                getHelloWord: function() {

                    return this.someStr + this.child.someStr;
                }
            },

            methods: {
                clickBtn: function(e) {
                    var randomStrArr = ['childOne', 'childTwo', 'childThree'];
                    this.child.someStr = randomStrArr[parseInt(Math.random() * 3)];
                    this.child.htmlStr = randomStrArr[parseInt(Math.random() * 3)];
                }
            }
        });

        vm.$watch('child.someStr', function() {
            console.log('few', arguments);
        });
        vm.$watch('child.someStr', function() {
            console.log('fewfewfww');
        });
<div id="mvvm-app">
        <input type="text" v-model="someStr">
        <input type="text" v-model="child.someStr">
        <p>{{getHelloWord}}</p>
        <p v-html="child.htmlStr"></p>
        <button v-on:click="clickBtn">change model</button>
    </div>