前言前言对于使用Vue的前端而言,watch、computed和methods三个属性相信是不陌生的,是日常开发中经常使用的属性。但是对于它们的区别及使用场景,又是否清楚,本文我将跟大家一起通过源码来分析这三者的背后实现原理,更进一步地理解它们所代表的含义。 在继续阅读本文之前,希望你已经具备了一定的Vue使用经验,如果想学习Vue相关知识,请移步至官网。WatchWatch我们先来找到watch的初始化的代码,/src/core/instance/state.js
export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props) // 初始化props
if (opts.methods) initMethods(vm, opts.methods) // 初始化方法
if (opts.data) {
initData(vm) // 先初始化data 重点
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed) // 初始化computed
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch) // 初始化watch
}
}

export function initState (vm: Component) {
vm._watchers = []
const opts = vm.$options
if (opts.props) initProps(vm, opts.props) // 初始化props
if (opts.methods) initMethods(vm, opts.methods) // 初始化方法
if (opts.data) {
initData(vm) // 先初始化data 重点
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed) // 初始化computed
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch) // 初始化watch
}
}
接下来我们深入分析一下initWatch的作用,不过在接下去之前,这里有一点是data的初始化是在computed和watch初始化之前,这是为什么呢?大家可以停在这里想一下这个问题。想不通也没关系,继续接下来的源码分析,这个问题也会迎刃而解。initWatchinitWatch
function initWatch (vm: Component, watch: Object) {
for (const key in watch) {
const handler = watch[key]
if (Array.isArray(handler)) { // 如果handler是一个数组

for (let i = 0; i < handler.length; i++) { // 遍历watch的每一项,执行createWatcher

createWatcher(vm, key, handler[i])

}
} else {

createWatcher(vm, key, handler)
}
}
}

function initWatch (vm: Component, watch: Object) {
for (const key in watch) {
const handler = watch[key]
if (Array.isArray(handler)) { // 如果handler是一个数组

for (let i = 0; i < handler.length; i++) { // 遍历watch的每一项,执行createWatcher

createWatcher(vm, key, handler[i])

}
} else {

createWatcher(vm, key, handler)
}
}
}
createWatchercreateWatcher
function createWatcher (
vm: Component,
expOrFn: string | Function,
handler: any,
options?: Object
) {
if (isPlainObject(handler)) { // 判断handler是否是纯对象,对options和handler重新赋值
options = handler
handler = handler.handler
}
if (typeof handler === 'string') { // handler用的是methods上面的方法,具体用法请查看官网文档
handler = vm[handler]
}
// expOrnFn: watch的key值, handler: 回调函数 options: 可选配置
return vm.$watch(expOrFn, handler, options) // 调用原型上的$watch
}

function createWatcher (
vm: Component,
expOrFn: string | Function,
handler: any,
options?: Object
) {
if (isPlainObject(handler)) { // 判断handler是否是纯对象,对options和handler重新赋值
options = handler
handler = handler.handler
}
if (typeof handler === 'string') { // handler用的是methods上面的方法,具体用法请查看官网文档
handler = vm[handler]
}
// expOrnFn: watch的key值, handler: 回调函数 options: 可选配置
return vm.$watch(expOrFn, handler, options) // 调用原型上的$watch
}
Vue.prototype.$watchVue.prototype.$watch
Vue.prototype.$watch = function (
expOrFn: string | Function,
cb: any,
options?: Object
): Function {
const vm: Component = this
if (isPlainObject(cb)) { // 判断cb是否是对象,如果是则继续调用createWatcher

return createWatcher(vm, expOrFn, cb, options)
}
options = options || {}
options.user = true // user Watcher的标示 options = { user: true, ...options }
const watcher = new Watcher(vm, expOrFn, cb, options) // new Watcher 生成一个user Watcher
if (options.immediate) { // 如果传入了immediate 则直接执行回调cb

try {

cb.call(vm, watcher.value)

} catch (error) {

handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)

}
}
return function unwatchFn () {

watcher.teardown()
}
}
}

