<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
{{message}}
{{message}}
{{message}}
</div>
<script src="../Vue源码/vue-2.6.14/dist/vue.js"></script>
<script>
const app = new Vue({
el:'#app',
data(){
return{
message:'哈哈哈'
}
}
})
</script>
</body>
</html>
vue中数据发生改变,则界面也会发生更新改变。这就是响应式。
1.app.message修改数据时,Vue内部是如何实现监听message发生改变的
vue2采用的是Object.defineProperty进行数据劫持绑定,遍历data中所有对象通过Observer劫持监听所有属性,而vue3则是采用了Proxy对整个对象进行代理,在vue3中,会定义一个响应式函数reactive通过Proxy进行拦截,里面会有get和set方法。进行更改时需要使用.value进行更改
2.当数据发生改变时,Vue如何通知哪些地方进行刷新的。
vue2通过发布订阅者模式来通知页面进行更改数据。
观察者模式(发布-订阅模式)如下:
观察者(订阅者)- Watcher:
update():当事件发生时,具体需要做的事情;
目标(发布者)- Dep:
1.subs数组:用来存储所有的观察者;
2.addSub():用来添加观察者;
3.notify:当事件发生时,调用所有观察者的update()方法;
如下所示:
const obj ={}
Object.keys(obj).forEach(key =>{
let value = obj[key]
Object.defineProperty(obj, key,{
get(){
console.log('试图读取obj的a属性')
},
set(newValue){
console.log('试图改变obj的a属性')
属性变化时进⾏对
实例进⾏通知
//
Watcher
dep.notify()
}
})
})
发布者
//
class Dep{
constructor(){
this.subs =[]
}
addSub(watch){
this.subs.push(watch)
}
属性变化发送通知函数
//
notify(){
接受到通知后调⽤
函数进⾏视图的更新
//
update
this.subs.forEach(item =>{
item.update()
})
}
}
订阅者
//
class Watcher {
constructor(name){
this.name =name
}
update(){
console.log(this.name+'发⽣update');
}
}
模拟创建⼀个发布者
//
const dep =new Dep()
模拟创建⼀个订阅者
//
const watch1 =new Watcher('第⼀个实例')
将订阅者添加到发布者中
的数组⾥进⾏管理
//
subs
dep.addSub(watch1)
const watch2 =new Watcher('第⼆个实例')
dep.addSub(watch2)
const watch3 =new Watcher('第三个实例')
dep.addSub(watch3)
在其中的过程为:设置一个Observer监听器来对数据(data)中的所有属性进行劫持监听,如果属性发生了变化,则告诉订阅者Watcher看是否需要更新。因为订阅者有多个,所以需要一个消息订阅器(发布者)Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进⾏统⼀管理的。接着,我们还需要有⼀个指令解析器Compile,对每个节点元素进⾏扫描和解析,将相关指令对应初始化成⼀个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执⾏对应的更新函数,从⽽更新视图。
Vue3中的响应式原理
实现原理:
- 通过Proxy(代理)拦截:拦截对象中任意属性的变化,包括属性的读写,属性的添加,属性的删除等。
- 通过Reflect(反射):对源对象的属性进行操作。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
<script>
//源数据
let obj = {
name:'法外狂徒',
age:100
}
// 模拟vue3中的响应式
//Proxy第一个参数是源数据。第二个参数是配置
const p = new Proxy(obj,{
// 读取
get(target,propName){
console.log(`读取了p的${propName}属性`)
return target[propName]
},
// 修改,新增
set(target,propName,value){
console.log(`修改了p的${propName}属性,准备更新界面`)
return target[propName] = value
},
//删除
deleteProperty(target,propName){
console.log(`删除了p的${propName}属性,准备更新界面`)
return delete target[propName]
}
})
</script>
</html>
由此再描述一下Reflect(反射的意思),首先在一个对象obj中,有两个属性a和b,一般情况下读取a属性会使用obj.a来实现。而另一种方式则是使用Reflect.get(obj,a)。
而vue3则正是利用了Reflect实现了数据的响应式。