当前位置:   article > 正文

c语言watch窗口不见了,深入浅出vm.$watch和watch初始化

devc++addwatch窗口消失

目录

1.业务场景

2.用法

3.vm.$watch内部原理

4.deep参数实现原理

5.初始化watch

6.总结

1.业务场景

watch是用来监听某个数据发生变化,之后调用什么函数处理。一个数据可以去影响多个数据,比如说浏览器自适应、监控路由对象、监控自身属性变化等等。

2.用法

vm.$watch( expOrFn, callback, [options] )

定义:官方文档是这么写的:观察 Vue 实例上的一个表达式或者一个函数计算结果的变化。回调函数得到的参数为新值和旧值。表达式只接受监督的键路径。对于更复杂的表达式,用一个函数取代。

实例:

// 键路径

vm.$watch('a.b.c', function (newVal, oldVal) {

// 做点什么

})

// 函数

vm.$watch(

function () {

// 表达式 `this.a + this.b` 每次得出一个不同的结果时

// 处理函数都会被调用。

// 这就像监听一个未被定义的计算属性

return this.a + this.b

},

function (newVal, oldVal) {

// 做点什么

}

)

复制代码

选项deep:为了发现对象内部值的变化,可以在选项参数中指定 deep: true 。注意监听数组的变动不需要这么做。

vm.$watch('someObject', callback, {

deep: true

})

vm.someObject.nestedValue = 123

// callback is fired

复制代码

选项immediate:在选项参数中指定 immediate: true将立即以表达式的当前值触发回调。

vm.$watch('a', callback, {

immediate: true

})

// 立即以 `a` 的当前值触发回调

复制代码

返回值:vm.$watch会返回一个取消此监听的函数。

var unwatch = vm.$watch(

'value',

function () {

doSomething()

if (unwatch) {

unwatch()

}

},

{ immediate: true }

)

复制代码

3.vm.$watch内部原理

实际上,vm.$watch其实是对watcher的一种封装,watcher是什么,不太懂的可以参考我这篇文章:深入浅出vue变化侦测。

我们通过watcher完全可以实现watcher的功能,但同时,vm.$watch中的参数deep和immediate是没有的。下面我们看看vm.$watch是如何实现的:

Vue.prototype.$watch=function(exp,cb,options){

const vm=this

options=options||{}

const watcher=new Watcher(vm,exp,cb,options)

if(options.immediate){

cb.call(vm,watcher.value)

}

return function unwatchFn(){

watcher.teardown()

}

}

复制代码

逻辑也很简单,执行 new Watcher来实现vm.$watch基本功能的。

但是有一个细节,exp是可以接受函数的,所以watcher构造函数就要改一改。

export default class watcher{

constructor(vm,exp,cb){

this.vm=vm

//exp参数支持函数

if(typeof exp==='function'){

this.getter=exp

}else{

this.getter=parsePath(exp)

}

this.cb=cb

this.value=this.get()

}

......

}

复制代码

当exp为函数时,它不止可以动态返回数据,其中读取的所有数据都会被watcher观察到。任何一个发生变化时,wacther都会得到通知。

返回值:执行完vm.$watch后,返回一个函数unwatchFn,作用取消观察数据。既然要取消观察,我们就要知道watcher它自己都订阅了谁,也就是说watcher实例收集了哪些Dep。循环自己收集的Dep将自己从Dep中的依赖移除即可。

在watcher中添加addDep方法,作用是记录自己都订阅了哪些Dep:

export default class watcher{

constructor(vm,exp,cb){

this.vm=vm

//新增

this.deps=[]

// 新增

this.depIds=new Set()

if(typeof exp==='function'){

this.getter=exp

}else{

this.getter=parsePath(exp)

}

this.cb=cb

this.value=this.get()

}

//新增

addDep(dep){

const id=dep.id

// dep watcher产生互相关联

if(!this.depIds.has[id]){

this.depIds.add(id)

//watcher添加dep

this.deps.push(dep)

//dep添加watcher

dep.addSub(this)

}

}

......

}

复制代码

代码分析:我们使用了depIds来判断当前watcher已经订阅Dep,就不会重新订阅。当watcher读取value时,会触发收集依赖逻辑。通过this.depIds.add(id)来记录watcher已经订阅了这个Dep,通过this.deps.push(dep)记录自己都订阅了哪些Dep,最后触发dep.addSub(this)将自己添加到Dep中。

watcher中新增了addDep后,Dep中收集依赖的逻辑也需要改变:

let uid=0 //新增

export default class Dep{

constructor(){

this.id=uid++ //新增

this.subs=[]

}

depend(){

if(window.target){

this.addSub(window.target) //废弃

window.target.addDep(this) //新增

}

}

}

