vue变化侦测_vue3 检测用户是否有修改,学习web开发培训

70 阅读5分钟

最后

整理面试题,不是让大家去只刷面试题,而是熟悉目前实际面试中常见的考察方式和知识点,做到心中有数,也可以用来自查及完善知识体系。

《前端基础面试题》,《前端校招面试题精编解析大全》,《前端面试题宝典》,《前端面试题:常用算法》

前端面试题宝典

前端校招面试题详解

开源分享:docs.qq.com/doc/DSmRnRG… const oldValue = this.value; this.value = this.get(); this.cb.call(this.vm, this.value, oldValue); } }

get方法中先把window.target设置成了this,也就是当前的watcher实例,然后再读一下data.a.b.c的值,这会触发getter。  
 触发getter就会触发收集依赖的逻辑。而关于收集依赖,会从window.target中读取一个依赖并添加到Dep中。  
 这就导致,只要先在window.target附一个this,然后再读一下值去触发getter就可以把this主动添加到keypath的Dep中。  
 依赖注入到Dep中后,每当data.a.b.c的值发生变化时,就会让依赖列表中所有的依赖循环触发update方法,也就是watcher中的update方法。而update方法会执行参数中的回调函数,将value和oldValue传到参数中。  
 所以,其实不管是用户执行的vm.$watch(‘a.b.c’, (value, oldValue) => {}),还是模板中用到的data,都是通过watcher来通知自己是否需要发生变化。  
 那么parsePath是怎么读取一个字符串的keypath的,下面来解释一下:



const bailRE = /[^\w.$]/; export function parsePath(path){ if(bailRE.test(path)){ return; } const segments = path.split(".") return function (obj){ for(let i = 0; i < segments.length; i++){ if(!obj){ return; } obj = obj[segments[i]]; } return obj; } }


可以看到,先将keypath用.分割成数组,然后循环数组一层一层去读数据,最后拿到的obj就是keyPath中想要读的数据。


### 2.递归侦测所有key


现在,其实已经可以实现变化侦测的功能了,但是前面介绍的代码只能侦测数据中的某一个属性,我们希望把数据中的所有属性(包括子属性)都侦测到,所以要封装一个Observer类。这个类的作用是将一个数据内的所有属性(包括子属性)都转换成getter/setter的形式,然后去追踪它们的变化:



export class Observer{ constructor(value) { this.value = value; if (!Array.isArray(value)){ this.walk(value) } }

walk(obj){
    const keys = Object.keys(obj);
    for (let i = 0; i < keys.length; i++){
        defineReactive(obj, keys[i], obj[keys[i]])
    }
}

}

function defineReactive(data, key, val) { if (typeof val == 'object'){ new Object(val); } let dep = new Dep(); Object.defineProperty(data, key, { enumerable: true, configurable: true, get: function(){ dep.depend(); return val; }, set: function(newVal){ if (val == newVal){ return; } val = newVal; dep.notify(); } })

}


在上面的代码中,我们定义了Observer类,它用来将一个正常的object转换成被侦测的object。  
 然后判断数据的类型,只有object类型的数据才会调用walk将每一个属性转换成getter/setter的形式来侦测变化。  
 最后,在defineReavtive中新增new Observer(val)来递归子属性,这样我们就可以把data中的所有属性(包括子属性)都转换成getter/setter 来侦测变化。


### 3.关于Object的问题


前面介绍了Object类型数据的变化侦测原理,了解了数据的变化是通过getter/setter来追踪的。也正是由于这种追踪方式,有些语法中即便是数据发生了变化,vue.is也追踪不到。  
 比如,向Object添加属性:



var vm = new Vue({ el: "#el", template: "#demo-tenplate", methods: { action(){ this.obj.name = "berwin"; } }, data: { obj: {} } })


在action方法中,我们在obj上面新增了name属性,vue无法侦测到这个变化,所以不会向依赖发送通知。  
 在比如,从obj中删除一个属性:



var vm = new Vue({ el: "#el", template: "#demo-tenplate", methods: { action(){ delete this.obj.name; } }, data: { name: 'berwin' } })


在上面的代码中,我们在action方法中删除了obj中的name属性,而vue无法侦测到这个变化,所以不会向依赖发送通知。  
 vue通过Object.defineProperty来将对象的key转换成getter/setter的形式来追踪变化,但getter/setter只能追踪一个数据是否被修改,无法追踪新增属性和删除属性,所以才会导致上面的例子中提到的问题。


### 4.总结


变化侦测就是侦测数据的变化,当数据吧发生变化时,要能侦测到并发出通知。  
 Object可以通过Object.defineProperty将属性转换成getter/setter的形式来追踪变化。读取数据时会触发getter,修改数据时会触发setter。  
 我们需要在getter中收集哪些依赖使用了数据,当setter被触发时,去通知getter中收集的依赖数据发生了变化。  
#### 总结一下

面试前要精心做好准备,简历上写的知识点和原理都需要准备好,项目上多想想难点和亮点,这是面试时能和别人不一样的地方。

还有就是表现出自己的谦虚好学,以及对于未来持续进阶的规划,企业招人更偏爱稳定的人。



万事开头难,但是程序员这一条路坚持几年后发展空间还是非常大的,一切重在坚持。

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://docs.qq.com/doc/DSmRnRGxvUkxTREhO)**

为了帮助大家更好更高效的准备面试,特别整理了《前端工程师面试手册》电子稿文件。

![](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/4e6cd23c33ed4e53ab47dc390dd8d9c0~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg55So5oi3MzM5MTQ5MjgwNjA=:q75.awebp?rk3s=f64ab15b&x-expires=1772535017&x-signature=XnCLnUY5kU%2FChp%2FKgfLaIM%2BNKQ8%3D)



![](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/8efce60ec78e4b65b1f52128407f25c5~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg55So5oi3MzM5MTQ5MjgwNjA=:q75.awebp?rk3s=f64ab15b&x-expires=1772535017&x-signature=2OSt4NM1h236VLxfhZpawPP%2B6Ks%3D)



**前端面试题汇总**

![](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/b0ecdb120b5e4b27b8862fbfb2f853ae~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg55So5oi3MzM5MTQ5MjgwNjA=:q75.awebp?rk3s=f64ab15b&x-expires=1772535017&x-signature=u9pa92uPEl3ZfsMozTUP6tk8Ihs%3D)