如果你在学习一种前端框架,如vue、angular等,那么你一定不会对数据的单向绑定陌生。
何为数据的单向绑定?
传统开发模式下,如使用jQuery开发,我们想将一个变量显示到html中,首先要定义一个变量name
,然后通过jq代码操作dom将变量放到HTML中,如果name
发生修改,还要再次通过jq代码操作dom将新的变量值放到HTML中。这就是传统的MVC框架,其中的Model和View是我们通过代码联系在一起的。
在MVVM框架中,我们不再过多的关注数据与视图间的操作,而是使用一种新的机制,数据的单/双向绑定。
我在刚接触angular的时候感觉这个绑定的机制简直不要再神奇,但是当慢慢深入学习后发现其实原理非常简单,通过简短的js代码就可以实现一个简单的数据单向绑定。
Proxy对象
Proxy可以当做在目标对象之前架设一层拦截,或者说是代理。任何对该对象的访问都要先经过这个代理。那么也就是说我们可以通过Proxy对象拦截到外界对一个对象的访问。
ES6中将Proxy标准化了,提供了Proxy构造函数,用来生成Proxy实例。下面就是官方的定义:
let p = new Proxy(target, handler);
- target:用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)
- 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框架,那么一定会对下面的代码非常熟悉
<div id="app">
姓名:{{name}}
<br> 年龄:{{age}}
</div>
“{{}}”叫做插值表达式,vue、angular中都是使用这种方式进行数据的单向绑定。在这我们也使用这种格式绑定数据。
然后
let el = document.getElementById('app');
let template = el.innerHTML;
上面两步,首先获取到根元素,然后将根元素下的html保存下来。这里为什么要保存原始的html呢?
因为接下来的数据变化会重新编译标签,既然要重新编译,那么就要保留最初的状态,否则编译一次后,第二次就无法正常编译了。
再然后
let _data = {
name: '_BuzzLy',
age: 25
};
定义一个_data对象,这个对象是给我们接下来创建的Proxy对象用的,并不是暴露出去供访问的。
接下来
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>
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。