Vue.prototype.$watch = function (
expOrFn: string | Function,
cb: any,
options?: Object
): Function {
const vm: Component = this
if (isPlainObject(cb)) { // 判断cb是否是对象,如果是则继续调用createWatcher

return createWatcher(vm, expOrFn, cb, options)
}
options = options || {}
options.user = true // user Watcher的标示 options = { user: true, ...options }
const watcher = new Watcher(vm, expOrFn, cb, options) // new Watcher 生成一个user Watcher
if (options.immediate) { // 如果传入了immediate 则直接执行回调cb

try {

cb.call(vm, watcher.value)

} catch (error) {

handleError(error, vm, `callback for immediate watcher "${watcher.expression}"`)

}
}
return function unwatchFn () {

watcher.teardown()
}
}
}
上面几个函数调用的逻辑都比较简单,所以就在代码上写了注释。我们重点关注一下这个userWatcher生成的时候做了什么。WatcherWatcher又来到了我们比较常见的Watcher类的阶段了,这次我们重点关注生成userWatch的过程。
export default class Watcher {
vm: Component;
expression: string;
cb: Function;
id: number;
deep: boolean;
user: boolean;
lazy: boolean;
sync: boolean;
dirty: boolean;
active: boolean;
deps: Array;
newDeps: Array;
depIds: SimpleSet;
newDepIds: SimpleSet;
before: ?Function;
getter: Function;
value: any;

constructor (
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
) {
this.vm = vm
if (isRenderWatcher) {

vm._watcher = this
}
vm._watchers.push(this)
// options
if (options) { // 在 new UserWatcher的时候传入了options,并且options.user = true

this.deep = !!options.deep

this.user = !!options.user

this.lazy = !!options.lazy

this.sync = !!options.sync

this.before = options.before
} else {

this.deep = this.user = this.lazy = this.sync = false
}
this.cb = cb
this.id = ++uid // uid for batching
this.active = true
this.dirty = this.lazy // for lazy watchers
this.deps = []
this.newDeps = []
this.depIds = new Set()
this.newDepIds = new Set()
this.expression = process.env.NODE_ENV !== 'production' // 一个函数表达式

? expOrFn.toString()

: ''
// parse expression for getter
if (typeof expOrFn === 'function') {

this.getter = expOrFn
} else {

this.getter = parsePath(expOrFn) // 进入这个逻辑,调用parsePath方法,对getter进行赋值

if (!this.getter) {

this.getter = noop

process.env.NODE_ENV !== 'production' && warn(

`Failed watching path: "${expOrFn}" ` +

'Watcher only accepts simple dot-delimited paths. ' +

'For full control, use a function instead.',

vm

)

}
}
this.value = this.lazy

? undefined

: this.get()
}
}

export default class Watcher {
vm: Component;
expression: string;
cb: Function;
id: number;
deep: boolean;
user: boolean;
lazy: boolean;
sync: boolean;
dirty: boolean;
active: boolean;
deps: Array;
newDeps: Array;
depIds: SimpleSet;
newDepIds: SimpleSet;
before: ?Function;
getter: Function;
value: any;

constructor (
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
) {
this.vm = vm
if (isRenderWatcher) {

vm._watcher = this
}
vm._watchers.push(this)
// options
if (options) { // 在 new UserWatcher的时候传入了options,并且options.user = true

this.deep = !!options.deep

this.user = !!options.user

this.lazy = !!options.lazy

this.sync = !!options.sync

this.before = options.before
} else {

this.deep = this.user = this.lazy = this.sync = false
}
this.cb = cb
this.id = ++uid // uid for batching
this.active = true
this.dirty = this.lazy // for lazy watchers
this.deps = []
this.newDeps = []
this.depIds = new Set()
this.newDepIds = new Set()
this.expression = process.env.NODE_ENV !== 'production' // 一个函数表达式

? expOrFn.toString()

: ''
// parse expression for getter
if (typeof expOrFn === 'function') {

this.getter = expOrFn
} else {

this.getter = parsePath(expOrFn) // 进入这个逻辑,调用parsePath方法,对getter进行赋值

if (!this.getter) {

this.getter = noop

process.env.NODE_ENV !== 'production' && warn(

`Failed watching path: "${expOrFn}" ` +

'Watcher only accepts simple dot-delimited paths. ' +

'For full control, use a function instead.',

vm

)

}
}
this.value = this.lazy

? undefined

: this.get()
}
}
首先会对这个watcher的属性进行一系列的初始化配置,接着判断expOrFn这个值,对于我们watch的key而言,不是函数所以会执行parsePath函数,该函数定义如下:
/**
* Parse simple path.
*/
const bailRE = new RegExp(`[^${unicodeRegExp.source}.$_\\d]`)
export function parsePath (path: string): any {
if (bailRE.test(path)) {
return
}
const segments = path.split('.')
return function (obj) {
for (let i = 0; i < segments.length; i++) { // 遍历数组

if (!obj) return

obj = obj[segments[i]] // 每次把当前的key值对应的值重新赋值obj
}
return obj
}
}

