1. Vue的初始化过程
1. 初始化生命周期
2. 初始化事件系统
3. 初始化state,依次处理props, methods, data, computed ...
export function initState(vm: Component) { const opts = vm.$options if (opts.props) initProps(vm, opts.props) if (opts.methods) initMethods(vm, opts.methods) if (opts.data) initData(vm) if (opts.computed) initComputed(vm, opts.computed) if (opts.watch) initWatch(vm, opts.watch) } }
4. 开始渲染 _mount() => _render() => 返回vdom => _update() => _patch() 更新dom
2. Vue数据响应式原理
initData 初始化用户数据
observe 将数据进行观测
new Observer 定义观测对象
this.walk(value) 循环处理对象的所有key
defineReactive 将对象的指定key定位为响应式
Object.defineProperty 在get和set方法种定义响应式,在get方法种用 Dep.depend 进行依赖收集
在set方法中用 dep.notify 进行依赖更新通知
3. Vue数组变化检测
对数组类型的数据,进行方法重写,将原数组方法替换为自定义的同名方法,在自定义方法中调用原方法取值,并进行依赖更新通知。
依然是使用 dep.notify 方法
initData 初始化用户数据
observe 观测用户数据
new Observer 定义观测对象
protoAugment 将数组的原型方法指向重写的原型
重写数组的原型方法,并添加变更通知
深度观测数组中的每一项引用类型
4. Vue异步渲染步骤
// src/core/observer/dep.js let uid = 0 // Dep实例的id,为了方便去重 export default class Dep { static target: ?Watcher // 当前是谁在进行依赖的收集 id: number subs: Array<Watcher> // 观察者集合 constructor() { this.id = uid++ // Dep实例的id,为了方便去重 this.subs = [] // 存储收集器中需要通知的Watcher } addSub(sub: Watcher) { ... } /* 添加一个观察者对象 */ removeSub(sub: Watcher) { ... } /* 移除一个观察者对象 */ depend() { ... } /* 依赖收集,当存在Dep.target的时候把自己添加观察者的依赖中 */ notify() { ... } /* 通知所有订阅者 */ } const targetStack = [] // watcher栈 export function pushTarget(_target: ?Watcher) { ... } /* 将watcher观察者实例设置给Dep.target,用以依赖收集。同时将该实例存入target栈中 */ export function popTarget() { ... } /* 将观察者实例从target栈中取出并设置给Dep.target */
pushTaget和popTarget是和Dep类同级的方法,直接暴露给其他类使用
dep.notify() dep通知更新
subs[i].update() dep中的watcher调用update方法
queueWatcher 将watcher加入到队列中
nextTick(flushSchedulerQueue) 在下一个tick,flush队列,执行所有的watcher方法
5. nextTick方法实现原理
nextTick(cb) nextTick方法传入回调
callbacks.push(cb) 数组存储所有回调
timerFunc 在timerFunc中调用回调,timerFunc是用microTask模拟的,例如Promise.then, MutationObserver,setImmedate,setTimeout等
返回Promise 可以执行then方法
6. Vue的computed实现过程
initComputed
new Watcher
defineComputed
createComputedGetter 在此方法中进行依赖收集,和依赖计算,即执行watcher.evaluate方法
用户取值 dirty = false 使用缓存,返回上次结果,dirty = true watcher.evaluate(),重新计算结果
7. Vue的响应式更新过程
model更新 set方法 -> 触发Dep.notify -> Dep的subs中的Watcher -> Watcher调用update -> queueWatcher -> nextTick -> flushQueueWatcher -> 生成新的Vnode -> 新VNode 与 老VNode 进行diff -> patch 将差异更新到视图
diff是patch过程的核心。diff算法有2个显著特点:
1. 只会同级进行比较,不会垮层级比较
2. 在diff列表过程中,循环从两边向中间收拢