温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Vue侦测相关api的实现

发布时间:2021-06-03 16:34:51 来源:亿速云 阅读:129 作者:Leah 栏目:web开发

Vue侦测相关api的实现?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

vm.$watch

用法: vm.$watch( expOrFn, callback, [options] ) ,返回值为 unwatch 是一个函数用来取消观察;下面主要理解 options 中的两个参数 deep 和 immediate 以及 unwatch

Vue.prototype.$watch = function (expOrFn, cb, options) {   const vm = this   options = options || {}   const watcher = new Watcher(vm, expOrFn, cb, options)    if(options.immediate) {     cb.call(vm, watcher,.value)   }   return function unwatchFn() {     watcher.teardown()   } }

immediate

从上面代码中可以看出当 immediate 为 true 时,就会直接进行执行回调函数

unwatch

实现方式是:

  1. 将被访问到的数据 dep 收集到 watchs 实例对象上,通过 this.deps 存起来

  2. 将被访问到的数据 dep.id 收集到 watchs 实例对象上,通过 this.depIds 存起来

  3. 最后通过 watchs 实例对象的 teardown 进行删除

class Watcher {   constructor (vm, expOrFn, cb) {     this.vm = vm     this.deps = []     this.depIds = new Set()     if(typeof expOrFn === 'function') {       this.getter = expOrFn     }else {       this.getter = parsePath(expOrFn)     }     this.cb = cb     this.value = this.get()   }   ....   addDep (dep) {     const id = dep.id       //参数dep是Dep实例对象     if(!this.depIds.has(id)) {  //判断是否存在避免重复添加       this.depIds.add(id)           this.deps.push(dep)       dep.addSub(this)     //this 是依赖     }   }   teardown () {     let i = this.deps.length     while (i--) {       this.deps[i].removeSub(this)     }   } } let uid = 0 class Dep {   constructor () {     this.id = uid++     ...   }   ...   depend () {     if(window.target) {       window.target.addDep(this)  //将this即当前dep对象加入到watcher对象上     }   }   removeSub (sub) {     const index = this.subs.indexOf(sub)     if(index > -1) {       return this.subs.splice(index, 1)     }   } }

分析

当执行 teardown() 时需要循环;因为例如 expOrFn = function () { return this.name + this.age } ,这时会有两个 dep 分别是 name 与 age 分别都加入了 watcher 依赖( this ),都会加入到 this.deps 中,所以需要循环将含有依赖的 dep 都删除其依赖

deep

需要明白的是

  1. deep 干啥用的,例如 data = {arr: [1, 2, {b: 6]} ,当我们只是监听 data.arr 时,在 [1, 2, {b: 66}] 这个数值内部发生变化时,也需要触发,即 b = 888

怎么做呢?

class Watcher {   constructor (vm, expOrFn, cb, options) {     this.vm = vm     this.deps = []     this.depIds = new Set()     if(typeof expOrFn === '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     let value = this.getter.call(vm, vm)     if(this.deep) {       traverse(value)     }     window.target = undefined     return value   }   ... } const seenObjects = new Set() function traverse (val) {   _traverse(val, seenObjects)   seenObjects.clear() } function _traverse(val, seen) {   let i, keys   const isA = Array.isArray(val)   if((!isA && isObject(val)) || Object.isFrozen(val)) { //判断val是否是对象或者数组以及是否被冻结     return   }   if(val._ob_) {     const depId = val._ob_.dep.id   //可以看前面一篇我们对Observer类添加了this.dep = new Dep(),所以能访问其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)   } }

分析

  1. window.target = this ,寄存依赖

  2. let value = this.getter.call(vm, vm) 访问当前val,并执行 get

的 dep.depend() ,如果发现 val 为数组,则将依赖加入到 observer 的 dep 中,也就实现了对当前数组的拦截

  1. traverse(value) 也就是执行 _traverse(val, seenObjects) ;核心就是对被 Observer 的 val 通过 val[i] 通过这种操作,间接触发 get ,将依赖添加到当前数值的 dep 中,这样也就实现了,当内部数据发生变化,也会循环 subs 执行依赖的 update ,从而触发回调;当是数组时,只需进行遍历,看内部是否有 Object 对象即可,因为在第二步的时候,会对 val 进行判断是否是数组,变改变七个方法的value,在遍历;所以这边只要是内部数组都会进行拦截操作,添加依赖,即对象 {} 这种没没添加依赖。

  2. seenObjects.clear() 当内部所以类型数据都添加好其依赖后,就清空。

  3. window.target = undefined 消除依赖

vm.$set

用法: vm.$set(target, key, value)

作用

  1. 对于数组,进行 set 则是添加新元素,并需要触发依赖更新

  2. 对于对象,如果 key 值存在,则是修改 value ;不存在,则是添加新元素,需新元素要进行响应式处理,以及触发更新

  3. 对于对象本身不是响应式,则直接添加 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`处理     return val   }   if(key in targert && !(key in Object.prototype) { //能遍历并且是自身key     target[key] = val  //触发set,执行依赖更新     return val   }   const ob = target._ob_   if(target.isVue || (ob && ob.vm.Count) { //不是vue实例也不是vue实例的根对象(即不是this.$data跟对象)     //触发警告     return   }   if(!ob) {  //只添加     target[key] = val     return val   }   defineReactive(ob.value, key, val) //进行响应式处理   ob.dep.notify() //触发依赖更新   returnv val }

vm.$delete

用法: vm.$delete( target, key)

作用

  1. 对于数组,进行 delete 则是删除新元素,并需要触发依赖更新

  2. 对于对象,如果 key 值不存在,直接 return ,存在,删除元素,

  3. 对于对象本身不是响应式,则只删除 key-value ,无需其他处理

Vue.prototype.$delete = function (target, key) {   if(Array.isArray(target) && isValidArrayIndex(key)) {     target.splice(key, 1)     return   }   const ob = target._ob_   if(target.isVue || (ob && ob.vm.Count) { //不是vue实例也不是vue实例的根对象(即不是this.$data跟对象)     //触发警告     return   }   if(!hasOwn(target, key)) {     return   }   delete target[key]   if(!ob) {     return   }   ob.dep.notify() }

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注亿速云行业资讯频道,感谢您对亿速云的支持。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI