DMQ 9 anni fa
parent
commit
c5efffe254
8 ha cambiato i file con 368 aggiunte e 394 eliminazioni
  1. BIN
      img/1.gif
  2. 159 157
      js/compile.js
  3. 27 27
      js/mvvm.js
  4. 59 60
      js/observer.js
  5. 50 50
      js/watcher.js
  6. 37 37
      mvvm.html
  7. 14 13
      readme.html
  8. 22 50
      readme.md

BIN
img/1.gif


+ 159 - 157
js/compile.js

@@ -1,186 +1,188 @@
 function Compile(el, vm) {
-	this.$vm = vm;
-	this.$el = this.isElementNode(el) ? el : document.querySelector(el);
-	this.$fragment = this.node2Fragment(this.$el);
-
-	this.init();
-
-	this.$el.appendChild(this.$fragment);
+    this.$vm = vm;
+    this.$el = this.isElementNode(el) ? el : document.querySelector(el);
+
+    if (this.$el) {
+        this.$fragment = this.node2Fragment(this.$el);
+        this.init();
+        this.$el.appendChild(this.$fragment);
+    }
 }
 
 Compile.prototype = {
-	node2Fragment: function(el) {
-		var fragment = document.createDocumentFragment(),
-			child;
-
-		// 将原生节点拷贝到fragment
-		while (child = el.firstChild) {
-			fragment.appendChild(child);
-		}
-
-		return fragment;
-	},
-
-	init: function() {
-		this.compileElement(this.$fragment);
-	},
-
-	compileElement: function(el) {
-		var childNodes = el.childNodes,
-			me = this;
-
-		[].slice.call(childNodes).forEach(function(node) {
-			var text = node.textContent;
-			var reg = /\{\{(.*)\}\}/;
-
-			if (me.isElementNode(node)) {
-				me.compile(node);
-
-			} else if (me.isTextNode(node) && reg.test(text)) {
-				me.compileText(node, RegExp.$1);
-			}
-
-			if (node.childNodes && node.childNodes.length) {
-				me.compileElement(node);
-			}
-		});
-	},
-	
-	compile: function(node) {
-		var nodeAttrs = node.attributes,
-			me = this;
-
-		[].slice.call(nodeAttrs).forEach(function(attr) {
-			var attrName = attr.name;
-			if (me.isDirective(attrName)) {
-				var exp = attr.value;
-				var dir = attrName.substring(2);
+    node2Fragment: function(el) {
+        var fragment = document.createDocumentFragment(),
+            child;
+
+        // 将原生节点拷贝到fragment
+        while (child = el.firstChild) {
+            fragment.appendChild(child);
+        }
+
+        return fragment;
+    },
+
+    init: function() {
+        this.compileElement(this.$fragment);
+    },
+
+    compileElement: function(el) {
+        var childNodes = el.childNodes,
+            me = this;
+
+        [].slice.call(childNodes).forEach(function(node) {
+            var text = node.textContent;
+            var reg = /\{\{(.*)\}\}/;
+
+            if (me.isElementNode(node)) {
+                me.compile(node);
+
+            } else if (me.isTextNode(node) && reg.test(text)) {
+                me.compileText(node, RegExp.$1);
+            }
+
+            if (node.childNodes && node.childNodes.length) {
+                me.compileElement(node);
+            }
+        });
+    },
+
+    compile: function(node) {
+        var nodeAttrs = node.attributes,
+            me = this;
+
+        [].slice.call(nodeAttrs).forEach(function(attr) {
+            var attrName = attr.name;
+            if (me.isDirective(attrName)) {
+                var exp = attr.value;
+                var dir = attrName.substring(2);
                 // 事件指令
-				if (me.isEventDirective(dir)) {
-					compileUtil.eventHandler(node, me.$vm, exp, dir);
-                // 普通指令
-				} else {
-					compileUtil[dir] && compileUtil[dir](node, me.$vm, exp);
-				}
-
-				node.removeAttribute(attrName);
-			}
-		});
-	},
-
-	compileText: function(node, exp) {
-		compileUtil.text(node, this.$vm, exp);
-	},
-
-	isDirective: function(attr) {
-		return attr.indexOf('v-') == 0;
-	},
+                if (me.isEventDirective(dir)) {
+                    compileUtil.eventHandler(node, me.$vm, exp, dir);
+                    // 普通指令
+                } else {
+                    compileUtil[dir] && compileUtil[dir](node, me.$vm, exp);
+                }
+
+                node.removeAttribute(attrName);
+            }
+        });
+    },
+
+    compileText: function(node, exp) {
+        compileUtil.text(node, this.$vm, exp);
+    },
+
+    isDirective: function(attr) {
+        return attr.indexOf('v-') == 0;
+    },
 
     isEventDirective: function(dir) {
         return dir.indexOf('on') === 0;
     },
 
-	isElementNode: function(node) {
-		return node.nodeType == 1;
-	},
+    isElementNode: function(node) {
+        return node.nodeType == 1;
+    },
 
-	isTextNode: function(node) {
-		return node.nodeType == 3;
-	}
+    isTextNode: function(node) {
+        return node.nodeType == 3;
+    }
 };
 
 // 指令处理集合
 var compileUtil = {
-	text: function(node, vm, exp) {
-		this.bind(node, vm, exp, 'text');
-	},
-
-	html: function(node, vm, exp) {
-		this.bind(node, vm, exp, 'html');
-	},
-
-	model: function(node, vm, exp) {
-		this.bind(node, vm, exp, 'model');
-
-		var me = this, val = this._getVMVal(vm, exp);
-		node.addEventListener('input', function(e) {
-			var newValue = e.target.value;
-			if (val === newValue) {
-				return;
-			}
-			
-			me._setVMVal(vm, exp, newValue);
-			val = newValue;
-		});
-	},
-
-	class: function(node, vm, exp) {
-		this.bind(node, vm, exp, 'class');
-	},
-
-	bind: function(node, vm, exp, dir) {
-		var updaterFn = updater[dir + 'Updater'];
-
-		updaterFn && updaterFn(node, this._getVMVal(vm, exp));
-
-		new Watcher(vm, exp, function(value, oldValue) {
-			updaterFn && updaterFn(node, value, oldValue);
-		});
-	},
+    text: function(node, vm, exp) {
+        this.bind(node, vm, exp, 'text');
+    },
+
+    html: function(node, vm, exp) {
+        this.bind(node, vm, exp, 'html');
+    },
+
+    model: function(node, vm, exp) {
+        this.bind(node, vm, exp, 'model');
+
+        var me = this,
+            val = this._getVMVal(vm, exp);
+        node.addEventListener('input', function(e) {
+            var newValue = e.target.value;
+            if (val === newValue) {
+                return;
+            }
+
+            me._setVMVal(vm, exp, newValue);
+            val = newValue;
+        });
+    },
+
+    class: function(node, vm, exp) {
+        this.bind(node, vm, exp, 'class');
+    },
+
+    bind: function(node, vm, exp, dir) {
+        var updaterFn = updater[dir + 'Updater'];
+
+        updaterFn && updaterFn(node, this._getVMVal(vm, exp));
+
+        new Watcher(vm, exp, function(value, oldValue) {
+            updaterFn && updaterFn(node, value, oldValue);
+        });
+    },
 
     // 事件处理
-	eventHandler: function(node, vm, exp, dir) {
-		var eventType = dir.split(':')[1],
+    eventHandler: function(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);
+        if (eventType && fn) {
+            node.addEventListener(eventType, fn.bind(vm), false);
         }
-	},
-
-	_getVMVal: function(vm, exp) {
-		var val = vm._data;
-		exp = exp.split('.');
-		exp.forEach(function(k) {
-			val = val[k];
-		});
-		return val;
-	},
-
-	_setVMVal: function(vm, exp, value) {
-		var val = vm._data;
-		exp = exp.split('.');
-		exp.forEach(function(k, i) {
-			// 非最后一个key,更新val的值
-			if (i < exp.length - 1) {
-				val = val[k];
-			} else {
-				val[k] = value;
-			}
-		});
-	}
+    },
+
+    _getVMVal: function(vm, exp) {
+        var val = vm._data;
+        exp = exp.split('.');
+        exp.forEach(function(k) {
+            val = val[k];
+        });
+        return val;
+    },
+
+    _setVMVal: function(vm, exp, value) {
+        var val = vm._data;
+        exp = exp.split('.');
+        exp.forEach(function(k, i) {
+            // 非最后一个key,更新val的值
+            if (i < exp.length - 1) {
+                val = val[k];
+            } else {
+                val[k] = value;
+            }
+        });
+    }
 };
 
 
 var updater = {
-	textUpdater: function(node, value) {
-		node.textContent = typeof value == 'undefined' ? '' : value;
-	},
+    textUpdater: function(node, value) {
+        node.textContent = typeof value == 'undefined' ? '' : value;
+    },
 
-	htmlUpdater: function(node, value) {
-		node.innerHTML = 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$/, '');
+    classUpdater: function(node, value, oldValue) {
+        var className = node.className;
+        className = className.replace(oldValue, '').replace(/\s$/, '');
 
-		var space = className && String(value) ? ' ' : '';
+        var space = className && String(value) ? ' ' : '';
 
-		node.className = className + space + value;
-	},
+        node.className = className + space + value;
+    },
 
