简介
在前端的三大框架中都有数据响应式的影子,那它到底是怎样去实现(Vue 里面是 E5 语法 object.defindProperty)的呢?
先来看看 Vue 里面是如何使用双向绑定的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
{{LGD}}<br>
{{OG}}<br>
<input type="text" v-model="mydata"><br>
{{mydata}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
LGD: '老干爹是不可战胜的',
OG: '内部数据',
mydata: '双向绑定'
},
});
</script>
</body>
</html>
我们使用了 Vue 中提供的文本插值(后续会详细介绍)的方法来提供视图,
在输入框中输入数据,就可以实时更新我们的视图。
一起来尝试写出一个双向绑定吧~
一个小功能
Proxy代理与数据劫持
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class="box"></div>
<script>
let box = document.querySelector('.box')
let data = {
name: 'dingdang',
age: 18
}
box.innerHTML = data.age
data.age = 19
console.log(data);
</script>
</body>
</html>
直接开撸~咦,看到了没?上述代码确实可以做到:
数据改变=>视图改变
视图改变=>数据改变
但不是实时改变的,因为我们监听到双方任何一方的变化。。。
怎么办呢?
ES6 语法 Proxy 可以解决
Proxy代理
MDN : Proxy 对象用于定义基本操作的自定义行为。
看不懂是吧,我也看不懂~~
代理倒是知道,什么?你不知道?
emm,中介好吧!代理=中介
他的方法有很多,只列出要用的吧
Handler.set()
拦截对象的设置属性操作。
let p = new Proxy(target, {
set(target, property, newValue, receiver) {
}
});
target 目标对象
property 属性
newValue 要设置的值
receiver Proxy或者继承Proxy的对象(不常用)
Handler.get()
拦截对象的读取属性操作。
let p = new Proxy(target, {
get(target, property, receiver) {
}
});
target 目标对象
property 属性
receiver Proxy或者继承Proxy的对象(不常用)
Handler…
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class="box"></div>
<script>
let box = document.querySelector('.box')
let data = {
name: 'dingdang',
age: 18
}
/* box.innerHTML = data.age
data.age = 19
console.log(data);*/
let p = new Proxy(data, {
set(target, property, newValue) {
console.log('set...', target, property, newValue)
},
get(target, property,) {
console.log('get...', target, property,)
}
})
</script>
</body>
</html>
嘿,此时控制台什么都没打印!WTF?!
没有打印才是正确的,因为不管是 set 还是 get 都已经被我们拦截。所以什么都不会打印
最后加上对 p 的使用,就可以打印咯
p.age = 19
console.log(p.name)
我们要监控变化时,只需要监控对象 p 就可以啦。
等等,为什么 p.name 是 undefined 呢?
原因同上解释,被拦截了。那怎么才能打印出来呢?
答案:Reflect,只要把 arguments 里面的东西返回就可以啦
Reflect反射
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class="box"></div>
<script>
let box = document.querySelector('.box')
let data = {
name: 'dingdang',
age: 18
}
/* box.innerHTML = data.age
data.age = 19
console.log(data);*/
let p = new Proxy(data, {
set(target, property, newValue) {
console.log('set...', target, property, newValue)
return Reflect.set(...arguments)
},
get(target, property,) {
console.log('get...', target, property,)
return Reflect.get(...arguments)
}
})
p.age = 19
console.log(p.name)
</script>
</body>
</html>
Vue 数据响应式实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./vm.js"></script>
</head>
<body>
<div id="app">
{{name}}
<div>{{age}}</div>
</div>
<script>
let vm = new vue({
el: '#app',
data: {
name: 'dingdang',
age: 18
}
})
</script>
</body>
</html>
class vue {
//数据响应式
constructor(option) {
this.option = option
this._data = this.option.data
this.el = document.querySelector(this.option.el)
this.compileNode(this.el)
}
//工具函数
//正则匹配&修改视图
compileNode(el) {
let child = el.childNodes;
[...child].forEach(node => {
if (node.nodeType === 3) {
let text = node.textContent;
let reg = /\{\{\s*([^\s\{\}]+)\s*\}\}/g;
if (reg.test(text)) {
let $1 = RegExp.$1;
this._data[$1] && (node.textContent = text.replace(reg, this._data[$1]));
}
} else if (node.nodeType === 1) {
console.log('元素节点');
this.compileNode(node);
}
})
}
}
三个步骤
- 获取数据
- 在 HTML 里面匹配{{ }}双大括号模板
- 修改替换数据
现在我们不仅可以对数据进行代理,还实现了数据的响应式,就差最后一个双向绑定啦
其实只要把他们结合起来就可以了