vue的数据双向绑定的小例子:
。html
<!DOCTYPE html> <html> <head> <meta charset=utf-8> <title>vue数据双向绑定原理</title> </head> <body> <h1 id="name"><<<<name>>>>>></h1> </body> <script src="testvuejs/observer.js"></script> <script src="testvuejs/watcher.js"></script> <script src="testvuejs/index.js"></script> <script type="text/javascript"> var ele = document.querySelector('#name'); var selfVue = new SelfVue({ name: 'hello world' }, ele, 'name'); window.setTimeout(function () { console.log('name值改变了'); selfVue.name = 'canfoo'; }, 2000); </script> </html>
index.js
function SelfVue (data, el, exp) { var self = this; this.data = data; //把data里的key直接绑定到this对象上 Object.keys(data).forEach(function(key) { self.proxyKeys(key); }); //对data的每一层级的属性进行监听,如果变化执行notify observe(data); // 初始化模板数据的值 el.innerHTML = this.data[exp]; new Watcher(this, exp, function (value) { el.innerHTML = value; }); return this; } SelfVue.prototype = { proxyKeys: function (key) { Object.defineProperty(this, key, { enumerable: false, configurable: true, get: ()=> { return this.data[key]; }, set: (newVal)=> { this.data[key] = newVal; } }); } }
observer.js
function Observer(data) { this.data = data; this.walk(data); } Observer.prototype = { walk: function(data) { var self = this; Object.keys(data).forEach(function(key) { self.defineReactive(data, key, data[key]); }); }, defineReactive: function(data, key, val) { var dep = new Dep(); //对二级三级子属性...进行监听尽 observe(val); Object.defineProperty(data, key, { enumerable: true, configurable: true, get: function() { if (Dep.target) { dep.addSub(Dep.target); } return val; }, set: function(newVal) { if (newVal === val) { return; } val = newVal; dep.notify(); } }); } }; function observe(value, vm) { if (!value || typeof value !== 'object') { return; } return new Observer(value); }; function Dep () { this.subs = []; } Dep.prototype = { addSub: function(sub) { this.subs.push(sub); }, notify: function() { this.subs.forEach(function(sub) { sub.update(); }); } }; Dep.target = null;
watcher.js
function Watcher(vm, exp, cb) { this.cb = cb; this.vm = vm; this.exp = exp; //当new一个对象的时候,立即执行get方法,Dep的target为Watcher自己 this.value = this.get(); // 将自己添加到订阅器的操作 } Watcher.prototype = { update: function() { this.run(); }, run: function() { var value = this.vm.data[this.exp]; var oldVal = this.value; if (value !== oldVal) { this.value = value; this.cb.call(this.vm, value); } }, get: function() { Dep.target = this; // 缓存自己 var value = this.vm.data[this.exp] // this.vm.data[this.exp]:强制执行监听器里的get函数,使自己(Watcher {cb: ƒ, vm: SelfVue, exp: "name"})被添加上 Dep.target = null; // 释放自己 return value; } };
原理:当new vue后,将data属性直接给vm添加上,将属性的每一级进行set get 当set新值时通知notify函数。执行 new watcher ,强制执行data的get 使watch被添加上。
当data set新值时,触发notify函数,使所有watcher都执行update,watcher的update时,本地的value是旧值,取新值,回调函数更新view。