Vue侦测相关api
栏目: JavaScript · 发布时间: 6年前
内容简介:用法:从上面代码中可以看出当当执行
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
实现方式是:
-
将被访问到的数据
dep收集到watchs实例对象上,通过this.deps存起来 -
将被访问到的数据
dep.id收集到watchs实例对象上,通过this.depIds存起来 -
最后通过
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
需要明白的是
-
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)
}
}
分析
-
window.target = this,寄存依赖 -
let value = 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`处理
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)
作用
-
对于数组,进行
delete则是删除新元素,并需要触发依赖更新 -
对于对象,如果
key值不存在,直接return,存在,删除元素, -
对于对象本身不是响应式,则只删除
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()
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Vue 源码剖析 —— 变化侦测相关 API 实现原理
- 读vue的变化侦测
- Vue 源码剖析 —— 对象变化侦测
- 海康威视网络摄像头-预览出现绿色移动侦测规则框
- Sophos Intercept X EDR:智能、简单的端点侦测与响应
- WebAssembly 相关工具
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Beginning ASP.NET 4 in C# and Vb
Imar Spaanjaars / Wrox / 2010-3-19 / GBP 29.99
This book is for anyone who wants to learn how to build rich and interactive web sites that run on the Microsoft platform. With the knowledge you gain from this book, you create a great foundation to ......一起来看看 《Beginning ASP.NET 4 in C# and Vb》 这本书的介绍吧!