若是你在学习一种前端框架,如vue、angular等,那么你必定不会对数据的单向绑定陌生。html
传统开发模式下,如使用jQuery开发,咱们想将一个变量显示到html中,首先要定义一个变量name
,而后经过jq代码操做dom将变量放到HTML中,若是name
发生修改,还要再次经过jq代码操做dom将新的变量值放到HTML中。这就是传统的MVC框架,其中的Model和View是咱们经过代码联系在一块儿的。
在MVVM框架中,咱们再也不过多的关注数据与视图间的操做,而是使用一种新的机制,数据的单/双向绑定。
我在刚接触angular的时候感受这个绑定的机制简直不要再神奇,可是当慢慢深刻学习后发现其实原理很是简单,经过简短的js代码就能够实现一个简单的数据单向绑定。前端
Proxy能够当作在目标对象以前架设一层拦截,或者说是代理。任何对该对象的访问都要先通过这个代理。那么也就是说咱们能够经过Proxy对象拦截到外界对一个对象的访问。
ES6中将Proxy标准化了,提供了Proxy构造函数,用来生成Proxy实例。下面就是官方的定义:vue
let p = new Proxy(target, handler);
再看一个栗子:数组
let p = new Proxy({}, { get: function(target, name){ return name in target ? target[name] : 37; } }); p.a = 1; p.b = undefined; console.log(p.a, p.b); // 1, undefined console.log('c' in p, p.c); // false, 37
栗子中经过Proxy对象拦截了p对象的访问,当对象中不存在属性名时返回37,若是有就返回属性值。
到这已经了解了Proxy对象的工做方式,咱们就要用Proxy来作点事了。前端框架
首先
若是你接触过一些mvvm框架,那么必定会对下面的代码很是熟悉app
<div id="app"> 姓名:{{name}} <br> 年龄:{{age}} </div>
“{{}}”叫作插值表达式,vue、angular中都是使用这种方式进行数据的单向绑定。在这咱们也使用这种格式绑定数据。
而后框架
let el = document.getElementById('app'); let template = el.innerHTML;
上面两步,首先获取到根元素,而后将根元素下的html保存下来。这里为何要保存原始的html呢?
由于接下来的数据变化会从新编译标签,既然要从新编译,那么就要保留最初的状态,不然编译一次后,第二次就没法正常编译了。
再而后dom
let _data = { name: '_BuzzLy', age: 25 };
定义一个_data对象,这个对象是给咱们接下来建立的Proxy对象用的,并非暴露出去供访问的。
接下来mvvm
let data = new Proxy(_data, { // 试图设置数据时调用 // 参数:_data,属性名,值 set(obj, name, value) { obj[name] = value; // 数据变了 console.log(`数据变了,设置 ${name}=>${value}`); // 数据改变后从新渲染 render(); }, // 试图获取数据的时调用,默认要什么就返回什么 // get() {} });
这步咱们建立一个data,这个data就是对外的,是一个Proxy对象。
Proxy是原生的对象,能够将真正的数据对象隐藏,咱们修改的是代理对象。
至关于咱们想修改_data必定要通过一步代理,告诉代理咱们要修改的对象及修改的值,而后代理去帮咱们操做。
在修改完成后调用render函数去从新渲染视图。
最后
上面已经完成了最重要的部分,当一个数据发生改变后调用了render函数,这个函数就是将改变后的数据从新渲染到视图中去。函数
function render() { el.innerHTML = template.replace(/\{\{w+\}\}/g, str => { console.log(str); // 匹配出来的 {{name}} {{age}} // 截取字符串,获得属性key值 str = str.substring(2, str.length - 2); // 从真实数据中拿到对应属性的值返回,替换{{key}} return _data[str]; }) }
这里的实现很简单,就是匹配到html中{{key}},而后拿到key,再去_data中找到值便可。
注意这里的template,这就是咱们在上面为何要保存一份原始的html缘由。
如今你能够去尝试手动修改data的值看看效果了。
data.name = 'halo wode'; data.age = 18;
到这一个最简单的数据单向绑定就实现了。
完整代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title></title> </head> <body> <div id="app"> 姓名:{{name}} <br> 年龄:{{age}} </div> </body> <script> let el = document.getElementById('app'); let template = el.innerHTML; let _data = { name: '_BuzzLy', age: 25 }; let data = new Proxy(_data, { set(obj, name, value) { obj[name] = value; render(); } }); render(); function render() { el.innerHTML = template.replace(/\{\{\w+\}\}/g, str => { str = str.substring(2, str.length - 2); return _data[str]; }); } </script> </html>