Vue侦测相关api的实现方法
vm.$watch
用法:vm.$watch(expOrFn,callback,[options]),返回值为unwatch是一个函数用来取消观察;下面主要理解options中的两个参数deep和immediate以及unwatch
Vue.prototype.$watch=function(expOrFn,cb,options){
constvm=this
options=options||{}
constwatcher=newWatcher(vm,expOrFn,cb,options)
if(options.immediate){
cb.call(vm,watcher,.value)
}
returnfunctionunwatchFn(){
watcher.teardown()
}
}
immediate
从上面代码中可以看出当immediate为true时,就会直接进行执行回调函数
unwatch
实现方式是:
- 将被访问到的数据dep收集到watchs实例对象上,通过this.deps存起来
- 将被访问到的数据dep.id收集到watchs实例对象上,通过this.depIds存起来
- 最后通过watchs实例对象的teardown进行删除
classWatcher{
constructor(vm,expOrFn,cb){
this.vm=vm
this.deps=[]
this.depIds=newSet()
if(typeofexpOrFn==='function'){
this.getter=expOrFn
}else{
this.getter=parsePath(expOrFn)
}
this.cb=cb
this.value=this.get()
}
....
addDep(dep){
constid=dep.id//参数dep是Dep实例对象
if(!this.depIds.has(id)){//判断是否存在避免重复添加
this.depIds.add(id)
this.deps.push(dep)
dep.addSub(this)//this是依赖
}
}
teardown(){
leti=this.deps.length
while(i--){
this.deps[i].removeSub(this)
}
}
}
letuid=0
classDep{
constructor(){
this.id=uid++
...
}
...
depend(){
if(window.target){
window.target.addDep(this)//将this即当前dep对象加入到watcher对象上
}
}
removeSub(sub){
constindex=this.subs.indexOf(sub)
if(index>-1){
returnthis.subs.splice(index,1)
}
}
}
分析
当执行teardown()时需要循环;因为例如expOrFn=function(){returnthis.name+this.age},这时会有两个dep分别是name与age分别都加入了watcher依赖(this),都会加入到this.deps中,所以需要循环将含有依赖的dep都删除其依赖
deep
需要明白的是
- deep干啥用的,例如data={arr:[1,2,{b:6]},当我们只是监听data.arr时,在[1,2,{b:66}]这个数值内部发生变化时,也需要触发,即b=888
怎么做呢?
classWatcher{
constructor(vm,expOrFn,cb,options){
this.vm=vm
this.deps=[]
this.depIds=newSet()
if(typeofexpOrFn==='function'){
this.getter=expOrFn
}else{
this.getter=parsePath(expOrFn)
}
if(options){//取值
this.deep=!!options.deep
}else{
this.deep=false
}
this.cb=cb
this.value=this.get()
}
get(){
window.target=this
letvalue=this.getter.call(vm,vm)
if(this.deep){
traverse(value)
}
window.target=undefined
returnvalue
}
...
}
constseenObjects=newSet()
functiontraverse(val){
_traverse(val,seenObjects)
seenObjects.clear()
}
function_traverse(val,seen){
leti,keys
constisA=Array.isArray(val)
if((!isA&&isObject(val))||Object.isFrozen(val)){//判断val是否是对象或者数组以及是否被冻结
return
}
if(val._ob_){
constdepId=val._ob_.dep.id//可以看前面一篇我们对Observer类添加了this.dep=newDep(),所以能访问其dep.id
if(seen.has(depId)){
return
}
seen.add(depId)
}
if(isA){
i=val.length
while(i--)_traverse(val[i],seen)
}else{
keys=Object.keys(val)
i=keys.length
while(i--)_traverse(val[i],seen)
}
}
分析
- window.target=this,寄存依赖
- letvalue=this.getter.call(vm,vm)访问当前val,并执行get
的dep.depend(),如果发现val为数组,则将依赖加入到observer的dep中,也就实现了对当前数组的拦截
- traverse(value)也就是执行_traverse(val,seenObjects);核心就是对被Observer的val通过val[i]通过这种操作,间接触发get,将依赖添加到当前数值的dep中,这样也就实现了,当内部数据发生变化,也会循环subs执行依赖的update,从而触发回调;当是数组时,只需进行遍历,看内部是否有Object对象即可,因为在第二步的时候,会对val进行判断是否是数组,变改变七个方法的value,在遍历;所以这边只要是内部数组都会进行拦截操作,添加依赖,即对象{}这种没没添加依赖。
- seenObjects.clear()当内部所以类型数据都添加好其依赖后,就清空。
- window.target=undefined消除依赖
vm.$set
用法:vm.$set(target,key,value)
作用
- 对于数组,进行set则是添加新元素,并需要触发依赖更新
- 对于对象,如果key值存在,则是修改value;不存在,则是添加新元素,需新元素要进行响应式处理,以及触发更新
- 对于对象本身不是响应式,则直接添加key-value,无需处理
Vue.prototype.$set=function(target,key,val){
if(Array.isArray(target)&&isValidArrayIndex(key)){//是数组并且key有效
target.length=Math.max(target.length,key)//处理key>target.length
target.splice(key,1,val)//添加新元素,并输出依赖更新同时新元素也会进行`Obsever`处理
returnval
}
if(keyintargert&&!(keyinObject.prototype){//能遍历并且是自身key
target[key]=val//触发set,执行依赖更新
returnval
}
constob=target._ob_
if(target.isVue||(ob&&ob.vm.Count){//不是vue实例也不是vue实例的根对象(即不是this.$data跟对象)
//触发警告
return
}
if(!ob){//只添加
target[key]=val
returnval
}
defineReactive(ob.value,key,val)//进行响应式处理
ob.dep.notify()//触发依赖更新
returnvval
}
vm.$delete
用法:vm.$delete(target,key)
作用
- 对于数组,进行delete则是删除新元素,并需要触发依赖更新
- 对于对象,如果key值不存在,直接return,存在,删除元素,
- 对于对象本身不是响应式,则只删除key-value,无需其他处理
Vue.prototype.$delete=function(target,key){
if(Array.isArray(target)&&isValidArrayIndex(key)){
target.splice(key,1)
return
}
constob=target._ob_
if(target.isVue||(ob&&ob.vm.Count){//不是vue实例也不是vue实例的根对象(即不是this.$data跟对象)
//触发警告
return
}
if(!hasOwn(target,key)){
return
}
deletetarget[key]
if(!ob){
return
}
ob.dep.notify()
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
热门推荐
7 简短的二胎祝福语
10 简短霸气女儿生日 祝福语
11 祝福语高考小众诗句简短
12 元旦祝福语20秒简短
13 老师过年祝福语大全 简短
14 一生祝福语简短
15 产检祝福语简短霸气
16 朋友店开张祝福语简短
17 生日爱情祝福语大全简短
18 朋友女儿生日祝福语 简短