网络编程 发布日期:2025/11/8 浏览次数:1
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
实现方式是:
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
需要明白的是
怎么做呢?
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)
}
}
分析
的 dep.depend() ,如果发现 val 为数组,则将依赖加入到 observer 的 dep 中,也就实现了对当前数组的拦截
vm.$set
用法: vm.$set(target, 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)
作用
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()
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。