We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
前言
vue 中数据是普通的js对象,当数据变化时,视图会进行更新。数据之所以能驱动视图的更新,关键的部分就是它的响应式系统。
vue
js
在new Vue()初始化阶段,this._init(options)方法执行的时候,会执行initState(vm),它定义在src/core/instance/state.js中。
new Vue()
this._init(options)
initState(vm)
src/core/instance/state.js
export function initState (vm: Component) { // ... if (opts.data) { initData(vm) } // ... }
initState的作用主要是初始化props,methods,data,computed,watcher等属性,这里我们重点看看initData。
initState
props
methods
data
computed
watcher
initData
function initData (vm: Component) { // ... observe(data, true /* asRootData */) }
initData,初始化data用的,在最后有一行observe(data, true)
observe(data, true)
export function observe (value: any, asRootData: ?boolean): Observer | void { // ... ob = new Observer(value) // ... }
observe()是将用户定义的数据data变成响应式的数据。主要是去实例化一个Observe对象实例,用于收集依赖和派发更新。
observe()
Observe
export class Observer { constructor (value: any) { this.value = value this.walk(value) } walk (obj: Object) { const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i]) } } }
在Observer中,对对象的每一项执行,defineReactive(obj, keys[i])。
Observer
defineReactive(obj, keys[i])
export function defineReactive ( obj: Object, key: string, val: any, customSetter?: ?Function, shallow?: boolean ) { const dep = new Dep() // 依赖管理器 val = obj[key] observe(val) // 递归对象 Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { // 收集依赖 }, set: function reactiveSetter (newVal) { // 更新依赖 } }) }
// mountComponent export function mountComponent( vm: Component, // 组件实例 el: ?Element, // 挂载的元素 hydrating?: boolean // 服务端渲染相关 ): Component { ... updateComponent = () => { // vm._render(),生成 vnode,在 instance/render.js 中 // vm._update(),更新 dom vm._update(vm._render(), hydrating) } // watcher 会调用 updateComponent,先生成 vnode ,然后调用 update 更新 dom; new Watcher(vm, updateComponent, noop, { before() { ... } }, true /* isRenderWatcher */) return vm } // watcher export default class Watcher { constructor( vm: Component, // 组件实例 expOrFn: string | Function, cb: Function, // 当监听的数据变化时,会触发该回调 options?: ?Object, isRenderWatcher?: boolean ) { ... // expOrFn 是 `updateComponent` 方法 this.getter = expOrFn this.value = this.lazy ? undefined : this.get() } get() { // 添加 target,相当于 watcher pushTarget(this) ... try { // 相当于执行 updateComponent() value = this.getter.call(vm, vm) } catch (e) { ... } }
vue 在mount 过程中会执行mountComponent,而在mountComponent中会new 一个 Watcher,然后里面执行this.get()方法。这个get()方法中,又执行了pushTarget(this),是将当前的watcher保存到数组中。
mount
mountComponent
new
Watcher
this.get()
get()
pushTarget(this)
Dep.target = null const targetStack = [] export function pushTarget (target: ?Watcher) { targetStack.push(target) Dep.target = target // 当前的 watcher 实例 }
pushTarget 的定义在 src/core/observer/dep.js 中。然后get()方法中,执行了
pushTarget
src/core/observer/dep.js
this.getter.call(vm, vm) // 相当于执行vm._update(vm._render(), hydrating)
首先执行的vm._render()方法中,如果有使用data数据,会触发defineProperty重写的get方法。每个对象值都会持有一个dep,在触发get时,会调用dep.depend()方法。
vm._render()
defineProperty
get
dep
dep.depend()
Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { const value = getter ? getter.call(obj) : val if (Dep.target) { dep.depend() // ... } return value }, set: function reactiveSetter (newVal) { // 派发更新 } })
export default class Dep { static target: ?Watcher; id: number; subs: Array<Watcher>; // ... constructor () { this.id = uid++ this.subs = [] // 对象 key 值对应的 watcher 集合 } addSub (sub: Watcher) { this.subs.push(sub) // 将 watcher 添加到subs 数组中 } depend () { if (Dep.target) { Dep.target.addDep(this) // 执行 addDep } } // ... } ------------------------------------------------- addDep(dep: Dep) { const id = dep.id if (!this.newDepIds.has(id)) { // ... dep.addSub(this) // 执行 dep.addSub } }
dep.depend()会执行Dep.target.addDep(this)方法。接着执行dep.addSub(this)方法,最后会执行this.subs.push(sub)。也就是说,data对象每个key值对应的watcher,都放入到subs中,后续数据如果有变化,可以通知 watcher 需要进行更新。
Dep.target.addDep(this)
dep.addSub(this)
this.subs.push(sub)
key
subs
Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter () { // 收集依赖 }, set: function reactiveSetter (newVal) { const value = getter ? getter.call(obj) : val if (newVal === value || (newVal !== newVal && value !== value)) { return } // ... if (setter) { setter.call(obj, newVal) } else { val = newVal } childOb = !shallow && observe(newVal) dep.notify() } })
当我们对响应的数据做了改变时,会触发set 方法,有两个比较关键的地方是,一个是childOb = !shallow && observe(newVal) ,它是将新设置的值变成一个响应式的值。另一个是dep.notify(),通知watcher进行更新。notify -> update -> queueWatcher。
set
childOb = !shallow && observe(newVal)
dep.notify()
notify -> update -> queueWatcher
notify () { const subs = this.subs.slice() // ... for (let i = 0, l = subs.length; i < l; i++) { subs[i].update() } } ------------------------------------------------ update() { // ... queueWatcher(this) // }
const queue = [] let has = {} export function queueWatcher (watcher: Watcher) { const id = watcher.id if (has[id] == null) { has[id] = true // ... queue.push(watcher) // 将要更新的 watcher 添加到 queue 队列 // ... nextTick(flushSchedulerQueue) // 异步更新,相当于 promise.then } }
queueWatcher是将要更新的watcher添加到queue,然后在nextTick后执行flushSchedulerQueue,统一更新这些watcher。
queueWatcher
queue
nextTick
flushSchedulerQueue
function flushSchedulerQueue () { currentFlushTimestamp = getNow() flushing = true let watcher, id queue.sort((a, b) => a.id - b.id) // ... for (index = 0; index < queue.length; index++) { watcher = queue[index] if (watcher.before) { watcher.before() } id = watcher.id has[id] = null watcher.run() } // ... }
flushSchedulerQueue主要的一些作用是:
队列排序queue.sort((a, b) => a.id - b.id) 每次更新按着id(new watcher 时生成) 的大小顺序来更新。 1、组件更新顺序是,先父组件,然后在是子组件。 2、自定义的watch,优先于渲染watcher,因为自定义watcher先创建的.computed watcher->自定义 watcher -> render watcher,这个顺序是根据它们初始化顺序来的。 3、如果一个子组件在父组件的watcher执行期间被销毁,那么这个子组件的watcher会被跳过,所以父组件应该先执行。
queue.sort((a, b) => a.id - b.id)
id
watch
computed watcher->自定义 watcher -> render watcher
run() { if (this.active) { const value = this.get() // ... if ( value !== this.value ) { const oldValue = this.value this.value = value // ... this.cb.call(this.vm, value, oldValue) // ... } // ... } }
run()方法是执行watcher 的回调函数,先通过this.get()获得当前的新值value,使用this.value 获取旧值oldValue,然后将这两个值传入到回调函数中,所以,在我们在watch 中可以拿到新值和旧值的原因。同时,在执行this.get()方法时,会再次触发vm._update(vm._render(), hydrating),然后进行diff比对,进行视图的更新。
run()
value
this.value
oldValue
vm._update(vm._render(), hydrating)
diff
在组件初始化时,vue会使用defineProperty劫持data 数据的get 和set 方法。在new Watcher 时,会触发data对象的get方法,进行数据依赖收集。当数据改变时,会触发data对象的set方法,进行数据的更新。
new Watcher
The text was updated successfully, but these errors were encountered:
No branches or pull requests
vue
中数据是普通的js
对象,当数据变化时,视图会进行更新。数据之所以能驱动视图的更新,关键的部分就是它的响应式系统。初始化数据
initState
在
new Vue()
初始化阶段,this._init(options)
方法执行的时候,会执行initState(vm)
,它定义在src/core/instance/state.js
中。initState
的作用主要是初始化props
,methods
,data
,computed
,watcher
等属性,这里我们重点看看initData
。initData
initData
,初始化data
用的,在最后有一行observe(data, true)
observe
observe()
是将用户定义的数据data
变成响应式的数据。主要是去实例化一个Observe
对象实例,用于收集依赖和派发更新。new Observer(value)
在
Observer
中,对对象的每一项执行,defineReactive(obj, keys[i])
。defineReactive
收集依赖
vue
在mount
过程中会执行mountComponent
,而在mountComponent
中会new
一个Watcher
,然后里面执行this.get()
方法。这个get()
方法中,又执行了pushTarget(this)
,是将当前的watcher
保存到数组中。pushTarget
的定义在src/core/observer/dep.js
中。然后get()
方法中,执行了首先执行的
vm._render()
方法中,如果有使用data
数据,会触发defineProperty
重写的get
方法。每个对象值都会持有一个dep
,在触发get
时,会调用dep.depend()
方法。dep.depend()
会执行Dep.target.addDep(this)
方法。接着执行dep.addSub(this)
方法,最后会执行this.subs.push(sub)
。也就是说,data
对象每个key
值对应的watcher
,都放入到subs
中,后续数据如果有变化,可以通知watcher
需要进行更新。派发更新
当我们对响应的数据做了改变时,会触发
set
方法,有两个比较关键的地方是,一个是childOb = !shallow && observe(newVal)
,它是将新设置的值变成一个响应式的值。另一个是dep.notify()
,通知watcher
进行更新。notify -> update -> queueWatcher
。queueWatcher
queueWatcher
是将要更新的watcher
添加到queue
,然后在nextTick
后执行flushSchedulerQueue
,统一更新这些watcher
。flushSchedulerQueue
flushSchedulerQueue
主要的一些作用是:队列排序
queue.sort((a, b) => a.id - b.id)
每次更新按着
id
(new watcher 时生成) 的大小顺序来更新。1、组件更新顺序是,先父组件,然后在是子组件。
2、自定义的
watch
,优先于渲染watcher
,因为自定义watcher
先创建的.computed watcher->自定义 watcher -> render watcher
,这个顺序是根据它们初始化顺序来的。3、如果一个子组件在父组件的
watcher
执行期间被销毁,那么这个子组件的watcher
会被跳过,所以父组件应该先执行。watcher.run()
run()
方法是执行watcher
的回调函数,先通过this.get()
获得当前的新值value
,使用this.value
获取旧值oldValue
,然后将这两个值传入到回调函数中,所以,在我们在watch
中可以拿到新值和旧值的原因。同时,在执行this.get()
方法时,会再次触发vm._update(vm._render(), hydrating)
,然后进行diff
比对,进行视图的更新。总结
在组件初始化时,
vue
会使用defineProperty
劫持data
数据的get
和set
方法。在new Watcher
时,会触发data
对象的get
方法,进行数据依赖收集。当数据改变时,会触发data
对象的set
方法,进行数据的更新。The text was updated successfully, but these errors were encountered: