Vue 初始化过程
...
merge options
...
//合并配置选项options
initLifecycle(vm)
//初始化组件实例关系属性,比如 $parent、$children、$root、$refs 等
initEvents(vm)
//初始化自定义事件,注意:这里的事件是值在父组件在子组件上定义的事
initRender(vm)
//解析组件的插槽信息,得到 vm.$slot,处理渲染函数,
// 得到 vm.$createElement方法,即 h 函数
callHook(vm, 'beforeCreate')
//调用 beforeCreate 钩子函数
initInjections(vm) // resolve injections before data/props
// 初始化组件的 inject 配置项,得到 result[key] = val 形式的配置对象,
// 然后对结果数据进行响应式处理,并代理每个 key 到 vm 实例
initState(vm)
// 初始化 props、methods、data、computed、watch(按照此顺序)
initProvide(vm) // resolve provide after data/props
// 解析组件配置项上的 provide 对象,将其挂载到 vm._provided 属性上
callHook(vm, 'created')
//调用 create 钩子函数
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
// 如果存在el,则挂载
Vue数据响应式原理
initState: 数据响应式入口,分别处理 InitProps、initMethods、initData、initComputed、initWatch
- 其中,响应式核心函数:defineReactive(obj, key, value),为每个key设置响应式.
每个key拥有一个dep,在getter读取obj[key]时进行拦截,收集Watcher,在setter更新数据时
通过dep.notify()通知dep中的watcher更新,触发重新渲染。
/**
* 拦截 obj[key] 的读取和设置操作:
* 1、在第一次读取时收集依赖,比如执行 render 函数生成虚拟 DOM 时会有读取操作
* 2、在更新时设置新值并通知依赖更新
*/
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
// 实例化 dep,一个 key 一个 dep
const dep = new Dep()
// 获取 obj[key] 的属性描述符,发现它是不可配置对象的话直接 return
// Object.getOwnPropertyDescriptor 返回指定对象上一个自有属性对应的属性描述符。
//(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// 记录 getter 和 setter,获取 val 值
// cater for pre-defined getter/setters
const getter = property && property.get
const setter = property && property.set
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
// 递归调用,处理 val 即 obj[key] 的值为对象的情况,保证对象中的所有 key 都被观察
let childOb = !shallow && observe(val)
// 响应式核心
// Object.defineProperty: 直接在一个对象上定义一个新属性,
// 或者修改一个对象的现有属性,并返回此对象。
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
// get 拦截对 obj[key] 的读取操作
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
/**
* Dep.target 为 Dep 类的一个静态属性,值为 watcher,在实例化 Watcher 时会被设置
* 实例化 Watcher 时会执行 new Watcher 时传递的回调函数(computed 除外,因为它懒执行)
* 而回调函数中如果有 vm.key 的读取行为,则会触发这里的 读取 拦截,进行依赖收集
* 回调函数执行完以后又会将 Dep.target 设置为 null,避免这里重复收集依赖
*/
if (Dep.target) {
// 依赖收集,在 dep 中添加 watcher,也在 watcher 中添加 dep
dep.depend()
// childOb 表示对象中嵌套对象的观察者对象,如果存在也对其进行依赖收集
if (childOb) {
// 这就是 this.key.chidlKey 被更新时能触发响应式更新的原因
childOb.dep.depend()
// 如果是 obj[key] 是 数组,则触发数组响应式
if (Array.isArray(value)) {
// 为数组项为对象的项添加依赖
dependArray(value)
}
}
}
return value
},
// set 拦截对 obj[key] 的设置操作
set: function reactiveSetter (newVal) {
// 旧的 obj[key]
const value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
// 如果新老值一样,则直接 return,不更新更不触发响应式更新过程
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
// #7981: for accessor properties without setter
// setter 不存在说明该属性是一个只读属性,直接 return
if (getter && !setter) return
// setter存在,设置新值
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
// 对新值进行观察,让新值也是响应式的
childOb = !shallow && observe(newVal)
// 依赖通知更新
dep.notify()
}
})
}
异步更新队列
- dep.notify(): 通知dep中所有的 watcher 执行 watcher.update()
- watcher.update(): 执行queueWatcher(this), 将自身放入到一个全局 watcher 队列,如果已经存在于队列中则跳过。
- nextTick(flushSchedulerQueue),利用浏览器异步更新队列刷新 watcher s。
- flushSchedulerQueue:
首先对queue进行排序:a.保证父组件先于子组件更新(因为父组件总是在子组件之前创建); b.如果一个组件在其父组件的 watcher 执行期间被销毁,则它的 watcher 可以被跳过; c.一个组件的用户 watcher 在其渲染 watcher 之前被执行,因为用户 watcher 先于 渲染 watcher 创建。然后执行watcher.run()函数,触发更新,比如updateComponent。
- nextTick(flushSchedulerQueue):
将flushSchedulerQueue函数放入全局callbacks数组中, 定义flushCallbacks()函数(作用是执行并清空callbacks数组),并将flushCallbacks放入浏览器的异步更新队列中,放入队列的优先级如下Promise > MutationObserver > setImmediate > setTimeout