1. 发布订阅模式与观察者模式
1.1 发布订阅
- 订阅者
- 发布者
- 信号中心
我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布" (publish) 一个信号,其他任务可以向信号中心"订阅" (subscribe) 这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式" (publish-subscribe pattern)
1.1.1 发布订阅模式应用
- Vue自定义事件
let vm = new Vue()
// { 'click': [fn1, fn2], 'change': [fn] }
// 注册事件(订阅消息)
vm.$on('dataChange', () => {
console.log('dataChange')
})
vm.$on('dataChange', () => {
console.log('dataChange1')
})
// 触发事件(发布消息)
vm.$emit('dataChange')
- eventBus
//事件中心
let eventHub = new Vue()
// ComponentA. vue
// 发布者
addTodo: function () {
//发布消息(事件)
eventHub.$emit('add-todo', { text: this.newTodoText })
this.newTodoText = ''
}
// ComponentB.vue
// 订阅者
created: function () {
// 订阅消息(事件)
eventHub.$on('add-todo', this.addTodo)
}
1.1.2 发布订阅实现
class EventEmitter {
constructor () {
// { eventType: [ handler1, handler2 ] }
this.subs = Object.create(null)
}
// 订阅通知
$on (eventType, handler) {
this.subs[eventType] = this.subs[eventType] || []
this.subs[eventType].push(handler)
}
// 发布通知
$emit (eventType) {
if (this.subs[eventType]) {
this.subs[eventType].forEach(handler => {
handler()
})
}
}
}
// 测试
var bus = new EventEmitter()
// 注册事件
bus.$on('click', function () {
console.log('click')
})
bus.$on('click', function () {
console.log('click1')
})
// 触发事件
bus.$emit('click')
1.2 观察者模式
观察者模式符合的设计原则:主题和观察者分离,不是主动触发而是被动监听,两者解耦
// 主题,接收状态变化,触发每个观察者
class Subject {
constructor() {
this.state = 0
this.observers = []
}
getState() {
return this.state
}
setState(state) {
this.state = state
this.notifyAllObservers()
}
// 订阅通知
attach(observer) {
this.observers.push(observer)
}
// 执行通知
notifyAllObservers() {
this.observers.forEach(observer => {
observer.update()
})
}
}
// 观察者,等待被触发
class Observer {
constructor(name, subject) {
this.name = name
this.subject = subject
this.subject.attach(this)
}
update() {
console.log(`${this.name} update, state: ${this.subject.getState()}`)
}
}
// 测试代码
let s = new Subject()
let o1 = new Observer('o1', s)
let o2 = new Observer('o2', s)
let o3 = new Observer('o3', s)
s.setState(1)
s.setState(2)
s.setState(3)
1.3 发布订阅与观察者模式区别
总结:
- 观察者模式是由具体目标调度,比如当事件触发,Dep 就会去调用观察者的方法,所以观察者模式的订阅者与发布者之间是存在依赖的。
- 发布/订阅模式由统一调度中心调用,因此发布者和订阅者不需要知道对方的存在。
1.4 Vue中的观察者模式
// 目标(发布者)
// Dependency
class Dep {
constructor () {
// 存储所有的观察者
this.subs = []
}
// 添加观察者
addSub (sub) {
if (sub && sub.update) {
this.subs.push(sub)
}
}
// 通知所有观察者
notify () {
this.subs.forEach(sub => {
sub.update()
})
}
}
// 观察者(订阅者)
class Watcher {
update () {
console.log('update')
}
}
// 测试
let dep = new Dep()
let watcher = new Watcher()
dep.addSub(watcher)
dep.notify()
- 观察者(订阅者) - Watcher
- update(): 当事件发生时,具体要做的事情
- 目标(发布者) -- Dep
- subs数组:存储所有的观察者
- addSub():添加观察者
- notify(): 当事件发生,调用所有观察者的update()方法
- 没有事件中心
2. Vue MVVM简单实现
2.1 Vue
- 功能
- 负责接收初始化的参数(选项)
- 负责把 data 中的属性注入到 Vue 实例,转换成 getter/setter
- 负责调用 observer 监听 data 中所有属性的变化
- 负责调用 compiler 解析指令/差值表达式
- 结构
这里我们把 data 属性挂载到 Vue 实例上其实是为了方便我们去使用数据,不用 this.data.,直接 this. 即可
class Vue {
constructor (options) {
// 1. 通过属性保存选项的数据
this.$options = options || {}
this.$data = options.data || {}
this.$el = typeof options.el === 'string' ? document.querySelector(options.el) : options.el
// 2. 把data中的成员转换成getter和setter,注入到vue实例中
this._proxyData(this.$data)
// 3. 调用observer对象,监听数据的变化
new Observer(this.$data)
// 4. 调用compiler对象,解析指令和差值表达式
new Compiler(this)
}
_proxyData(data) {
Object.keys(data).forEach(key => {
Object.defineProperty(this, key, {
enumerable: true,
configurable: true,
get() {
return data[key]
},
set(newValue) {
if(newValue !== data[key]) {
data[key] = newValue
}
}
})
})
}
}
2.2 Observer
- 功能
- 负责把 data 选项中的属性转换成响应式数据
- data 中的某个属性也是对象,把该属性转换成响应式数据
- 数据变化发送通知
- 实现
这里我们需要注意 get 方法中我们返回的是 val 而不是 data[key],因为使用 data[key] 又会触发 getter,就变成了死循环。
另外我们在 set 中去修改 val,get 中返回 val,val 其实被锁在 defineReactive 函数里,我们 get、set 操作都去修改 val 即可
class Observer {
constructor (data) {
this.walk(data)
}
walk(data) {
// 1. 判断data是否是对象
if (!data || typeof data !== 'object') {
return
}
Object.keys(data).forEach(key => {
this.defineReactive(data, key, data[key])
})
}
defineReactive(data, key, val) {
let _self = this
let dep = new Dep()
this.walk(val)
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get() {
Dep.target && dep.addSub(Dep.target) // 观察者模式添加观察者
return val
},
set(newValue) {
if(newValue !== val) {
val = newValue
_self.walk(newValue)
dep.notify() // 观察者模式发布通知
}
}
})
}
}
3.3 Dep
- 功能
- 收集依赖,添加观察者(watcher)
- 通知所有观察者
- 实现
class Dep {
constructor() {
this.subs = []
}
addSub(sub) {
if(sub && sub.update) {
this.subs.push(sub)
}
}
notify() {
this.subs.forEach(sub => {
sub.update()
})
}
remove() {
if(arr.length) {
const index = arr.indexOf(item)
if(index > -1) {
return arr.splice(index, 1)
}
}
}
}
3.4 watcher
- 功能
- 当数据变化触发依赖,dep 通知所有的 Watcher 实例更新视图
- 自身实例化的时候往 dep 对象中添加自己
- 实现
我们设置 Dep.target = this,然后调用 this.oldValue = vm[key],这时会触发一次getter,即 defineReactive 中 get 方法执行 Dep.target && dep.addSub(Dep.target) 添加一个观察者,接着我们再清空target Dep.target = null
class Watcher {
constructor (vm, key, cb) {
this.vm = vm
// data 中的属性名称
this.key = key
// 当数据变化的时候,调用 cb 更新视图
this.cb = cb
// 在 Dep 的静态属性上记录当前 watcher 对象,当访问数据的时候把 watcher 添加到 dep 的 subs 中
Dep.target = this
// 触发一次 getter,让 dep 为当前 key 记录 watcher
this.oldValue = vm[key]
// 清空 target
Dep.target = null
}
update () {
const newValue = this.vm[this.key]
if (this.oldValue === newValue) {
return
}
this.cb(newValue)
}
}
3.5 调用 watcher
在 compiler.js 中为每一个指令/插值表达式创建 watcher 对象,监视数据的变化
// 因为在 textUpdater等中要使用
this updaterFn && updaterFn.call(this, node, this.vm[key], key)
// v-text 指令的更新方法
textUpdater (node, value, key) {
node.textContent = value // 每一个指令中创建一个 watcher,观察数据的变化
new Watcher(this.vm, key, value => {
node.textContent = value
})
}
视图变化更新数据
// v-model 指令的更新方法
modelUpdater (node, value, key) {
node.value = value // 每一个指令中创建一个 watcher,观察数据的变化
new Watcher(this.vm, key, value => {
node.value = value
})// 监听视图的变化
node.addEventListener('input', () => {
this.vm[key] = node.value
})
}
3.6 批处理
功能:
- 异步处理视图渲染,这样我们多次修改data时,只渲染最后一次
- 由于异步渲染,这里我们拿到的 data 肯定是最后一次的 data。
- 我们可以得出结论,修改数据是同步的,渲染页面是异步的
class Batcher {
constructor () {
this.has = {}
this.queue = []
this.waiting = false
}
push(job) {
let id = job.id
// 如果没有该id,才继续走下一步
if (!this.has[id]) {
this.queue.push(job)
//设置元素的ID
this.has[id] = true
if (!this.waiting) {
this.waiting = true
if ("Promise" in window) {
Promise.resolve().then( ()=> {
this.flush()
})
} else {
setTimeout(() => {
this.flush()
}, 0)
}
}
}
}
flush() {
this.queue.forEach((job) => {
job.cb(job.newValue)
})
this.reset()
}
reset() {
this.has = {}
this.queue = []
this.waiting = false
}
}
3.7 总体流程
4. Vue源码中的MVVM
我们通过源码来找以下几个问题的答案
vm.msg = { count: 0 },重新给属性赋值,是否是响应式的?vm.arr[0] = 4,给数组元素赋值,视图是否会更新vm.arr.length = 0,修改数组的 length,视图是否会更新vm.arr.push(4),视图是否会更新
4.1 initState
在 Vue 的初始化阶段,_init 方法执行的时候,会执行 initState(vm) 方法,它的定义在 src/core/instance/state.js 中
这里会判断data是否是 函数,是否与 methods/props/ 等中有重名,判断完后,调用 observer() 方法
- 执行顺序:
- props
- methods
- data
- computed
- watch
if (opts.props) initProps(vm, opts.props)
if (opts.methods) initMethods(vm, opts.methods)
if (opts.data) {
// 判断data是否是函数,是否与methods/props/等中有重名,最后再调用observer()
initData(vm)
} else {
observe(vm._data = {}, true /* asRootData */)
}
if (opts.computed) initComputed(vm, opts.computed)
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
initState 方法主要是对 props、methods、data、computed 和 wathcer 等属性做了初始化操作
这里主要做了两件事:一个是对定义 data 函数返回对象的遍历,通过 proxy 把每一个值 vm._data.xxx 都代理到 vm.xxx 上;
另一个是调用 observe 方法观测整个 data 的变化,把 data 也变成响应式
4.2 响应式对象
1. observer
observer在 /src/core/observer/index.js 中
- 判断 value 是否是对象或vnode实例,是直接返回
- 如果 value 有
__ob__(observer对象) 属性,说明已有该对象,直接返回 - 创建一个 Observer 对象
export function observe (value: any, asRootData: ?boolean): Observer | void {
// 判断 value 是否是对象或vnode实例
if (!isObject(value) || value instanceof VNode) {
return
}
let ob: Observer | void
// 如果 value 有 __ob__(observer对象) 属性,说明已有该对象,结束
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue // 不能是vue实例
) {
// 创建一个 Observer 对象
ob = new Observer(value)
}
if (asRootData && ob) {
ob.vmCount++
}
return ob
}
2. Observer
- src\core\observer\index.js
- 通过执行 def 函数把自身实例添加到数据对象 value 的
__ob__属性 - 对对象做响应化处理
- 对数组做响应化处理(下面单独作一个章节,这里先略过)
- walk(obj)
- 遍历 obj 的所有属性,为每一个属性调用 defineReactive() 方法,设置 getter/setter
- 通过执行 def 函数把自身实例添加到数据对象 value 的
export class Observer {
constructor (value: any) {
this.value = value
this.dep = new Dep()
// 初始化实例的 vmCount 为0
this.vmCount = 0
// 将实例挂载到观察对象的 __ob__ 属性
def(value, '__ob__', this)
// 数组的响应式处理
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods)
} else {
copyAugment(value, arrayMethods, arrayKeys)
}
// 为数组中的每一个对象创建一个 observer 实例
this.observeArray(value)
} else {
// 遍历对象中的每一个属性,转换成 setter/getter
this.walk(value)
}
}
walk (obj: Object) {
// 获取观察对象的每一个属性
const keys = Object.keys(obj)
// 遍历每一个属性,设置为响应式数据
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
3. defineReactive()
- src\core\observer\index.js
- defineReactive(obj, key, val, customSetter, shallow)
- 为一个对象定义一个响应式的属性,每一个属性对应一个 dep 对象
- 如果对象不可配置,什么都不做
- 如果该属性的值是对象,继续调用 observe
- 如果给属性赋新值,继续调用 observe
- get中建立依赖
- set如果数据更新发送通知
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
// 创建依赖对象实例
const dep = new Dep()
// 获取 obj 的属性描述符对象,如果该对象不可配置,直接返回
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
// 判断是否递归观察子对象,并将子对象属性都转换成 getter/setter,返回子观察对象
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
// 如果预定义的 getter 存在则 value 等于getter 调用的返回值
// 否则直接赋予属性值
const value = getter ? getter.call(obj) : val
// 如果存在当前依赖目标,即 watcher 对象,则建立依赖
if (Dep.target) {
dep.depend()
// 如果子观察目标存在,建立子对象的依赖关系
if (childOb) {
childOb.dep.depend()
// 如果属性是数组,则特殊处理收集数组对象依赖
if (Array.isArray(value)) {
dependArray(value)
}
}
}
// 返回属性值
return value
},
set: function reactiveSetter (newVal) {
// 如果预定义的 getter 存在则 value 等于getter 调用的返回值
// 否则直接赋予属性值
const value = getter ? getter.call(obj) : val
// 如果新值等于旧值或者新值旧值为NaN则不执行
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
// 如果没有 setter 直接返回
// #7981: for accessor properties without setter
if (getter && !setter) return
// 如果预定义setter存在则调用,否则直接更新新值
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
// 如果新值是对象,观察子对象并返回 子的 observer 对象
childOb = !shallow && observe(newVal)
// 派发更新(发布更改通知)
dep.notify()
}
})
}
4.3 收集依赖
1. dep
- Dep 实际上就是对 Watcher 的一种管理
- addSub
- notify
Dep.target用来存放目前正在使用的 watcher, 全局唯一,并且一次也只能有一个 watcher 被使用
export default class Dep {
// 静态属性,watcher 对象
static target: ?Watcher;
// dep 实例 Id
id: number;
// dep 实例对应的 watcher 对象/订阅者数组
subs: Array<Watcher>;
constructor () {
this.id = uid++
this.subs = []
}
// 添加新的订阅者 watcher 对象
addSub (sub: Watcher) {
this.subs.push(sub)
}
// 将观察对象和 watcher 建立依赖
depend () {
if (Dep.target) {
// 如果 target 存在,把 dep 对象添加到 watcher 的依赖中
Dep.target.addDep(this)
}
}
// 发布通知
notify () {
const subs = this.subs.slice()
// 调用每个订阅者的update方法实现更新
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
// Dep.target 用来存放目前正在使用的watcher
// 全局唯一,并且一次也只能有一个watcher被使用
Dep.target = null
const targetStack = []
// 入栈并将当前 watcher 赋值给 Dep.target
// 父子组件嵌套的时候先把父组件对应的 watcher 入栈,
// 再去处理子组件的 watcher,子组件的处理完毕后,再把父组件对应的 watcher 出栈,继续操作
export function pushTarget (target: ?Watcher) {
targetStack.push(target)
Dep.target = target
}
export function popTarget () {
// 出栈操作
targetStack.pop()
Dep.target = targetStack[targetStack.length - 1]
}
2. watcher
- get
- pushTarget 入栈并将当前 watcher 赋值给 Dep.target
- value = this.getter.call(vm, vm) // 读取值,触发get
- popTarget 出栈操作, Dep.target 赋值为下个target
- update
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
}
update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
this.run()
} else {
queueWatcher(this)
}
}
3. 注册 watcher
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
4.4 数组处理
1. 获取数组原型 arrayMethods
创建变量 arrayMethods,它继承自 Array.prototype,具备其所有功能。未来,我们要使用 arrayMethods 去覆盖Array.prototype。
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
;[
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
].forEach(function(method) {
// 缓存原始方法
const original = arrayProto[method]
Object.defineProperty(arrayMethods, method, {
value:function mutator(...args) {
return original.apply(this, args)
},
enumerable:false,
writable:true,
configurable: true
})
})
2. 覆盖响应式数据的 Array.prototype
我们用 arrayMethods 去覆盖 Array.prototype。但是我们又不能直接覆盖,因为这样会污染全局的 Array,我们希望拦截操作只针对那些被侦测了变化的数据生效,也就是说希望拦截器只覆盖那些响应式数组的原型。而将一个数据转换成响应式的,需要通过 Observer,所以我们只需要在 Observer 中使用拦截器覆盖那些即将被转换成响应式 Array 类型数据的原型就好了
export class Observer {
constructor(value){
this.value = value
if(Array.isArray(value)){
value.__proto__ = arrayMethods//新增
} else {
this.walk(value)
}
}
}
value.__proto__ = arrayMethods 的作用是将拦截器(加工后具备拦截功能的 arrayMethods)赋值给 value.__proto__,通过 __proto__ 可以很巧妙地实现覆盖 value 原型的功能
3. 处理没有__proto__的情况
处理不能使用 __proto__ 的情况,Vue的做法非常粗暴,如果不能使用 __proto__,就直接将 arrayMethods 身上的这些方法设置到被侦测的数组上
//_proto_是否可用
const hasProto = '__proto__' in {}
const arrayKeys = Object.getOMnPropertyNames(arrayMethods)
export class Observer {
constructor(value){
this.value = value
if (Array.isArray(value)) {
// 修改
const augment = hasProto ? protoAugment : copyAugment
augment(value, arrayMethods, arrayKeys)
} else {
this.walk(value)
}
}
}
function protoAugment(target, src, keys) {
target.__proto__ = src
}
function copyAugment(target, src, keys) {
for(let i=0,l = keys.length; i<l; i++) {
const key = keys[i]
def(target,key,src[key])
}
}
4. 为数组添加 __ob__
__ob__ 的作用不仅仅是为了在拦截器中访问Observer实例这么简单,还可以用来标记当前 value 是否已经被 Observer 转换成了响应式数据。
也就是说,所有被侦测了变化的数据身上都会有一个 __ob__ 属性来表示它们是响应式的。如果value是响应式的,则直接返回 __ob__;如果不是响应式的,则使用 new Observer 来将数据转换成响应式数据。
当value身上被标记了 __ob__ 之后,就可以通过 value.__ob__ 来访问observer实例。如果是Array拦截器,因为拦截器是原型方法,所以可以直接通过 this.__ob__ 来访问Observer 实例
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
;[
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
].forEach(function(method) {
// 缓存原始方法
const original= arrayProto[method]
def(arrayMethods,method, function mutator(...args){
const result = original.apply(this,args)
const ob = this.__ob__
ob.dep.notify() // 向依赖发送消息
return result
})
})
5. 侦测数组元素变化
export class Observer {
constructor(value){
this.value = value
def(value, '__ob__', this)
if (Array.isArray(value)) {
// 修改
const augment = hasProto ? protoAugment : copyAugment
augment(value, arrayMethods, arrayKeys)
this.observerArray(value)
} else {
this.walk(value)
}
}
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
6. 对数组新增元素监听
;[
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
].forEach(function(method) {
// 缓存原始方法
const original= arrayProto[method]
def(arrayMethods,method, function mutator(...args){
const result = original.apply(this,args)
const ob = this.__ob__
let inserted
switch(method){
case 'push':
case 'unshift':
inserted= args
break
case 'splice':
inserted = args.slice(2)
break
}
if(inserted) ob.observerArray(inserted)
ob.dep.notify() // 向依赖发送消息
return result
})
})
我们通过 switch 对 method 进行判断,如果 method 是 push、unshift、splice 这种可以新增数组元素的方法,那么从 args中将新增元素取出来,暂存在 inserted 中。接下来使用 Observer 把 inserted 中的元素转换成响应式的。
4.5 异步更新渲染队列
-
has 对象保证同一个 Watcher 只添加一次
-
flushing 正在刷新,表示队列是否正在被处理,如果没有被处理,直接添加到队列中,否则插入到队列中
-
waiting 表示当前队列是否正在执行,如果没有被执行,就调用
flushSchedulerQueue, 保证对 nextTick(flushSchedulerQueue) 的调用逻辑只有一次 -
queue排序目的
-
- 组件更新顺序从父组件到子组件,因为先创建父组件再创建子组件
-
- 用户的自定义 watcher 要优先于渲染 watcher 执行;因为用户自定义 watcher 是在渲染 watcher 之前创建的
-
- 如果一个组件在父组件的 watcher 执行期间被销毁,那么它对应的 watcher 执行都可以被跳过,所以父组件的 watcher 应该先执行
-
-
遍历watcher
- 不要去缓存 queue 长度,因为这个队列是在动态添加的
export function queueWatcher (watcher: Watcher) {
const id = watcher.id
if (has[id] == null) {
has[id] = true
// 队列是否正在被处理
if (!flushing) { // 添加到队列中
queue.push(watcher)
} else {
// if already flushing, splice the watcher based on its id
// if already past its id, it will be run next immediately.
let i = queue.length - 1
while (i > index && queue[i].id > watcher.id) {
i--
}
queue.splice(i + 1, 0, watcher)
}
// queue the flush
if (!waiting) {
waiting = true
if (process.env.NODE_ENV !== 'production' && !config.async) {
flushSchedulerQueue()
return
}
nextTick(flushSchedulerQueue)
}
}
}
function flushSchedulerQueue () {
currentFlushTimestamp = getNow()
flushing = true
let watcher, id
// 排序目的
// 1. 组件更新顺序从父组件到子组件,因为先创建父组件再创建子组件
// 2. 组件的用户watcher要在渲染watcher之前运行
// 3. 如果一个组件在运行前父组件被销毁了,那么应该跳过本次更新
queue.sort((a, b) => a.id - b.id)
// do not cache length because more watchers might be pushed
// as we run existing watchers
// 不要去缓存queue长度,因为这个队列是在动态添加的
for (index = 0; index < queue.length; index++) {
watcher = queue[index]
if (watcher.before) {
watcher.before()
}
id = watcher.id
has[id] = null
watcher.run()
}
// keep copies of post queues before resetting state
const activatedQueue = activatedChildren.slice()
const updatedQueue = queue.slice()
resetSchedulerState()
// call component updated and activated hooks
callActivatedHooks(activatedQueue)
callUpdatedHooks(updatedQueue)
// devtool hook
/* istanbul ignore if */
if (devtools && config.devtools) {
devtools.emit('flush')
}
}