/**
* Parse simple path.
*/
const bailRE = new RegExp(`[^${unicodeRegExp.source}.$_\\d]`)
export function parsePath (path: string): any {
if (bailRE.test(path)) {
return
}
const segments = path.split('.')
return function (obj) {
for (let i = 0; i < segments.length; i++) { // 遍历数组

if (!obj) return

obj = obj[segments[i]] // 每次把当前的key值对应的值重新赋值obj
}
return obj
}
}
首先会判断传入的path是否符合预期,如果不符合则直接return,接着讲path根据'.'字符串进行拆分,因为我们传入的watch可能有如下几种形式:
watch: {

a: () {}
'formData.a': () {}
}

watch: {

a: () {}
'formData.a': () {}
}
所以需要对path进行拆分,接下来遍历拆分后的数组,这里返回的函数的参数obj其实就是vm实例,通过vm[segments[i]],就可以最终找到这个watch所对应的属性,最后将obj返回。
constructor () { // 初始化的最后一段逻辑

this.value = this.lazy // 因为this.lazy为false,所以会执行this.get方法

? undefined

: this.get()
}


get () {
pushTarget(this) // 将当前的watcher实例赋值给 Dep.target
let value
const vm = this.vm
try {

value = this.getter.call(vm, vm) // 这里的getter就是上文所讲parsePath放回的函数,并将vm实例当做第一个参数传入
} catch (e) {

if (this.user) {

handleError(e, vm, `getter for watcher "${this.expression}"`) // 如果报错了会这这一块逻辑

} else {

throw e

}
} finally {

// "touch" every property so they are all tracked as

// dependencies for deep watching

if (this.deep) { // 如果deep为true,则执行深递归

traverse(value)

}

popTarget() // 将当前watch出栈

this.cleanupDeps() // 清空依赖收集 这个过程也是尤为重要的,后续我会单独写一篇文章分析。
}
return value
}

constructor () { // 初始化的最后一段逻辑

this.value = this.lazy // 因为this.lazy为false,所以会执行this.get方法

? undefined

: this.get()
}