-	modelUpdater: function(node, value, oldValue) {
-		node.value = typeof value == 'undefined' ? '' : value;
-	}
-};
+    modelUpdater: function(node, value, oldValue) {
+        node.value = typeof value == 'undefined' ? '' : value;
+    }
+};

+ 27 - 27
js/mvvm.js

@@ -1,35 +1,35 @@
-function MVVM(options){
-	this.$options = options;
-	var data = this._data = this.$options.data;
-	var me = this;
+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._proxy(key);
-	});
+    // 数据代理
+    // 实现 vm.xxx -> vm._data.xxx
+    Object.keys(data).forEach(function(key) {
+        me._proxy(key);
+    });
 
-	observe(data, this);
+    observe(data, this);
 
-	this.$compile = new Compile(options.el || document.body, this)
+    this.$compile = new Compile(options.el || document.body, this)
 }
 
 MVVM.prototype = {
-	$watch: function(key, cb, options) {
-		new Watcher(this, key, cb);
-	},
+    $watch: function(key, cb, options) {
+        new Watcher(this, key, cb);
+    },
 
-	_proxy: function(key) {
-		var me = this;
-		Object.defineProperty(me, key, {
-			configurable: true,
-			enumerable: true,
-			get: function proxyGetter() {
-				return me._data[key];
-			},
-			set: function proxySetter(newVal) {
-				me._data[key] = newVal;
-			}
-		});
-	}
+    _proxy: function(key) {
+        var me = this;
+        Object.defineProperty(me, key, {
+            configurable: true,
+            enumerable: true,
+            get: function proxyGetter() {
+                return me._data[key];
+            },
+            set: function proxySetter(newVal) {
+                me._data[key] = newVal;
+            }
+        });
+    }
 };

