導(dǎo)航
[深入01] 執(zhí)行上下文
[深入02] 原型鏈
[深入03] 繼承
[深入04] 事件循環(huán)
[深入05] 柯里化 偏函數(shù) 函數(shù)記憶
[深入06] 隱式轉(zhuǎn)換 和 運(yùn)算符
[深入07] 瀏覽器緩存機(jī)制(http緩存機(jī)制)
[深入08] 前端安全
[深入09] 深淺拷貝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模塊化
[深入13] 觀察者模式 發(fā)布訂閱模式 雙向數(shù)據(jù)綁定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手寫Promise
[深入20] 手寫函數(shù)
[部署01] Nginx
[部署02] Docker 部署vue項(xiàng)目
[部署03] gitlab-CI
[源碼-webpack01-前置知識(shí)] AST抽象語(yǔ)法樹
[源碼-webpack02-前置知識(shí)] Tapable
[源碼-webpack03] 手寫webpack - compiler簡(jiǎn)單編譯流程
[源碼] Redux React-Redux01
[源碼] axios
[源碼] vuex
[源碼-vue01] data響應(yīng)式 和 初始化渲染
[源碼-vue02] computed 響應(yīng)式 - 初始化,訪問(wèn),更新過(guò)程
[源碼-vue03] watch 偵聽屬性 - 初始化和更新
[源碼-vue04] Vue.set 和 vm.$set
[源碼-vue05] Vue.extend
[源碼-vue06] Vue.nextTick 和 vm.$nextTick
前置知識(shí)
學(xué)習(xí)目標(biāo)
- computed計(jì)算屬性只有在computed被訪問(wèn)時(shí),才會(huì)去計(jì)算
- 因?yàn)樵趎ew Watcher是computed watcher時(shí),即lazy=true時(shí),在構(gòu)造函數(shù)中沒有立即執(zhí)行g(shù)et()方法,而是在計(jì)算屬性被訪問(wèn)時(shí)觸發(fā)computed的響應(yīng)式get后,執(zhí)行的get方法中回去調(diào)用computed getter函數(shù)
- computed計(jì)算屬性具有緩存功能
- 通過(guò)dirty=true時(shí),才會(huì)去執(zhí)行watcher.evaluate()方法,才會(huì)執(zhí)行computed wawtcher中的get()即computd 定義的函數(shù),從新計(jì)算,計(jì)算完后,將this.dirty=false
- 下次再訪問(wèn)時(shí),會(huì)先判斷dirty,是false就直接返回緩存的值
- computed的依賴必須是響應(yīng)式數(shù)據(jù),不然即使依賴變化不會(huì)觸發(fā)computed重新計(jì)算
- 即使computed的依賴變化了,但是computed計(jì)算的值沒有變化的話,不會(huì)從新渲染
- computed watcher 和 render watcher 和 dep 和 dep.subs 和 watcher.deps 之間復(fù)雜的關(guān)系
- computed訪問(wèn)過(guò)程
- 訪問(wèn)computed,觸發(fā)computed響應(yīng)式的get函數(shù),get函數(shù)中判斷如果dirty=true,那么執(zhí)行computed watchet的evalute方法,即watcher中的get()方法,然后把 Dep.target = computed watcher,執(zhí)行computed getter函數(shù)就是用戶指定的computed函數(shù)
- 執(zhí)行computed getter函數(shù)的時(shí),因?yàn)橛幸蕾嚨捻憫?yīng)式的data,所以又會(huì)觸發(fā)data的get函數(shù),執(zhí)行dep.depend(),就是把computed watcher中的 newDeps中添加computed依賴項(xiàng)的 dep,同時(shí)向computed依賴項(xiàng)的dep的subs中添加computed watcher,然后又把Dep.target = targetStack數(shù)組中的前一個(gè)watcher,然后返回計(jì)算的結(jié)果,并且把 dirty=false
- 然后判斷 Dep.taret 存在,就執(zhí)行 computed watcher的depend方法,循環(huán)遍歷computed watcher中的deps,取出dep,執(zhí)行dep.depend
- 因?yàn)榇藭r(shí)的 Dep.target = render watcher,所以dep.depend會(huì)向render watcher的 newDeps中添加data的dep,向data的dep中的subs中添加render watcher,那么此時(shí)的computed計(jì)算屬性的依賴項(xiàng)的dep中的subs就是[computed watcher, render watcher]這樣就保證了渲染的時(shí)候,computed先于render先執(zhí)行,保證computed有值
- comuted更新過(guò)程
- 上面的例子中,change的過(guò)程,是改變了computed的依賴,之后的一系列流程
- 觸發(fā)computed的響應(yīng)式依賴的dep.notify()方法,循環(huán)遍歷該依賴的dep.subs數(shù)組中的每一個(gè)watcher.update()方法,在上面的例子中subs=[computed watcher, render watcher]所以先執(zhí)行computed watcher
- compued watcher
- 在update方法中,會(huì)判斷l(xiāng)azy=true?,因?yàn)閏omputed watcher的lazy=true,所以執(zhí)行 dirty=true
- render watcher
- 在update方法中, lazy不為true, 會(huì)執(zhí)行 queueWatcher(this),就是調(diào)用渲染watcher重新求值和渲染
- computed訪問(wèn)過(guò)程
一些單詞
internal:內(nèi)部的
Scheduler:調(diào)度器
queue:隊(duì)列
( flushSchedulerQueue : 刷新調(diào)度器隊(duì)列 )
computed源碼
(1) computed的初始化
-
Vue.prototype._init=>initState(vm)=>initComputed(vm, opts.computed)
(1-1) initComputed(vm, opts.computed)
-
主要做了以下事情
-
<font color=blue size=5>(1-1-1) new Watcher(vm, getter, noop, computedWatcherOptions) </font>
- new了一個(gè) computed watcher
- 參數(shù)
- computedWatcherOptions
- 如何知道是一個(gè)computed watcher => 主要通過(guò)第4個(gè)參數(shù)computedWatcherOptions => { lazy: true },即comoputed watcher的 lazy屬性是true
- getter
- 就是用戶自己定義的computed對(duì)象中的函數(shù)
- noop
- 是一個(gè)空函數(shù)
- computedWatcherOptions
- 注意點(diǎn):
- 在 new Watcher 計(jì)算屬性watcher的構(gòu)造函數(shù)中
this.value=this.lazy ? undefined : this.get()- 因?yàn)橛?jì)算屬性watcher的lazy=true,所以不會(huì)立即去執(zhí)行 get() 方法
- <font color=blue>那什么時(shí)候去執(zhí)行呢get()呢?</font>
- <font color=blue>執(zhí)行時(shí)機(jī)就是在template中訪問(wèn)了computed</font>,因?yàn)閏omputed又定義了響應(yīng)式,訪問(wèn)了computed屬性就會(huì)執(zhí)行computed的get方法,在get方法中會(huì)執(zhí)行
watcher.evaluate()方法,在里面就是去執(zhí)行get(),從而去計(jì)算computed的結(jié)果
- <font color=blue>執(zhí)行時(shí)機(jī)就是在template中訪問(wèn)了computed</font>,因?yàn)閏omputed又定義了響應(yīng)式,訪問(wèn)了computed屬性就會(huì)執(zhí)行computed的get方法,在get方法中會(huì)執(zhí)行
- 在 new Watcher 計(jì)算屬性watcher的構(gòu)造函數(shù)中
-
<font color=blue size=5>(1-1-2) 把computd定義成響應(yīng)式</font>
-
defineComputed(vm, key, userDef)=>Object.defineProperty(target, key, sharedPropertyDefinition)=>sharedPropertyDefinition.get=>createComputedGetter(key)=>computedGetter - 也就是說(shuō)訪問(wèn)computed中的
this.xxxx就會(huì)去執(zhí)行computedGetter函數(shù)
-
-
<font color=blue size=5>(1-1-3) computedGetter - 這個(gè)方法很重要 !!!!!!!!!!!!!!!!!!!!!</font>
- watcher.evaluate() 執(zhí)行計(jì)算屬性watcher中的evaluate方法
- 當(dāng)dirty=true,并且watcher存在時(shí),就會(huì)執(zhí)行 computed watcher 的 evalute 方法
- <font color=blue>evalute</font> 方法會(huì)執(zhí)行 <font color=blue>get()</font> 方法,并將 <font color=blue>this.dirty</font> 改為 <font color=blue>false</font>
- <font color=blue>get()</font>
- <font color=blue>pushTarget(this)</font>
- <font color=blue>向 targetStack 數(shù)組中 push 一個(gè)computed watcher</font>
- <font color=blue>將 Dep.target 指定為 computed watcher</font>
- 執(zhí)行用戶在computed對(duì)象中定義的方法,即getter方法newName
- 比如
computed: {newName() {return this.name + 'new' }} - 注意:
- 在這過(guò)程中又會(huì)觸發(fā) data對(duì)象的影響式,即this.name觸發(fā)響應(yīng)式data中的get函數(shù),因?yàn)樵L問(wèn)了data的name屬性
- data,computed都有自己的響應(yīng)式
- 這里data的響應(yīng)式又會(huì)收集計(jì)算屬性的watcher,這個(gè)放在后面的計(jì)算屬性的訪問(wèn)流程中去梳理
- 主要就是
- 向 computed watcher 的newDeps中添加render watcher的dep
- 向 render watcher的依賴的屬性的dep的 subs 中添加 computed watcher
- 詳見下文
- watcher.depend() 執(zhí)行計(jì)算屬性watcher的depend方法
- 放在下面訪問(wèn)流程一起分析
- 向 computed watcher 的newDeps中添加render watcher的dep
- 主要就是
- 比如
- <font color=blue>pushTarget(this)</font>
- <font color=blue>get()</font>
- watcher.evaluate() 執(zhí)行計(jì)算屬性watcher中的evaluate方法
-
源碼
Vue.prototype._init => initState(vm) => initComputed(vm, opts.computed)
initComputed - scr/core/instance/state.js
initComputed - scr/core/instance/state.js
---
function initComputed (vm: Component, computed: Object) {
const watchers = vm._computedWatchers = Object.create(null)
// 聲明 watchers 和 _computedWatchers 為一個(gè)空對(duì)象
const isSSR = isServerRendering()
// 是否是ssr環(huán)境
for (const key in computed) {
const userDef = computed[key]
// userDef 是 computed 的 getter 函數(shù)
const getter = typeof userDef === 'function' ? userDef : userDef.get
// getter
// computed getter 可以是函數(shù) 或者 具有g(shù)et方法的對(duì)象
// 這里一般都是函數(shù),并且該函數(shù)需要return一個(gè)值
if (process.env.NODE_ENV !== 'production' && getter == null) {
warn(
`Getter is missing for computed property "${key}".`,
vm
)
}
// 如果不是函數(shù)或者對(duì)象就報(bào)警告
if (!isSSR) {
// create internal watcher for the computed property.
// 非ssr環(huán)境,即瀏覽器環(huán)境,就新建 computed watcher
// computed watcher
// computedWatcherOptions = { lazy: true }
// getter = 用戶自定義的computed對(duì)象中的函數(shù)
watchers[key] = new Watcher(
vm,
getter || noop, // 用戶自定義的computed對(duì)象中的函數(shù),即 computed getter
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.
// 在 vue.extends 和 new Vue() 過(guò)程中都定義了響應(yīng)式的computed
if (!(key in vm)) {
defineComputed(vm, key, userDef)
// defineComputed 將 computed 變成響應(yīng)式
} else if (process.env.NODE_ENV !== 'production') {
// 處理重名的情況,在props,data,computed不能用重名的key
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)
}
}
}
}
- defineComputed - scr/core/instance/state.js
defineComputed - scr/core/instance/state.js
---
export function defineComputed (
target: any,
key: string,
userDef: Object | Function
) {
const shouldCache = !isServerRendering()
// shouldCache 如果在瀏覽器環(huán)境就是 true
if (typeof userDef === 'function') {
sharedPropertyDefinition.get = shouldCache
? createComputedGetter(key) // 定義computed被訪問(wèn)時(shí),觸發(fā)的get
: createGetterInvoker(userDef)
sharedPropertyDefinition.set = noop
} else {
// userDef 不是 function,我們直接忽略
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)
// 定義響應(yīng)式 computed
// 1. 當(dāng)通過(guò) this.xxxx 訪問(wèn)computed,就會(huì)觸發(fā)sharedPropertyDefinition對(duì)象中的get
// 2. get 其實(shí)就是下面createComputedGetter返回的 computedGetter函數(shù)
}
- createComputedGetter - scr/core/instance/state.js
createComputedGetter - scr/core/instance/state.js
---
function createComputedGetter (key) {
return function computedGetter () {
const watcher = this._computedWatchers && this._computedWatchers[key]
// 取出每一個(gè) computed watcher
if (watcher) {
if (watcher.dirty) {
// watcher.dirty
// 1. 默認(rèn)初始化時(shí),comoputed watcher 的 dirty=true
// 2. 當(dāng) dirty=true 就會(huì)執(zhí)行 watcher.evaluate()
// 3. watcher.evaluate() 執(zhí)行完后, dirty=false
// 總結(jié): dirty=true => watcher.evaluate() => dirty=false
watcher.evaluate()
// watcher.evaluate()
// 1. 會(huì)去執(zhí)行 computed watcher 中的 get()
// pushTarget(this)
// 1. 將 computed watcher 添加到 targetStack 數(shù)組中
// 2. 將 Dep.target = computed watcher
// 執(zhí)行 this.getter.call(vm, vm) 即用戶自定義的 computed對(duì)象中的方法
// 1. 列如: computed: {newName() {return this.name + 'new' }}
// 2. 因?yàn)椋篶omputed的newName方法中,依賴了data中的this.name,即訪問(wèn)到了this.name就會(huì)觸發(fā)data響應(yīng)式的get方法
// 3. 所以:ata響應(yīng)式的get方法執(zhí)行過(guò)程如下
// 獲取到了this.name的值
// 此時(shí),Dep.target 是computed watcher
// 然后執(zhí)行this.name對(duì)象的dep類的depend方法進(jìn)行依賴收集
// 向 computed watcher 的newDeps中添加render watcher的dep
// 向 render watcher 的 subs 中添加 computed watcher
// popTarget()
// 1. targetStack.pop()將 computed watcher從targetStack數(shù)組中刪除
// 2. 并且將 Dep.target 指定為數(shù)組中的前一個(gè) watcher,沒有了就是undefined
// 2. 將 dirty=false
// evaluate () {
// this.value = this.get()
// this.dirty = false
// }
// get () {
// pushTarget(this)
// let value
// const vm = this.vm
// try {
// value = this.getter.call(vm, 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) {
// traverse(value)
// }
// popTarget()
// this.cleanupDeps()
// }
// return value
// }
}
if (Dep.target) {
watcher.depend()
// depend () {
// let i = this.deps.length
// while (i--) {
// this.deps[i].depend()
// }
// }
}
return watcher.value
}
}
}
- Watcher - scr/core/observer/watcher.js
Watcher - scr/core/observer/watcher.js
---
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<Dep>;
newDeps: Array<Dep>;
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
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)
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()
}
/**
* Evaluate the getter, and re-collect dependencies.
*/
get () {
pushTarget(this)
let value
const vm = this.vm
try {
value = this.getter.call(vm, 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) {
traverse(value)
}
popTarget()
this.cleanupDeps()
}
return value
}
/**
* Add a dependency to this directive.
*/
addDep (dep: Dep) {
const id = dep.id
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id)
this.newDeps.push(dep)
if (!this.depIds.has(id)) {
dep.addSub(this)
}
}
}
/**
* Clean up for dependency collection.
*/
cleanupDeps () {
let i = this.deps.length
while (i--) {
const dep = this.deps[i]
if (!this.newDepIds.has(dep.id)) {
dep.removeSub(this)
}
}
let tmp = this.depIds
this.depIds = this.newDepIds
this.newDepIds = tmp
this.newDepIds.clear()
tmp = this.deps
this.deps = this.newDeps
this.newDeps = tmp
this.newDeps.length = 0
}
/**
* Subscriber interface.
* Will be called when a dependency changes.
*/
update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
this.run()
} else {
queueWatcher(this)
}
}
/**
* Scheduler job interface.
* Will be called by the scheduler.
*/
run () {
if (this.active) {
const value = this.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) {
try {
this.cb.call(this.vm, value, oldValue)
} catch (e) {
handleError(e, this.vm, `callback for watcher "${this.expression}"`)
}
} else {
this.cb.call(this.vm, value, oldValue)
}
}
}
}
/**
* Evaluate the value of the watcher.
* This only gets called for lazy watchers.
*/
evaluate () {
this.value = this.get()
this.dirty = false
}
/**
* Depend on all deps collected by this watcher.
*/
depend () {
let i = this.deps.length
while (i--) {
this.deps[i].depend()
}
}
/**
* Remove self from all dependencies' subscriber list.
*/
teardown () {
if (this.active) {
// remove self from vm's watcher list
// this is a somewhat expensive operation so we skip it
// if the vm is being destroyed.
if (!this.vm._isBeingDestroyed) {
remove(this.vm._watchers, this)
}
let i = this.deps.length
while (i--) {
this.deps[i].removeSub(this)
}
this.active = false
}
}
}
(2) computed的訪問(wèn)過(guò)程
- 案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./vue.js"></script>
</head>
<body>
<div id="root">
<div>{{newName}}</div>
<button @click="change">change</button>
</div>
<script>
new Vue({
el: '#root',
data: {
name: 'ssssssssssssss'
},
computed: {
newName() {
return this.name + 'new'
}
},
methods: {
change() {
this.name = '222222222222222'
}
}
})
</script>
</body>
</html>
- 在
vm._update(vm._render(), hydrating)過(guò)程中, 當(dāng)在template模板中使用了 computed 對(duì)象中的key的時(shí)候,就會(huì)觸發(fā)computed響應(yīng)式對(duì)象的 get 方法 - computed的響應(yīng)式get方法就是computedGetter方法,在該方法中,會(huì)判斷wathcer和watcher.dirty是否存在,存在證明就是computed watcher,就會(huì)執(zhí)行 computed wathcer 的 watcher.evaluate() 方法
- watcher.evaluate()方法就會(huì)執(zhí)行computed watcher中的get()方法,并將 this.dirty 改為 false
-
watcher.evaluate()
- get()方法
- 調(diào)用 <font color=red>pushTarget(this)</font>
- <font color=red>向 targetStack 數(shù)組中 push 一個(gè)computed watcher</font>
- <font color=red>將 Dep.target 指定為 computed watcher</font>
- 然后調(diào)用 computed getter 方法,即用戶自己定義的computed對(duì)象中的方法
- 比如:
computed: {newName() {return this.name + 'new' }} - 因?yàn)椋篶omputed的newName方法中,依賴了data中的this.name,即訪問(wèn)到了this.name就會(huì)觸發(fā)data響應(yīng)式的get方法
- 所以:<font color=DarkOrChid>data響應(yīng)式的get方法執(zhí)行過(guò)程如下:</font>
- 獲取到了this.name的值
- 此時(shí),Dep.target 是 computed watcher
- 然后執(zhí)行this.name對(duì)應(yīng)的dep類的depend方法進(jìn)行依賴收集
- <font color=DarkOrChid>向 computed watcher 的newDeps中添加render watcher的對(duì)應(yīng)的data屬性的 dep</font>
- <font color=DarkOrChid>向 render watcher對(duì)應(yīng)的data屬性的dep實(shí)例的 subs 中添加 computed watcher</font>
- <font color=DarkOrChid>等于說(shuō)data的 this.name 和 computed Watcher 具有同一個(gè) dep 實(shí)例</font>
- 執(zhí)行完上面的步驟后 dep.subs 和 computed watcher.newDeps 的狀態(tài)是
this.name 對(duì)應(yīng)的 dep 實(shí)例中的 subs = [computedWatcher]computed watcher 中的 newDeps = [上面的this.name對(duì)應(yīng)的dep]
- 然后執(zhí)行this.name對(duì)應(yīng)的dep類的depend方法進(jìn)行依賴收集
- 返回name的值
- 比如:
- 調(diào)用 <font color=red>popTarget()</font>
- <font color=red>targetStack.pop()將 computed watcher從targetStack數(shù)組中刪除</font>
- <font color=red>并且將 Dep.target 指定為數(shù)組中的前一個(gè) watcher</font>
- <font color=red>Dep.target = render watcher</font>
- 在上面的例子中 targetStack 數(shù)組中在執(zhí)行computed 的getter方法時(shí)一共有兩個(gè)成員
- 第一個(gè):render watcher
- 第二個(gè):computed watcher
- pop后還剩一個(gè)render watcher
- 最后返回computed計(jì)算得到的結(jié)果值
- 調(diào)用 <font color=red>pushTarget(this)</font>
- get()方法
-
watcher.depend()
- 當(dāng) Dep.target 存在時(shí),才會(huì)執(zhí)行 watcher.depend()
- 上面執(zhí)行完,<font color=DarkOrChid>Dep.target = render watcher</font>
- watcher.depend()
- 然后執(zhí)行 <font color=DarkOrChid>compute watcher 中的 watcher.depend() 方法</font>
- 然后,<font color=DarkOrChid>從后往前,依次取出 computed watcher 中 deps 數(shù)組中的 dep,執(zhí)行 dep.depend()</font>
- 注意:上面computed watcher 中的 deps 中的 dep,就是this.name對(duì)象的dep,里面的subs數(shù)組中只有一個(gè)computedWatcher
- 對(duì)比:在data對(duì)象的屬性被訪問(wèn)的時(shí)候,也會(huì)執(zhí)行data對(duì)應(yīng)的屬性的 dep.depend()
- <font color=DarkOrChid>Dep.target.addDep(this)</font>
- <font color=DarkOrChid>此時(shí)的 Dep.targt 是 render watcher</font>,因?yàn)?popTarget() 操作pop出computed watcher后就只剩render watcher了
- addDep 前
- render watcher中的 deps 是空數(shù)組
- render watcher中的 newDeps 是空數(shù)組
- addDep 主要做兩件事情
- <font color=DarkOrChid>向 render watcher 的 newDeps 中添加 該render watcher 對(duì)應(yīng)的datad屬性的 dep</font>
- <font color=DarkOrChid>向 render watcher 對(duì)應(yīng)的data屬性對(duì)應(yīng)的dep類的 subs 中添加 render watcher</font>
- 添加后,data的dep.subs = [computed watcher, render watcher]
- <font color=red>這樣當(dāng)this.name屬性修改后觸發(fā)對(duì)應(yīng)的set函數(shù),就會(huì)觸發(fā)dep.notify,然后循環(huán)sub中的watcher,執(zhí)行watcher.update()方法</font>
- <font color=red>[computed watcher, render watcher]這樣的順序保證了在render的時(shí)候,computed一定有值</font>
(3) computed的更新過(guò)程
- 上面的例子中,change的過(guò)程,是改變了computed的依賴,之后的一系列流程
- 觸發(fā)computed的響應(yīng)式依賴的dep.notify()方法,循環(huán)遍歷該依賴的dep.subs數(shù)組中的每一個(gè)watcher.update()方法,在上面的例子中subs=[computed watcher, render watcher]所以先執(zhí)行computed watcher
- compued watcher
- 在update方法中,會(huì)判斷l(xiāng)azy=true?,因?yàn)閏omputed watcher的lazy=true,所以執(zhí)行 dirty=true
- render watcher
- 在update方法中, lazy不為true, 會(huì)執(zhí)行 queueWatcher(this),就是調(diào)用渲染watcher重新求值和渲染
資料
實(shí)際案例-避坑 https://juejin.im/post/6844903705540558855
詳細(xì) https://juejin.im/post/6844903606676799501
源碼版 https://juejin.im/post/6844903881583886349
computed watcher https://juejin.im/post/6844903648984596494