get () {
pushTarget(this) // 将当前的watcher实例赋值给 Dep.target
let value
const vm = this.vm
try {

value = this.getter.call(vm, vm) // 这里的getter就是上文所讲parsePath放回的函数,并将vm实例当做第一个参数传入
} catch (e) {

if (this.user) {

handleError(e, vm, `getter for watcher "${this.expression}"`) // 如果报错了会这这一块逻辑

} else {

throw e

}
} finally {

// "touch" every property so they are all tracked as

// dependencies for deep watching

if (this.deep) { // 如果deep为true,则执行深递归

traverse(value)

}

popTarget() // 将当前watch出栈

this.cleanupDeps() // 清空依赖收集 这个过程也是尤为重要的,后续我会单独写一篇文章分析。
}
return value
}
对于UserWatcher的初始化过程,我们基本上就分析完了,traverse函数本质就是一个递归函数,逻辑并不复杂,大家可以自行查看。 初始化过程已经分析完,但现在我们好像并不知道watch到底是如何监听data的数据变化的。其实对于UserWatcher的依赖收集,就发生在watcher.get方法中,通过this.getter(parsePath)函数,我们就访问了vm实例上的属性。因为这个时候已经initData,所以会触发对应属性的getter函数,这也是为什么initData会放在initWatch和initComputed函数前面。所以当前的UserWatcher就会被存放进对应属性Dep实例下的subs数组中,如下:
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {

const value = getter ? getter.call(obj) : val

if (Dep.target) {

dep.depend()

if (childOb) {

childOb.dep.depend()

if (Array.isArray(value)) {

dependArray(value)

}

}

}

return value
},
}

Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {

const value = getter ? getter.call(obj) : val

if (Dep.target) {

dep.depend()

if (childOb) {

childOb.dep.depend()

if (Array.isArray(value)) {

dependArray(value)

}

}

}

return value
},
}
前几个篇章我们都提到renderWatcher,就是视图的初始化渲染及更新所用。这个renderWathcer初始化的时机是在我们执行$mount方法的时候,这个时候又会对data上的数据进行了一遍依赖收集,每一个data的key的Dep实例都会将renderWathcer放到自己的subs数组中。如图:, 当我们对data上的数据进行修改时,就会触发对应属性的setter函数,进而触发dep.notify(),遍历subs中的每一个watcher,执行watcher.update()函数->watcher.run,renderWathcer的update方法我们就不深究了,不清楚的同学可以参考下我写的Vue数据驱动。 对于我们分析的UserWatcher而言,相关代码如下:Vue数据驱动
class Watcher {
constructor () {} //..
run () {
if (this.active) { // 用于标示watcher实例有没有注销

const value = this.get() // 执行get方法

if ( // 比较新旧值是否相同

value !== this.value ||

// Deep watchers and watchers on Object/Arrays should fire even

// when the value is the same, because the value may

// have mutated.

isObject(value) ||

this.deep

) {

// set new value

const oldValue = this.value

this.value = value

if (this.user) { // UserWatcher

try {

this.cb.call(this.vm, value, oldValue) // 执行回调cb,并传入新值和旧值作为参数

} catch (e) {

handleError(e, this.vm, `callback for watcher "${this.expression}"`)

}

} else {

this.cb.call(this.vm, value, oldValue)

}

}
}
}
}

class Watcher {
constructor () {} //..
run () {
if (this.active) { // 用于标示watcher实例有没有注销

const value = this.get() // 执行get方法

if ( // 比较新旧值是否相同

value !== this.value ||

// Deep watchers and watchers on Object/Arrays should fire even

// when the value is the same, because the value may

// have mutated.

isObject(value) ||

this.deep

) {

// set new value

const oldValue = this.value

this.value = value

if (this.user) { // UserWatcher

try {

this.cb.call(this.vm, value, oldValue) // 执行回调cb,并传入新值和旧值作为参数

} catch (e) {

handleError(e, this.vm, `callback for watcher "${this.expression}"`)

}

} else {

this.cb.call(this.vm, value, oldValue)

}

}
}
}
}
首先会判断这个watcher是否已经注销,如果没有则执行this.get方法,重新获取一次新值,接着比较新值和旧值,如果相同则不继续执行,若不同则执行在初始化时传入的cb回调函数,这里其实就是handler函数。至此,UserWatcher的工作原理就分析完了。接下来我们来继续分析ComputedWatcher,同样的我们找到初始代码Computed
Computed
initComputedinitComputed
const computedWatcherOptions = { lazy: true }