+ 59 - 60
js/observer.js

@@ -1,84 +1,83 @@
 function Observer(data) {
-	this.data = data;
-	this.walk(data);
+    this.data = data;
+    this.walk(data);
 }
 
 Observer.prototype = {
-	walk: function(data) {
-		var me = this;
-		Object.keys(data).forEach(function(key) {
-			me.convert(key, data[key]);
-		});
-	},
-	convert: function(key, val) {
-		this.defineReactive(this.data, key, val);
-	},
+    walk: function(data) {
+        var me = this;
+        Object.keys(data).forEach(function(key) {
+            me.convert(key, data[key]);
+        });
+    },
+    convert: function(key, val) {
+        this.defineReactive(this.data, key, val);
+    },
 
-	defineReactive: function(data, key, val) {
-		var dep = new Dep();
-		var childObj = observe(val);
+    defineReactive: function(data, key, val) {
+        var dep = new Dep();
+        var childObj = observe(val);
 
-		Object.defineProperty(data, key, {
-			enumerable: true,	// 可枚举
-			configurable: true,	// 不能再define
-			get: function() {
-				if (Dep.target) {
-					dep.depend();
-				}
-				return val;
-			},
-			set: function(newVal) {
-				if (newVal === val) {
-					return ;
-				}
-				val = newVal;
-				// 新的值是object的话,进行监听
-				childObj = observe(newVal);
-				// 通知订阅者
-				dep.notify();
-			}
-		});
-	}
+        Object.defineProperty(data, key, {
+            enumerable: true, // 可枚举
+            configurable: true, // 不能再define
+            get: function() {
+                if (Dep.target) {
+                    dep.depend();
+                }
+                return val;
+            },
+            set: function(newVal) {
+                if (newVal === val) {
+                    return;
+                }
+                val = newVal;
+                // 新的值是object的话,进行监听
+                childObj = observe(newVal);
+                // 通知订阅者
+                dep.notify();
+            }
+        });
+    }
 };
 
 function observe(value, vm) {
-	if (!value || typeof value !== 'object') {
-		return ;
-	}
+    if (!value || typeof value !== 'object') {
+        return;
+    }
 
-	return new Observer(value);
+    return new Observer(value);
 };
 
 
 var uid = 0;
 
 function Dep() {
-	this.id = uid++;
-	this.subs = [];
+    this.id = uid++;
+    this.subs = [];
 }
 
 Dep.prototype = {
-	addSub: function(sub) {
-		this.subs.push(sub);
-	},
+    addSub: function(sub) {
+        this.subs.push(sub);
+    },
 
-	depend: function() {
-		Dep.target.addDep(this);
-	},
+    depend: function() {
+        Dep.target.addDep(this);
+    },
 
-	removeSub: function(sub) {
-		var index = this.subs.indexOf(sub);
-		if (index != -1) {
-			this.subs.splice(index, 1);
-		}
-	},
+    removeSub: function(sub) {
+        var index = this.subs.indexOf(sub);
+        if (index != -1) {
+            this.subs.splice(index, 1);
+        }
+    },
 
-	notify: function() {
-		this.subs.forEach(function(sub){
-			sub.update();
-		});
-	}
+    notify: function() {
+        this.subs.forEach(function(sub) {
+            sub.update();
+        });
+    }
 };
 