复制代码

代码分析:此时,Dep会记录数据发生变化时,需要通知哪些watcher,而watcher也记录了自己被哪些Dep通知。所以Dep和Watcher是多对多的关系

为什么是多对多的关系:我们知道当视图多次使用一个数据时,此时一个Dep对应多个watcher.同时,当一个watcher观察的参数是函数时,此时该函数使用了多个数据时,那么这个watcher就要收集多个Dep了。

unwatcher:我们已经知道watcher订阅了哪些Dep,就可以在Watcher中新增teardown方法来通知这些订阅,把他们从依赖列表中移除掉:

//从dep列表移除自己

teardown(){

let i=this.deps.length

while(i--){

this.deps[i].removeSub(this)

}

}

复制代码

dep中添加removeSub方法:

removeSub(sub){

const index=this.subs.indexOf(sub)

if(index>-1){

return this.subs.splice(index,1)

}

}

复制代码

以上就是unwatch的原理,当数据改变时,也不会通知已经删去的watcher了。

4deep参数原理实现

deep的功能就是除了要触发当前这个被监听数据的收集依赖逻辑之外,还要把这个值在内的所有子值都要触发一遍收集依赖,watcher更改如下:

export default class watcher{

constructor(vm,exp,cb){

this.vm=vm

//新增

if(options){

this.deep=!!options.deep

}else{

this.deep=false

}

this.deps=[]

this.depIds=new Set()

if(typeof exp==='function'){

this.getter=exp

}else{

this.getter=parsePath(exp)

}

this.cb=cb

this.value=this.get()

}

get(){

window.target=this

let value=this.getter.call(this.vm,this.vm)

//新增

if(this.deep){

traverse(value)

}

window.target=undefined

return value

}

}

复制代码

代码分析:如果用户使用了deep参数,会在window.target=undefined之前调用traverse来处理deep逻辑。否则,watcher就不会收集到子值的依赖列表中了。

traverse函数:其实这个函数很简单。就是采用递归的方式,要递归value所有子值来触发他们收集依赖功能。

5.初始化watch

类型:{ [key: string]: string | Function | Object | Array }

官网解释:一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个属性。

示例:

var vm = new Vue({

data: {

a: 1,

b: 2,

c: 3,

d: 4,

e: {

f: {

g: 5

}

}

},

watch: {

a: function (val, oldVal) {

console.log('new: %s, old: %s', val, oldVal)

},

// 方法名

b: 'someMethod',

// 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深

c: {

handler: function (val, oldVal) { /* ... */ },

deep: true

},

// 该回调将会在侦听开始之后被立即调用

d: {

handler: 'someMethod',

immediate: true

},

e: [

'handle1',

function handle2 (val, oldVal) { /* ... */ },

{

handler: function handle3 (val, oldVal) { /* ... */ },

/* ... */

}

],

// watch vm.e.f's value: {g: 5}

'e.f': function (val, oldVal) { /* ... */ }

}

})

vm.a = 2 // => new: 2, old: 1

复制代码

初始化分析:

初始化watch其实不难,watch和vm.$watch功能是相同的,所以只要循环watch选项,将对象每一项调用vm.$watch方法来实现。

但是watch选项的值同时支持字符串,函数,数组,对象。不同的类型有不同的用法,所以在调用vm.$watch时我们需要做一些适配。

function initWatch(vm,watch){

for(const key in watch){

const handler=watch[key]

if(Array.isArray(handler)){

for(let i=0;i

createWatcher(vm,key,handler[i])

}

}else{

createWatcher(vm,key,handler)

}

}

}

复制代码

代码分析:先把watch选项值分为两类,数组和其他。接着在调用createWatcher函数处理其他类型并调用vm.$watch创建观察表达式。

function createWatcher(vm,exp,handler,options){

if(isPlainObject(handler)){

options=handler

handler=handler.handler

}

if(typeof handler==='string'){

handler=vm[handler]

}

return vm.$watch(exp,handler,options)

}

复制代码

代码分析:处理了三种类型:

函数:不用特殊处理,直接传递给vm.$watch

字符串:从vm取出方法,将它赋值给handler

对象:options的值设置为handler,并且将变量handler设置为handler对象handler方法。

watch初始化原理就已经说完了。

6.总结

关于watch这一方面其实不是很难,主要是灵活运用了Watcher类和Dep类.

一天较真一个api,一天比一天进步。

b739ec46bb5c46d9c0aa4ce35ba1ea56.png

关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。

本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。

[深入浅出vm.$watch和watch初始化]http://www.zyiz.net/tech/detail-120079.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/106334
推荐阅读
相关标签
  

闽ICP备14008679号