function initComputed (vm: Component, computed: Object) {
// $flow-disable-line
const watchers = vm._computedWatchers = Object.create(null) // 用来存放computedWatcher的map
// computed properties are just getters during SSR
const isSSR = isServerRendering()

for (const key in computed) {
const userDef = computed[key]
const getter = typeof userDef === 'function' ? userDef : userDef.get
if (process.env.NODE_ENV !== 'production' && getter == null) {

warn(

`Getter is missing for computed property "${key}".`,

vm

)
}

if (!isSSR) { // 不是服务端渲染

// create internal watcher for the computed property.

watchers[key] = new Watcher( // 执行new Watcher

vm,

getter || noop,

noop,

computedWatcherOptions { lazy: true }

)
}

// component-defined computed properties are already defined on the
// component prototype. We only need to define computed properties defined
// at instantiation here.
if (!(key in vm)) {
// 会在vm的原型上去查找computed对应的key值存不存在,如果不存在则执行defineComputed,存在的话则退出,
// 这个地方其实是Vue精心设计的
// 比如说一个组件在好几个文件中都引用了,如果不将computed

defineComputed(vm, key, userDef)
} else if (process.env.NODE_ENV !== 'production') {

if (key in vm.$data) {

warn(`The computed property "${key}" is already defined in data.`, vm)

} else if (vm.$options.props && key in vm.$options.props) {

warn(`The computed property "${key}" is already defined as a prop.`, vm)

}
}
}
}


const computedWatcherOptions = { lazy: true }

function initComputed (vm: Component, computed: Object) {
// $flow-disable-line
const watchers = vm._computedWatchers = Object.create(null) // 用来存放computedWatcher的map
// computed properties are just getters during SSR
const isSSR = isServerRendering()

for (const key in computed) {
const userDef = computed[key]
const getter = typeof userDef === 'function' ? userDef : userDef.get
if (process.env.NODE_ENV !== 'production' && getter == null) {

warn(

`Getter is missing for computed property "${key}".`,

vm

)
}

if (!isSSR) { // 不是服务端渲染

// create internal watcher for the computed property.

watchers[key] = new Watcher( // 执行new Watcher

vm,

getter || noop,

noop,

computedWatcherOptions { lazy: true }

)
}

// component-defined computed properties are already defined on the
// component prototype. We only need to define computed properties defined
// at instantiation here.
if (!(key in vm)) {
// 会在vm的原型上去查找computed对应的key值存不存在,如果不存在则执行defineComputed,存在的话则退出,
// 这个地方其实是Vue精心设计的
// 比如说一个组件在好几个文件中都引用了,如果不将computed

defineComputed(vm, key, userDef)
} else if (process.env.NODE_ENV !== 'production') {

if (key in vm.$data) {

warn(`The computed property "${key}" is already defined in data.`, vm)

} else if (vm.$options.props && key in vm.$options.props) {

warn(`The computed property "${key}" is already defined as a prop.`, vm)

}
}
}
}