-Dep.target = null;
-
+Dep.target = null;

+ 50 - 50
js/watcher.js

@@ -1,56 +1,56 @@
 function Watcher(vm, exp, cb) {
-	this.cb = cb;
-	this.vm = vm;
-	this.exp = exp;
-	this.depIds = {};
-	this.value = this.get();
+    this.cb = cb;
+    this.vm = vm;
+    this.exp = exp;
+    this.depIds = {};
+    this.value = this.get();
 }
 
 Watcher.prototype = {
-	update: function() {
-		this.run();
-	},
-	run: function() {
-		var value = this.get();
-		var oldVal = this.value;
-		if (value !== oldVal) {
-			this.value = value;
-			this.cb.call(this.vm, value, oldVal);
-		}
-	},
-	addDep: function(dep) {
-		// 1. 每次调用run()的时候会触发相应属性的getter
-		// getter里面会触发dep.depend(),继而触发这里的addDep
-		// 2. 假如相应属性的dep.id已经在当前watcher的depIds里,说明不是一个新的属性,仅仅是改变了其值而已
-		// 则不需要将当前watcher添加到该属性的dep里
-		// 3. 假如相应属性是新的属性,则将当前watcher添加到新属性的dep里
-		// 如通过 vm.child = {name: 'a'} 改变了 child.name 的值,child.name 就是个新属性
-		// 则需要将当前watcher(child.name)加入到新的 child.name 的dep里
-		// 因为此时 child.name 是个新值,之前的 setter、dep 都已经失效,如果不把 watcher 加入到新的 child.name 的dep中
-		// 通过 child.name = xxx 赋值的时候,对应的 watcher 就收不到通知,等于失效了
-		// 4. 每个子属性的watcher在添加到子属性的dep的同时,也会添加到父属性的dep
-		// 监听子属性的同时监听父属性的变更,这样,父属性改变时,子属性的watcher也能收到通知进行update
-		// 这一步是在 this.get() --> this.getVMVal() 里面完成,forEach时会从父级开始取值,间接调用了它的getter
-		// 触发了addDep(), 在整个forEach过程,当前wacher都会加入到每个父级过程属性的dep
-		// 例如:当前watcher的是'child.child.name', 那么child, child.child, child.child.name这三个属性的dep都会加入当前watcher
-		if (!this.depIds.hasOwnProperty(dep.id)) {
-			dep.addSub(this);
-			this.depIds[dep.id] = dep;
-		}
-	},
-	get: function() {
-		Dep.target = this;
-		var value = this.getVMVal();
-		Dep.target = null;
-		return value;
-	},
+    update: function() {
+        this.run();
+    },
+    run: function() {
+        var value = this.get();
+        var oldVal = this.value;
+        if (value !== oldVal) {
+            this.value = value;
+            this.cb.call(this.vm, value, oldVal);
+        }
+    },
+    addDep: function(dep) {
+        // 1. 每次调用run()的时候会触发相应属性的getter
+        // getter里面会触发dep.depend(),继而触发这里的addDep
+        // 2. 假如相应属性的dep.id已经在当前watcher的depIds里,说明不是一个新的属性,仅仅是改变了其值而已
+        // 则不需要将当前watcher添加到该属性的dep里
+        // 3. 假如相应属性是新的属性,则将当前watcher添加到新属性的dep里
+        // 如通过 vm.child = {name: 'a'} 改变了 child.name 的值,child.name 就是个新属性
+        // 则需要将当前watcher(child.name)加入到新的 child.name 的dep里
+        // 因为此时 child.name 是个新值,之前的 setter、dep 都已经失效,如果不把 watcher 加入到新的 child.name 的dep中
+        // 通过 child.name = xxx 赋值的时候,对应的 watcher 就收不到通知,等于失效了
+        // 4. 每个子属性的watcher在添加到子属性的dep的同时,也会添加到父属性的dep
+        // 监听子属性的同时监听父属性的变更,这样,父属性改变时,子属性的watcher也能收到通知进行update
+        // 这一步是在 this.get() --> this.getVMVal() 里面完成,forEach时会从父级开始取值,间接调用了它的getter
+        // 触发了addDep(), 在整个forEach过程,当前wacher都会加入到每个父级过程属性的dep
+        // 例如:当前watcher的是'child.child.name', 那么child, child.child, child.child.name这三个属性的dep都会加入当前watcher
+        if (!this.depIds.hasOwnProperty(dep.id)) {
+            dep.addSub(this);
+            this.depIds[dep.id] = dep;
+        }
+    },
+    get: function() {
+        Dep.target = this;
+        var value = this.getVMVal();
+        Dep.target = null;
+        return value;
+    },
 
-	getVMVal: function() {
-		var exp = this.exp.split('.');
-		var val = this.vm._data;
-		exp.forEach(function(k) {
-			val = val[k];
-		});
-		return val;
-	}
+    getVMVal: function() {
+        var exp = this.exp.split('.');
+        var val = this.vm._data;
+        exp.forEach(function(k) {
+            val = val[k];
+        });
+        return val;
+    }
 };

