Vue的数据改变
原理
- 先将数据进行
Obj.definedProperty()
进行绑定 , 判断数据为数组或者对象进行绑定 - 当页面开始渲染会定义
updateComponent
方法,这个方法主要就是为了调用render
方法 - 进入
render
的时候会执行页面使用的值,这时候会触发该数据的get
属性 - 开始进行绑定依赖,大概如图示
- 之后通过
patch
方法将节点进行更新替换或者新增 - 如果数据发生改变,会执行数据的
set
方法,这时候数据的Dep.notify
会调用 Dep.notify
回去调用绑定的Watcher
,促使他更新,并且收集所有改变,一次性进行改变- 调用
Watcher
的 run()方法,run方法会调用
updateComponent方法进行
render`更新 - 简单实现代码如下
<!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='text'>o.b</div>
<script>
// Watcher
class Watcher {
constructor(updateComponent) {
this.newDepIds = new Set()
this.newDeps = []
this.depIds = new Set()
this.getter = updateComponent;
this.get()
}
addDep(dep) {
const id = dep.id
if (!this.newDepIds.has(id)) {
// watcher保存和它有关的dep
this.newDepIds.add(id)
this.newDeps.push(dep)
// 反过来
if (!this.depIds.has(id)) {
dep.addSub(this)
}
}
}
get(){
setTarget(this)
this.getter.call()
removeTarget(this)
}
update(){
this.get()
}
}
//Dep
var uid = 0
class Dep {
constructor() {
this.id = uid++;
this.subs = []
}
addSub(Watcher) {
this.subs.push(Watcher)
}
depend(){
Dep.target.addDep(this)
}
notify(){
for (let i = 0, l = this.subs.length; i < l; i++) {
this.subs[i].update()
}
}
}
function setTarget(Watcher) {
Dep.target = Watcher
}
function removeTarget(Watcher) {
Dep.target = null
}
// Observer
var data = {
o: { b: '666' }
}
function defineReactive(obj, key, value) {
// console.log(obj,key)
const dep = new Dep();
if (arguments.length == 2) {
value = obj[key]
}
// 递归遍历
new Observer(obj[key])
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
if(Dep.target){
dep.depend()
}
return value
},
set: function reactiveSetter(newVal) {
value = newVal
dep.notify()
}
})
}
function Observer(value) {
if (value instanceof Object) {
walk(value);
}
}
function walk(obj) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
//模拟Vue执行的过程 暂时用setTimeout函数
setTimeout(()=>{
new Observer(data)
new Watcher(function updateComponent(){
// .. render 渲染更新,这里简化了虚拟dom和diff算法的实现
console.log('...','发生改变继续调用')
document.getElementById('text').innerText = this.data.o.b
})
})
setTimeout(()=>{
this.data.o.b = 555
},2000)
</script>
</body>
</html>
- 如果中间有表述错误,请各位大佬指正 😆 😆
- 2021-12-06 修改
为this.dep = new Dep()
const dep = new Dep()