defineComputeddefineComputednew Watcher的逻辑我们先放一边,我们先关注一下defineComputed这个函数到底做了什么
export function defineComputed (
target: any,
key: string,
userDef: Object | Function
) {
const shouldCache = !isServerRendering()
if (typeof userDef === 'function') { // 分支1
sharedPropertyDefinition.get = shouldCache

? createComputedGetter(key)

: createGetterInvoker(userDef)
sharedPropertyDefinition.set = noop
} else {
sharedPropertyDefinition.get = userDef.get

? shouldCache && userDef.cache !== false

? createComputedGetter(key)

: createGetterInvoker(userDef.get)

: noop
sharedPropertyDefinition.set = userDef.set || noop
}
if (process.env.NODE_ENV !== 'production' &&

sharedPropertyDefinition.set === noop) {
sharedPropertyDefinition.set = function () {

warn(

`Computed property "${key}" was assigned to but it has no setter.`,

this

)
}
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}

export function defineComputed (
target: any,
key: string,
userDef: Object | Function
) {
const shouldCache = !isServerRendering()
if (typeof userDef === 'function') { // 分支1
sharedPropertyDefinition.get = shouldCache

? createComputedGetter(key)

: createGetterInvoker(userDef)
sharedPropertyDefinition.set = noop
} else {
sharedPropertyDefinition.get = userDef.get

? shouldCache && userDef.cache !== false

? createComputedGetter(key)

: createGetterInvoker(userDef.get)

: noop
sharedPropertyDefinition.set = userDef.set || noop
}
if (process.env.NODE_ENV !== 'production' &&

sharedPropertyDefinition.set === noop) {
sharedPropertyDefinition.set = function () {

warn(

`Computed property "${key}" was assigned to but it has no setter.`,

this

)
}
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
这个函数本质也是调用Object.defineProperty来改写computed的key值对应的getter函数和setter函数,当访问到key的时候,就会触发其对应的getter函数,对于大部分情况下,我们会走到分支1,对于不是服务端渲染而言,sharedPropertyDefinition.get会被createComputedGetter(key)赋值,set会被赋值为一个空函数。createComputedGettercreateComputedGetter
function createComputedGetter (key) {
return function computedGetter () {
const watcher = this._computedWatchers && this._computedWatchers[key] // 就是上文中new Watcher()
if (watcher) {

if (watcher.dirty) {

watcher.evaluate()

}

if (Dep.target) {

watcher.depend()

}

return watcher.value
}
}
}

function createComputedGetter (key) {
return function computedGetter () {
const watcher = this._computedWatchers && this._computedWatchers[key] // 就是上文中new Watcher()
if (watcher) {

if (watcher.dirty) {

watcher.evaluate()

}

if (Dep.target) {

watcher.depend()

}

return watcher.value
}
}
}
可以看到createComputedGetter(key)其实会返回一个computedGetter函数,也就是说在执行render函数时,访问到这个vm[key]对应的computed的时候会触发getter函数,而这个getter函数就是computedGetter。

export default {

data () {

return {


a: 1,

b: 2

}
},
computed: {

message () { // 这里的函数名message就是所谓的key


return this.a + this.b

}
}
}


export default {

data () {

return {


a: 1,

b: 2

}
},
computed: {

message () { // 这里的函数名message就是所谓的key


return this.a + this.b

}
}
}
以上代码为例子,来一步步解析computedGetter函数。 首先我们需要先获取到key对应的watcher.
const watcher = this._computedWatchers && this._computedWatchers[key]

const watcher = this._computedWatchers && this._computedWatchers[key]
而这里的watcher就是在initComputed函数中所生成的。
if (!isSSR) { // 不是服务端渲染

// create internal watcher for the computed property.

watchers[key] = new Watcher( // 执行new Watcher

vm,

getter || noop,

noop,

computedWatcherOptions { lazy: true }

)
}

if (!isSSR) { // 不是服务端渲染

// create internal watcher for the computed property.

watchers[key] = new Watcher( // 执行new Watcher

vm,

getter || noop,

noop,

computedWatcherOptions { lazy: true }

)
}
我们来看看computedWatcher的初始化过程,我们还是接着来继续回顾一下Watcher类相关代码
export default class Watcher {
vm: Component;
expression: string;
cb: Function;
id: number;
deep: boolean;
user: boolean;
lazy: boolean;
sync: boolean;
dirty: boolean;
active: boolean;
deps: Array;
newDeps: Array;
depIds: SimpleSet;
newDepIds: SimpleSet;
before: ?Function;
getter: Function;
value: any;

constructor (
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
) {
this.vm = vm
if (isRenderWatcher) {

vm._watcher = this
}
vm._watchers.push(this)
// options
if (options) {

this.deep = !!options.deep

this.user = !!options.user

this.lazy = !!options.lazy // lazy = true

this.sync = !!options.sync

this.before = options.before
} else {

this.deep = this.user = this.lazy = this.sync = false
}
this.cb = cb
this.id = ++uid // uid for batching
this.active = true
this.dirty = this.lazy // for lazy watchers this.dirty = true 这里把this.dirty设置为true
this.deps = []
this.newDeps = []
this.depIds = new Set()
this.newDepIds = new Set()
this.expression = process.env.NODE_ENV !== 'production'

? expOrFn.toString()

: ''
// parse expression for getter
if (typeof expOrFn === 'function') { // 走到这一步

this.getter = expOrFn
} else {

// ..
}
this.value = this.lazy // 一开始不执行this.get()函数 直接返回undefined

? undefined

: this.get()
}

export default class Watcher {
vm: Component;
expression: string;
cb: Function;
id: number;
deep: boolean;
user: boolean;
lazy: boolean;
sync: boolean;
dirty: boolean;
active: boolean;
deps: Array;
newDeps: Array;
depIds: SimpleSet;
newDepIds: SimpleSet;
before: ?Function;
getter: Function;
value: any;

constructor (
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
) {
this.vm = vm
if (isRenderWatcher) {

vm._watcher = this
}
vm._watchers.push(this)
// options
if (options) {

this.deep = !!options.deep

this.user = !!options.user

this.lazy = !!options.lazy // lazy = true

this.sync = !!options.sync

this.before = options.before
} else {

this.deep = this.user = this.lazy = this.sync = false
}
this.cb = cb
this.id = ++uid // uid for batching
this.active = true
this.dirty = this.lazy // for lazy watchers this.dirty = true 这里把this.dirty设置为true
this.deps = []
this.newDeps = []
this.depIds = new Set()
this.newDepIds = new Set()
this.expression = process.env.NODE_ENV !== 'production'

? expOrFn.toString()

: ''
// parse expression for getter
if (typeof expOrFn === 'function') { // 走到这一步

this.getter = expOrFn
} else {

// ..
}
this.value = this.lazy // 一开始不执行this.get()函数 直接返回undefined

? undefined

: this.get()
}
紧接着回到computedGetter函数中,执行剩下的逻辑
if (watcher) {
if (watcher.dirty) {
watcher.evaluate()
}
if (Dep.target) {
watcher.depend()
}
return watcher.value
}

if (watcher) {
if (watcher.dirty) {
watcher.evaluate()
}
if (Dep.target) {
watcher.depend()
}
return watcher.value
}
首先判断watcher是否存在,如果存在则执行以下操作

判断watcher.dirty是否为true,如果为true,则执行watcher.evaluate

判断当前Dep.target是否存在,存在则执行watcher.depend

最后返回watcher.value
判断watcher.dirty是否为true,如果为true,则执行watcher.evaluate判断当前Dep.target是否存在,存在则执行watcher.depend最后返回watcher.value在computedWatcher初始化的时候,由于传入的options.lazy为true,所以相应的watcher.diry也为true,当我们在执行render函数的时候,访问到message,触发了computedGetter,所以会执行watcher.evaluate。
evaluate () {
this.value = this.get() // 这里的get() 就是vm['message'] 返回就是this.a + this.b的和
this.dirty = false // 将dirty置为false
}

evaluate () {
this.value = this.get() // 这里的get() 就是vm['message'] 返回就是this.a + this.b的和
this.dirty = false // 将dirty置为false
}
同时这个时候由于访问vm上的a属性和b属性,所以会触发a和b的getter函数,这样就会把当前这个computedWatcher加入到了a和b对应的Dpe实例下的subs数组中了。如图:接着当前的Dep.target毫无疑问就是renderWatcher了,并且也是存在的,所以就执行了watcher.depend()
depend () {
let i = this.deps.length
while (i--) {
this.deps[i].depend()
}
}

depend () {
let i = this.deps.length
while (i--) {
this.deps[i].depend()
}
}
对于当前的message computedWatcher而言,this.deps其实就是a和b两个属性对应的Dep实例,接着遍历整个deps,对每一个dep就进行depend()操作,也就是每一个Dep实例把当前的Dep.target(renderWatcher都加入到各自的subs中,如图:所以这个时候,一旦你修改了a和b的其中一个值,都会触发setter函数->dep.notify()->watcher.update,代码如下:
update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
this.run()
} else {
queueWatcher(this)
}
}

update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
this.run()
} else {
queueWatcher(this)
}
}
总结
总结
其实不管是watch还是computed本质上都是通过watcher来实现,只不过它们的依赖收集的时机会有所不同。就使用场景而言,computed多用于一个值依赖于其他响应式数据,而watch主要用于监听响应式数据,在进行所需的逻辑操作!大家可以通过单步调试的方法,一步步调试,能更好地加深理解。以上就是详解Vue中的watch和computed的详细内容,关于Vue watch和computed的资料请关注其它相关文章!