+ 37 - 37
mvvm.html

@@ -1,51 +1,51 @@
 <!DOCTYPE html>
 <html lang="en">
 <head>
-	<meta charset="UTF-8">
-	<title>MVVM</title>
+    <meta charset="UTF-8">
+    <title>MVVM</title>
 </head>
 <body>
 
 <div id="mvvm-app">
-	<input type="text" v-model="someStr">
-	<input type="text" v-model="child.someStr">
-	<p v-class="className" class="abc">
-		{{someStr}}
-		<span v-text="child.someStr"></span>
-	</p>
-	<p v-html="child.htmlStr"></p>
-	<button v-on:click="clickBtn">change model</button>
+    <input type="text" v-model="someStr">
+    <input type="text" v-model="child.someStr">
+    <p v-class="className" class="abc">
+        {{someStr}}
+        <span v-text="child.someStr"></span>
+    </p>
+    <p v-html="child.htmlStr"></p>
+    <button v-on:click="clickBtn">change model</button>
 </div>
 
-	<script src="http://cdn.bootcss.com/vue/1.0.25/vue.js"></script>
-	<script src="./js/observer.js"></script>
-	<script src="./js/watcher.js"></script>
-	<script src="./js/compile.js"></script>
-	<script src="./js/mvvm.js"></script>
-	<script>
-		var vm = new MVVM({
-			el: '#mvvm-app',
-			data: {
-				someStr: 'hello ',
-				className: 'btn',
-				htmlStr: '<span style="color: #f00;">red</span>',
-				child: {
-					someStr: 'World !'
-				}
-			},
+<script src="http://cdn.bootcss.com/vue/1.0.25/vue.js"></script>
+<script src="./js/observer.js"></script>
+<script src="./js/watcher.js"></script>
+<script src="./js/compile.js"></script>
+<script src="./js/mvvm.js"></script>
+<script>
+    var vm = new MVVM({
+        el: '#mvvm-app',
+        data: {
+            someStr: 'hello ',
+            className: 'btn',
+            htmlStr: '<span style="color: #f00;">red</span>',
+            child: {
+                someStr: 'World !'
+            }
+        },
 
-			methods: {
-				clickBtn: function(e) {
-					var randomStrArr = ['childOne', 'childTwo', 'childThree'];
-					this.child.someStr = randomStrArr[parseInt(Math.random() * 3)];
-				}
-			}
-		});
+        methods: {
+            clickBtn: function(e) {
+                var randomStrArr = ['childOne', 'childTwo', 'childThree'];
+                this.child.someStr = randomStrArr[parseInt(Math.random() * 3)];
+            }
+        }
+    });
 
-		vm.$watch('child.someStr', function() {
-			console.log(arguments);
-		});
-	</script>
+    vm.$watch('child.someStr', function() {
+        console.log(arguments);
+    });
+</script>
 
 </body>
 </html>

File diff suppressed because it is too large
+ 14 - 13
readme.html


+ 22 - 50
readme.md

@@ -1,47 +1,21 @@
-## 透过Vue, 如何实现一个简单的mvvm双向数据绑定
+## 剖析Vue实现原理 - 如何实现双向绑定mvvm
 
-> 本文主要是在分析Vue源码的基础上,对其相关核心思想和逻辑进行简化,并通过实现一个简单的实现来阐述相关原理和思想,文中并不会涉及太多源码片段的解析,但其核心思想都会在文中表现出来,对阅读Vue源码会有更好的帮助,相信会让你的思路更加清晰~
+> 本文能帮你做什么?
+1、了解vue的双向数据绑定原理以及核心代码模块
+2、缓解好奇心的同时了解如何实现双向绑定
+> 为了便于说明原理与实现,本文相关代码主要摘自[vue源码](https://github.com/vuejs/vue), 并进行了简化改造,相对较简陋,并未考虑到数组的处理、数据的循环依赖等,也难免存在一些问题,欢迎大家指正。不过这些并不会影响大家的阅读和理解,相信对大家在阅读vue源码的时候会更有帮助
+> 本文所有相关代码均在github上面可找到 [https://github.com/DMQ/mvvm](https://github.com/DMQ/mvvm)
+
+##### 相信大家对mvvm双向绑定应该都不陌生了,什么?还没了解过?骚年,out了,赶紧google一下吧。
+一言不合就上代码,下面先看一个本文最终实现的效果吧,和vue是一样的语法
 
-#### 1、一个简单的Vue例子:[Hello World!](./vue-demo/index.html)
-**code:** 
 ```
-<div id="vue-app">
+<div id="mvvm-app">
 	<input type="text" v-model="word">
 	<p>{{word}}</p>
 	<button v-on:click="sayHi">change model</button>
 </div>
 
-<script src="http://cdn.bootcss.com/vue/1.0.25/vue.js"></script>
-<script>
-	var vm = new Vue({
-		el: '#vue-app',
-		data: {
-			word: 'Hello World!'
-		},
-
-		methods: {
-			sayHi: function() {
-				this.word = 'Hi, everybody!';
-			}
-		}
-	});
-</script>
-```
-
-#### 2、如题,今天要跟大家分享的就是实现上面的功能,是这样子的:[My mvvm](./mvvm.html)
-**code:**
-```
-<div id="mvvm-app">
-	<input type="text" v-model="name">
-	<input type="text" v-model="child.name">
-	<p v-class="className" class="abc">
-		{{child.child.name}}
-	</p>
-	<span v-text="child.name"></span>
-	<p v-html="child.html"></p>
-	<button v-on:click="clickBtn">change model</button>
-</div>
-
 <script src="./js/observer.js"></script>
 <script src="./js/watcher.js"></script>
 <script src="./js/compile.js"></script>
@@ -50,28 +24,25 @@
 	var vm = new MVVM({
 		el: '#mvvm-app',
 		data: {
-			name: 'hello ',
-			className: 'btn',
-			spanText: 'hello world!',
-			child: {
-				name: '孩子名字',
-				html: '<span style="color: #f00;">red</span>',
-				child: {
-					name: '孩子的孩子名字 '
-				}
-			}
+			word: 'Hello World!'
 		},
-
 		methods: {
-			clickBtn: function(e) {
-				var randomStrArr = ['childOne', 'childTwo', 'childThree'];
-				this.child.name = randomStrArr[parseInt(Math.random() * 3)];
+			sayHi: function() {
+				this.word = 'Hi, everybody!';
 			}
 		}
 	});
 </script>
 ```
 
+效果:
+![demo1][demo1]
+
+
+##### 几种实现双向绑定的做法
+目前几种较为主流的双向绑定实现
+
+
 #### 3、目前实现数据绑定的几种做法
 
 
@@ -152,3 +123,4 @@ defineReative(data);
 
 [code](./js/watcher.js)
 
+[demo1]: ./img/1.gif

Some files were not shown because too many files changed in this diff