IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Vue 2.x/3.x 比较(基于源码) · 看不见我的美 · 是你瞎了眼

    馬腊咯稽发表于 2021-10-24 00:00:00
    love 0
    Vue 2.x/3.x 比较(基于源码)

    响应式原理 2.x

    Dep 用来存放订阅者(watcher)的对象;每个 Dep 实例都存在于对象每个属性对应的 getter/setter 所在的闭包里并且都有唯一的 id。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    
    export default class Dep {
     static target;
     constructor() {
     this.id = uid++;
     this.subs = []; // 存放着 watchers(订阅者)
     }
     addSub(sub) {
     this.subs.push(sub); // 将 watcher 添加到列表(一般由 watcher 调用)
     }
     removeSub(sub) {
     remove(this.subs, sub); // 从列表移除 watcher
     }
     depend() {
     if (Dep.target) {
     /**
     * 进行依赖收集时,Dep.target 会指向待被收集的 watcher
     * 调用 watcher 的 addDep 方法
     * watcher 会调用 dep.addSub 把它自己加到 subs 里
     * 同时,watcher 会把当前 dep 记录下来
     */
     Dep.target.addDep(this);
     }
     }
     notify() {
     // 循环“订阅者列表”,触发 watcher.update,告诉它们“数据变了”
     const subs = this.subs.slice();
     for (let i = 0, l = subs.length; i < l; i++) {
     subs[i].update();
     }
     }
    }
    /**
     * 用来指向某个正在被求值的 watcher
     * 某个时间点,仅有一个 watcher 可以被求值
     */
    Dep.target = null;
    /**
     * 由于 Vue 的更新粒度是组件级的,每个组件(render 函数)对应一个 watcher
     * 组件嵌套成树就映射为 render 的调用栈,对应的 watcher 也就保存在“栈”里
     */
    const targetStack = [];
    // 收集依赖之前调用
    export function pushTarget(target: ?Watcher) {
     targetStack.push(target);
     Dep.target = target;
    }
    // 收集依赖完成调用
    export function popTarget() {
     targetStack.pop();
     // 恢复上一个 Dep.target(恢复父级组件对应的 watcher)
     Dep.target = targetStack[targetStack.length - 1];
    }
    

    Watcher 实例被称作依赖(需要被收集起来)或者订阅者,它观察/订阅了数据变动(在依赖收集期间,访问了数据的 getter);数据的 getter 被触发时,watcher 会被收集起来(Dep.target 被收集起来);数据的 setter 被调用时,watcher.update 会被触发(watcher 得到了通知):

    • 在 mountComponent 中会 new Watcher(…) 作为组件的 renderWatcher;同时在组件 update 时,会调用 watcher.before 来触发 beforeUpdate 钩子;
    • 每个 vm.$watch 或者 watchOptions 都会 new Watcher(…);
    • 组件的 computedOptions 也会 new Watcher(…)。
     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    
    export default class Watcher {
     /**
     * 每个响应式数据都有自己的 Dep 列表(这个存在于闭包中);
     * 保存着所有依赖于“此数据”的“订阅者”。
     * 每当访问数据,会触发数据的 getter;
     * 将全局唯一变量 Dep.target 添加到自己的 dep 列表。
     * 每当数据变化,会触发数据的 setter;
     * setter 会通知所有“订阅者”(调用 watcher.update)。
     * 每个 watcher 都维护了一份 dep 列表;
     * 当 watcher 销毁时,需要告诉这些“dep”,
     * 把自己删掉(数据再发生变化,别通知我了)。
     */
     constructor(
     vm, // 组件实例
     expOrFn, // a.b.c || () => a.b.c
     cb, // 副作用回调函数
     options, // computed || deep || lazy
     isRenderWatcher // 是否是组件级别的 watcher
     ) {
     this.vm = vm;
     if (isRenderWatcher) {
     // 如果是组件 renderWatcher,就把自己保存在 vm._watcher 属性上
     vm._watcher = this;
     }
     // vm._watchers 是一个数组,保存着组件的 renderWatcher、computedWatchers 和 watchWatchers
     vm._watchers.push(this);
     if (options) {
     this.deep = !!options.deep; // deep: true || false
     this.user = !!options.user; // 是否是 watchWatcher
     this.lazy = !!options.lazy; // 是否是 computedWatcher
     this.sync = !!options.sync; // immediate: true || false
     this.before = options.before; // watcher.run 执行之前先要执行的逻辑
     } else {
     this.deep = this.user = this.lazy = this.sync = false;
     }
     this.cb = cb; // 保存回调
     this.id = ++uid; // 批处理用的,唯一的标识
     this.active = true; // watcher 是否存活,teardown 之后被置为 false
     this.dirty = this.lazy; // computedGetter 被触发时,会判断数据『是否"脏"了』,脏了的话,就会重新求最新值
     /**
     * 为什么会有 deps、newDeps、depIds、newDepIds?
     * 假设 vm.$watch(() => vm.a ? vm.b : vm.c, () => {...})
     * 上一次收集的依赖和这一次收集的依赖可能不一样(取决于 vm.a 的值)
     * 当 vm.a 为 true 时,收集的依赖为 vm.a、vm.b
     * 当 vm.a 为 false 时,收集的依赖为 vm.a、vm.c
     */
     this.deps = [];
     this.newDeps = [];
     this.depIds = new Set();
     this.newDepIds = new Set();
     this.expression = expOrFn.toString(); // 为了方便报错提示
     /**
     * 解析 expOrFn 为 getter,有两种形式:
     * 1. 属性路径:a.b.c 指向的数据会被 watcher 观察
     * 2. 函数:function () {
     * 函数里访问的所有数据会被 watcher 观察
     * 任何数据变化都会通知 watcher
     * }
     */
     if (typeof expOrFn === 'function') {
     this.getter = expOrFn;
     } else {
     /**
     * 将点语法转换为函数调用:
     * a.b.c 转换为 () => a.b.c
     */
     this.getter = parsePath(expOrFn);
     if (!this.getter) {
     this.getter = noop;
     }
     }
     // 如果不是 computedWatcher,则在实例化的过程中对 getter 求值,作为 prevValue
     this.value = this.lazy ? undefined : this.get();
     }
     // 对 getter 求值并重新出发依赖收集
     get() {
     /**
     * 将 Dep.target 设置为“当前 watcher 实例”
     * 并将“当前 watcher 实例”推进 targetStack
     * 方便“响应式数据”将本 watcher 添加到对应的依赖列表里
     */
     let value;
     pushTarget(this);
     const vm = this.vm;
     try {
     value = this.getter.call(vm, vm); // 从组件实例中获取数据
     } catch (e) {
     if (this.user) {
     handleError(`getter for watcher "${this.expression}"`);
     } else {
     throw e;
     }
     } finally {
     /**
     * 如果要深层监听数据变化的话
     * 递归的触发每个属性的 getter
     * 将 watcher 添加到他们的依赖列表里
     */
     if (this.deep) {
     traverse(value);
     }
     // 恢复上一个 Dep.target 的值
     popTarget();
     // 清理依赖
     this.cleanupDeps();
     }
     return value;
     }
     // 调用 dep.addSub(watcher),把“自己”添加进去
     addDep(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);
     }
     }
     }
     teardown() {
     if (this.active) {
     // 将自己从组件的 _watchers 里移除
     if (!this.vm._isBeingDestroyed) {
     remove(this.vm._watchers, this);
     }
     let i = this.deps.length;
     while (i--) {
     // 告诉所有“收集了自己作为依赖”的依赖列表,“我要销毁了,把我移除吧”
     this.deps[i].removeSub(this);
     }
     // 完成销毁
     this.active = false;
     }
     }
     // 清理依赖
     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;
     }
     // 调用 watcher.addDep(dep)
     depend() {
     let i = this.deps.length;
     while (i--) {
     this.deps[i].depend();
     }
     }
     // 当数据变化,会『被』调用的 update(dep.notify 调用的)
     update() {
     if (this.lazy) {
     /**
     * 如果是 computed,将 watcher 标记为“脏”的
     * 当访问 computed 的值时,会在 computedGetter 里调用 watcher.evaluate 求值
     */
     this.dirty = true;
     } else if (this.sync) {
     // 立即调用 run 方法,重新求值并执行回调,一般用来在组件初始化阶段执行一次 cb
     this.run();
     } else {
     // 把 watcher 加入异步队列
     queueWatcher(this);
     }
     }
     /**
     * watcher 接收到通知后,如果实例没被销毁
     * 重新访问 getter 获取最新值
     * 如果数据和之前的不一样
     * 或者数据是对象或数组
     * 或者开启了深层监听
     * 说明数据变化了
     * 需要执行回调
     */
     run() {
     if (this.active) {
     const value = this.get(); // 重新求值作为最新的值,并对比前后值;数据变化才会执行 cb
     if (value !== this.value || isObject(value) || this.deep) {
     const oldValue = this.value;
     this.value = value;
     // 将新/旧值都传给回调
     this.cb.call(this.vm, value, oldValue);
     }
     }
     }
     // 给 computed 惰性求值用的
     evaluate() {
     this.value = this.get();
     this.dirty = false; // 求值完了之后,会标记为『干净』
     }
    }
    

    defineReactive 通过调用 Object.defineProperty 将 o[k(可配置属性)] 转化为 getter/setter(访问器)的形式,并在闭包内实例化了一个 Dep,用于保存收集的 watcher:

    • 初始化依赖注入时,会调用 defineReactive(vm, key, (resolveInject(vm.$options.inject, vm))[key]);
    • 初始化渲染函数时,会调用 defineReactive(vm, ‘$attrs/$listeners’, …);
    • 初始化 props 时,会调用 defineReactive(props, key, value);
    • 作为 Vue.util.defineReactive 工具函数使用;
    • 每次调用 vm.$set 都会调用 defineReactive;
    • new Observer() 会调用 defineReactive。
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    
    // 将 obj[key] 转换为 getter/setters 形式
    export function defineReactive(obj, key, val, customSetter, shallow) {
     const property = Object.getOwnPropertyDescriptor(obj, key);
     if (property && property.configurable === false) {
     return;
     }
     const dep = new Dep();
     // 如果之前设置过 getter/setter,先将他们保存下来
     const getter = property && property.get;
     const setter = property && property.set;
     if ((!getter || setter) && arguments.length === 2) {
     val = obj[key];
     }
     // 如果要深层转换并且 val 是复杂数据的话,递归调用 observe
     let childOb = !shallow && observe(val); // val.__ob__ || undefined
     Object.defineProperty(obj, key, {
     enumerable: true,
     configurable: true,
     get: function reactiveGetter() {
     /**
     * watcher 的 getter 被调用之前会执行 pushTarget
     * 将 Dep.target 设置为 watcher,并把自身 push 到 targetStack
     * 调用 dep.depend,进而调用 watcher.addDep 方法
     * 最终,watcher 被收集到 dep.subs 里,dep 被 push 到 watcher.dep 里
     */
     const value = getter ? getter.call(obj) : val;
     if (Dep.target) {
     dep.depend();
     if (childOb) {
     childOb.dep.depend();
     if (Array.isArray(value)) {
     dependArray(value);
     }
     }
     }
     // 返回给 watcher
     return value;
     },
     set: function reactiveSetter(newVal) {
     const value = getter ? getter.call(obj) : val;
     // 对 newVal 和 value 进行浅比较,没变化就返回
     if (newVal === value || (newVal !== newVal && value !== value)) {
     return;
     }
     if (customSetter) {
     // 自定义 setter(比如,computed 可以自定义 setter)
     customSetter();
     }
     // 只读属性,直接返回
     if (getter && !setter) return;
     if (setter) {
     setter.call(obj, newVal);
     } else {
     val = newVal;
     }
     childOb = !shallow && observe(newVal);
     /**
     * dep.notify 会循环 dep.subs,调用每个 watcher.update
     * watcher 接到通知,会根据“配置”执行相应动作
     */
     dep.notify();
     }
     });
    }
    // 收集数组元素上的依赖关系,因为无法对数组元素的访问做拦截
    function dependArray(value) {
     for (let e, i = 0, l = value.length; i < l; i++) {
     e = value[i];
     e && e.__ob__ && e.__ob__.dep.depend();
     if (Array.isArray(e)) {
     // 递归调用
     dependArray(e);
     }
     }
    }
    

    observe(value) 会返回一个 Observer 实例(对 value 的观察者),这个实例挂载在 value.__ob__ 上,而且是单例的;同时,观察者对象的 value 指向被观察对象,形成循环引用;observe(value) 会通过判断 value 类型,将其转化为响应式对象/数组:

    • 作为 Vue.observable 工具函数使用(返回的对象是响应式的,可直接用于 Vue 渲染);
    • 在初始化组件状态时,会调用 observe(vm.$options.data);
    • 需要深层响应的场景,会递归调用 observe。
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    
    // 为“值”创建观察者实例的工厂函数
    export function observe(value, asRootData) {
     // value 不能是 VNode,也不能是 Vue 组件实例
     if (!isObject(value) || value instanceof VNode) {
     return;
     }
     let ob;
     if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
     // 观察者实例保存在 __ob__ 属性上
     ob = value.__ob__;
     } else if (
     shouldObserve &&
     (Array.isArray(value) || isPlainObject(value)) &&
     Object.isExtensible(value) &&
     !value._isVue
     ) {
     ob = new Observer(value);
     }
     if (asRootData && ob) {
     ob.vmCount++;
     }
     return ob;
    }
    /**
     * 对数组部分方法进行遮蔽(不涉及 Array.prototype 的修改)
     * 这也表明了为什么 arr[i] = j、arr.length = 0 不会触发更新
     */
    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__;
     let inserted;
     switch (method) {
     case 'push':
     case 'unshift':
     inserted = args;
     break;
     case 'splice':
     inserted = args.slice(2);
     break;
     }
     // 先单独对新插入的数据进行“观察”
     if (inserted) ob.observeArray(inserted);
     // 通知“订阅者”
     ob.dep.notify();
     return result;
     });
     }
    );
    export class Observer {
     constructor(value) {
     // 保存订阅了 value 本身的 watcher
     this.dep = new Dep();
     this.value = value;
     // 将此对象作为 rootData 的 Vue 实例数量
     this.vmCount = 0;
     // 将 observer 保存到 __ob__ 属性上
     def(value, '__ob__', this);
     if (Array.isArray(value)) {
     // 对于数组,重写原型链上的数组操作方法
     value.__proto__ = arrayMethods;
     // 并对数组的子元素依次调用 observe
     this.observeArray(value);
     } else {
     // 对于对象,遍历 value,将其可枚举属性转换为 getter/setter 形式
     this.walk(value);
     }
     }
     walk(obj) {
     const keys = Object.keys(obj);
     for (let i = 0; i < keys.length; i++) {
     defineReactive(obj, keys[i]);
     }
     }
     observeArray(items) {
     for (let i = 0, l = items.length; i < l; i++) {
     observe(items[i]);
     }
     }
    }
    

    在 beforeMount 和 mounted 钩子之间(mountComponent),会初始化组件 watcher(renderWatcher);renderWatcher 保存在 vm._watcher(一个组件仅有一个 renderWatcher),同时还保存在 vm._watchers 数组里(存放着组件涉及到的所有 watcher,比如 computed、watch 等)。renderWatcher 的 getter 为 updateComponent,当调用 vm._render 时,会触发所有在渲染函数中用到的数据的 getter(会执行 updateComponent.call(vm, vm))。函数访问到的数据的 getter 会把 renderWatcher 添加到自己的 dep.subs 列表中;当数据变化,响应式数据 setter 被触发,dep.notify 将调用 watcher.update,将执行 queueWatcher 把 watcher 添加到更新队列,更新队列会在合适的时候调用 watcher.run 触发页面更新(生成 VNode,再 patch 到 DOM,过程中会经历 diff)。

    响应式原理 3.x

    createReactiveObject 用来将普通对象(Object、Array、Map、Set、WeakMap、WeakSet)通过 Proxy + handlers 代理为响应式数据:

    1. reactive 对应的 handler:mutableHandlers, mutableCollectionHandlers;
    2. shallowReactive 对应的 handler:shallowReactiveHandlers, shallowCollectionHandlers;
    3. readonly 对应的 handler:readonlyHandlers, readonlyCollectionHandlers;
    4. shallowReadonly 对应的 handler:shallowReadonlyHandlers, readonlyCollectionHandlers。
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    
    export const reactiveMap = new WeakMap<Target, any>();
    export const readonlyMap = new WeakMap<Target, any>();
    function createReactiveObject(
     target: Target,
     isReadonly: boolean,
     baseHandlers: ProxyHandler<any>,
     collectionHandlers: ProxyHandler<any>
    ) {
     // 只有 Object、Array、Map、Set、WeakMap、WeakSet 才可以转换为响应式数据
     if (!isObject(target)) {
     return target;
     }
     // target 已经不是原始数据,直接返回
     if (target.__v_raw && !(isReadonly && target.__v_isReactive)) {
     return target;
     }
     // target 已经存在于 readonlyMap 或 reactiveMap 中
     const proxyMap = isReadonly ? readonlyMap : reactiveMap;
     const existingProxy = proxyMap.get(target);
     if (existingProxy) {
     return existingProxy;
     }
     // const targetType = getTargetType(target)
     // if (targetType === TargetType.INVALID) {
     // return target
     // }
     if (target.__v_skip || !Object.isExtensible(target)) {
     return target;
     }
     const toRawType = v => Object.prototype.toString.call(v).slice(8, -1);
     if (targetTypeMap(toRawType(value)) === TargetType.INVALID) {
     return target;
     }
     // 返回一个 proxy
     const proxy = new Proxy(
     target,
     /**
     * Object、Array 使用 baseHandlers
     * Map、Set、WeakMap、WeakSet 使用 collectionHandlers
     */
     targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
     );
     proxyMap.set(target, proxy);
     return proxy;
    }
    

    reactive 用来创建原始对象的响应式副本,这种转换是深层的,reactive 会影响所有嵌套属性。

     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    
    export function reactive(target) {
     // readonly 数据直接返回
     if (target && target.__v_isReadonly) {
     return target;
     }
     return createReactiveObject(
     target,
     false,
     {
     get(target, key, receiver) {
     if (key === '__v_isReactive') {
     return true;
     } else if (key === '__v_isReadonly') {
     return false;
     } else if (key === '__v_raw' && receiver === reactiveMap.get(target)) {
     // 无论是 readonly 还是 reactive,访问 __v_raw 都能得到原始数据
     return target;
     }
     const targetIsArray = isArray(target);
     // 如果 target 是数组,key 是 arrayInstrumentations 的一个属性
     if (targetIsArray && hasOwn(arrayInstrumentations, key)) {
     // arrayInstrumentations 包含了对数组方法的劫持
     return Reflect.get(arrayInstrumentations, key, receiver);
     }
     // 保存正常的结果
     const res = Reflect.get(target, key, receiver);
     // 如果 key 是内建 Symbol 或者是 __proto__/__v_isRef,不需要依赖收集,直接返回
     if (
     isSymbol(key)
     ? builtInSymbols.has(key)
     : key === `__proto__` || key === `__v_isRef`
     ) {
     return res;
     }
     track(target, 'get', key);
     if (isRef(res)) {
     // 如果 target 是一个数组且 key 是一个整数,就不应该把 res 自动展开
     const shouldUnwrap = !(targetIsArray && isIntegerKey(key));
     return shouldUnwrap ? res.value : res;
     }
     if (isObject(res)) {
     // 把返回值也 proxy 一下;惰性 proxy,访问到了再 reactive(res)
     return reactive(res);
     }
     return res;
     },
     set(target, key, value, receiver) {
     const oldValue = target[key];
     value = toRaw(value);
     // 如果 target 不是数组,旧值为 ref,新值不为 ref
     if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
     // 更新旧值的 value 为新值
     oldValue.value = value;
     return true;
     }
     const hadKey =
     // 如果 target 为数组,并且 key 为整数
     isArray(target) && isIntegerKey(key)
     ? Number(key) < target.length
     : hasOwn(target, key);
     const result = Reflect.set(target, key, value, receiver);
     // 如果 target 是原始原型链中的某项内容,则不要触发
     if (target === toRaw(receiver)) {
     if (!hadKey) {
     // key 不存在,add 行为
     trigger(target, 'add', key, value);
     } else if (hasChanged(value, oldValue)) {
     // 新旧值不同,set 行为
     trigger(target, 'set', key, value, oldValue);
     }
     }
     return result;
     },
     has(target, key) {
     const result = Reflect.has(target, key);
     if (!isSymbol(key) || !builtInSymbols.has(key)) {
     track(target, 'has', key);
     }
     return result;
     },
     deleteProperty(target, key) {
     const hadKey = hasOwn(target, key);
     const oldValue = (target as any)[key];
     const result = Reflect.deleteProperty(target, key);
     if (result && hadKey) {
     // key 本就存在,才可能有 delete 行为
     trigger(target, 'delete', key, undefined, oldValue);
     }
     return result;
     },
     ownKeys(target) {
     // 依赖收集
     track(target, 'iterate', isArray(target) ? 'length' : ITERATE_KEY);
     return Reflect.ownKeys(target);
     }
     },
     // Map、Set、WeakMap、WeakSet
     {
     get(target, key, receiver) {
     if (key === '__v_isReactive') {
     return true;
     } else if (key === '__v_isReadonly') {
     return false;
     } else if (key === '__v_raw') {
     return target;
     }
     return Reflect.get(
     hasOwn(mutableInstrumentations, key) && key in target
     ? mutableInstrumentations
     : target,
     key,
     receiver
     );
     }
     }
     );
    }
    

    readonly 用来创建原始对象的只读副本,这种转换同样是深层的,readonly 会影响所有嵌套属性。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    
    export function readonly(target) {
     return createReactiveObject(
     target,
     true,
     // Object、Array
     {
     get(target, key, receiver) {
     if (key === '__v_isReactive') {
     return false;
     } else if (key === '__v_isReadonly') {
     return true;
     } else if (key === '__v_raw' && receiver === readonlyMap.get(target)) {
     return target;
     }
     const targetIsArray = isArray(target);
     const res = Reflect.get(target, key, receiver);
     if (
     isSymbol(key)
     ? builtInSymbols.has(key)
     : key === `__proto__` || key === `__v_isRef`
     ) {
     return res;
     }
     if (isRef(res)) {
     // 如果 target 是一个数组且 key 是一个整数,就不应该把 res 自动展开
     const shouldUnwrap = !targetIsArray || !isIntegerKey(key);
     return shouldUnwrap ? res.value : res;
     }
     // 把返回值也 proxy 一下;惰性 proxy,访问到了再 readonly(res)
     if (isObject(res)) return readonly(res);
     return res;
     },
     set(target, key) {
     console.warn('target is readonly');
     return true;
     },
     deleteProperty(target, key) {
     console.warn('target is readonly');
     return true;
     }
     },
     // Map、Set、WeakMap、WeakSet
     {
     get(target, key, receiver) {
     if (key === '__v_isReactive') {
     return !true;
     } else if (key === '__v_isReadonly') {
     return true;
     } else if (key === '__v_raw') {
     return target;
     }
     return Reflect.get(
     hasOwn(readonlyInstrumentations, key) && key in target
     ? readonlyInstrumentations
     : target,
     key,
     receiver
     );
     }
     }
     );
    }
    

    createRef 用来将基本类型的值转换为包装类型;ref、shallowRef 都会通过它来转换数据。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    
    function createRef(rawValue, shallow = false) {
     if (rawValue && rawValue.__v_isRef === true) {
     return rawValue; // 已经是包装类型,就别来凑热闹了
     }
     return new RefImpl(rawValue, shallow);
    }
    class RefImpl<T> {
     private _value: T;
     public readonly __v_isRef = true;
     constructor(private _rawValue: T, public readonly _shallow = false) {
     this._value = _shallow
     ? _rawValue
     : isObject(_rawValue)
     ? reactive(_rawValue)
     : _rawValue;
     }
     get value() {
     track(toRaw(this), 'get', 'value');
     return this._value;
     }
     set value(newVal) {
     if (hasChanged(toRaw(newVal), this._rawValue)) {
     this._rawValue = newVal;
     this._value = this._shallow ? newVal : convert(newVal);
     trigger(toRaw(this), 'set', 'value', newVal);
     }
     }
    }
    

    ref 接受一个内部值并返回一个响应式且可变的 ref 对象;ref 对象具有指向内部值的单个属性 .value。

    1
    2
    3
    
    export function ref(value) {
     return createRef(value);
    }
    

    track 和 trigger 用于依赖收集和在依赖变动时触发的副作用。

     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    
    // 追踪类型
    export const enum TrackOpTypes {
     GET = 'get', // 属性读取
     HAS = 'has', // in 操作符(不包括 for...in)
     ITERATE = 'iterate' // 迭代器
    }
    // 触发类型
    export const enum TriggerOpTypes {
     SET = 'set', // 属性设置
     ADD = 'add', // 属性添加
     DELETE = 'delete', // 属性删除
     CLEAR = 'clear' // 清空数据
    }
    // 依赖列表(2.x Array => 3.x Set)
    type Dep = Set<ReactiveEffect>;
    // key 对应的依赖列表 [[[key, dep]],...]
    type KeyToDepMap = Map<any, Dep>;
    let shouldTrack = true;
    let activeEffect: ReactiveEffect | undefined;
    const trackStack: boolean[] = [];
    /**
     * target 的 key 对应的依赖列表,储存着 target -> key -> dep 的联系
     * 为什么不用 Map 而用 WeakMap?
     * 1. Map(WeakMap)内部有两个数组,分别存 key 和 value;所以在 Map(WeakMap)内部,key 和 value 并没有真正建立联系;
     * 2. Map 的 key 和内存地址绑定,外部引用的消失不会影响绑定关系;WeakMap 内部对 key 的引用都是弱引用,不会阻止垃圾回收;
     * 3. Map 的 key 可以为任意类型,WeakMap 的 key 只能为对象;
     * 4. Map 可以被遍历,WeakMap 不能被遍历。
     * 这里使用 WeakMap 可以使内存及时被释放。
     * 同理 Set 和 WeakSet 的区别如下:
     * 1. WeakSet 的成员都是弱引用;
     * 2. WeakSet 的成员都是对象;
     * 3. WeakSet 不能遍历。
     * 只要外部引用消失,立即清除就会帮我们自动回收
     */
    const targetMap = new WeakMap<any, KeyToDepMap>();
    export const ITERATE_KEY = Symbol('');
    export const MAP_KEY_ITERATE_KEY = Symbol('');
    /**
     * 暂停追踪,使用场景:
     * 1. 在对数组执行一些可能间接改变 length 属性的操作(push、pop、shift、unshift、splice);
     * 2. 在生命周期函数里暂停追踪,因为可能会触发潜在的 effect;
     * 3. 调用 applyOptions 之前(兼容 2.x 语法);
     * 4. 调用内置的 warn 时;
     * 5. 调用 setup 之前;
     */
    export function pauseTracking() {
     trackStack.push(shouldTrack);
     shouldTrack = false;
    }
    // 开始追踪
    export function enableTracking() {
     trackStack.push(shouldTrack);
     shouldTrack = true;
    }
    // 重置到上一步
    export function resetTracking() {
     const last = trackStack.pop();
     shouldTrack = last === undefined ? true : last;
    }
    /**
     * 收集 activeEffect(在对数据进行 get、has、iterate 操作时):
     * 每次 track,就是把当前激活的副作用函数 activeEffect 作为依赖;
     * 然后收集到 target 相关的 depsMap 对应 key 下的依赖集合 dep 中。
     */
    export function track(target: object, type: TrackOpTypes, key: unknown) {
     // 没有 activeEffect,就先不 track
     if (!shouldTrack || activeEffect === undefined) {
     return;
     }
     /**
     * targetMap 是一个 WeakMap;
     * targetMap.get(target) 是一个 Map
     * 保存着 target 的每个 key 对应的 effects(很像 2.x 里的 watcher,保存在 Set 里)
     * targetMap = {
     * [target]: {
     * [key]: [effect1, effect2, effect3,...],
     * ...
     * },
     * ...
     * }
     */
     let depsMap = targetMap.get(target);
     if (!depsMap) {
     targetMap.set(target, (depsMap = new Map()));
     }
     let dep = depsMap.get(key);
     if (!dep) {
     depsMap.set(key, (dep = new Set()));
     }
     if (!dep.has(activeEffect)) {
     dep.add(activeEffect); // 没有就添加
     activeEffect.deps.push(dep); // activeEffect 在自己 deps 里也保存一份
     }
    }
    // 触发收集到的 effects(在对数据进行 set、add、delete、clear 操作时)
    export function trigger(
     target: object,
     type: TriggerOpTypes,
     key?: unknown,
     newValue?: unknown,
     oldValue?: unknown,
     oldTarget?: Map<unknown, unknown> | Set<unknown>
    ) {
     const depsMap = targetMap.get(target);
     // target 没有被 track 过
     if (!depsMap) return;
     const effects = new Set<ReactiveEffect>();
     // 将 effectsToAdd 按条件添加到 effects 里
     const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
     if (effectsToAdd) {
     effectsToAdd.forEach(effect => {
     if (effect !== activeEffect || effect.allowRecurse) {
     effects.add(effect);
     }
     });
     }
     };
     if (type === 'clear') {
     // Map/Set 要被清空,触发 target 下的所有 effects
     depsMap.forEach(add);
     } else if (key === 'length' && isArray(target)) {
     // 触发数组“长度变化”对应的 effect
     depsMap.forEach((dep, k) => {
     // 遍历的时候,k 为 0、1、2、3、...、length
     if (k === 'length' || k >= newValue) {
     // target['length'] 和 target[newValue~k] 对应的 effects 都要加去
     add(dep);
     }
     });
     } else {
     // 处理 SET | ADD | DELETE
     if (key !== void 0) {
     // key 不为 undefined 的情况
     add(depsMap.get(key));
     }
     // 处理 ADD | DELETE | SET 的 iteration key
     switch (type) {
     case 'add':
     if (!isArray(target)) {
     add(depsMap.get(ITERATE_KEY));
     if (isMap(target)) {
     add(depsMap.get(MAP_KEY_ITERATE_KEY));
     }
     } else if (isIntegerKey(key)) {
     // 数组 length 改变
     add(depsMap.get('length'));
     }
     break;
     case 'delete':
     if (!isArray(target)) {
     add(depsMap.get(ITERATE_KEY));
     if (isMap(target)) {
     add(depsMap.get(MAP_KEY_ITERATE_KEY));
     }
     }
     break;
     case 'set':
     if (isMap(target)) {
     add(depsMap.get(ITERATE_KEY));
     }
     break;
     }
     }
     // 循环 effects 调用 track 收集的 effect
     effects.forEach((effect: ReactiveEffect) => {
     if (effect.options.scheduler) {
     effect.options.scheduler(effect);
     } else {
     effect();
     }
     });
    }
    

    effect 很像之前的 watcher;每次调用 effect,(如果不 lazy 的话)会调用 createReactiveEffect 创建 reactiveEffect 并设置为 activeEffect(就像 2.x 的 watcher 在被收集之前,会将自己赋值给 Dep.target 一样);之后会调用传入的 fn(componentEffect 内部的 renderComponentRoot 方法会执行组件的 render 方法、computedGetter、…),如果在 fn 调用的过程中,访问到了『响应式』数据;Proxy 会通过 track 将 activeEffect 收集起来(响应式数据与 effect 建立联系),放到 Set 里(dep.add(activeEffect));同时,activeEffect 也会保存一份 dep(activeEffect.deps.push(dep))。

    cleanup 流程就是通知 effect.deps 里保存的 dep,调用 dep.delete 方法。

    stop 流程就是在 cleanup 的基础上,调用传入的 onStop 钩子。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    
    let uid = 0;
    const effectStack: ReactiveEffect[] = [];
    export interface ReactiveEffect<T = any> {
     (): T; // ReactiveEffect 可调用
     _isEffect: true; // ReactiveEffect._isEffect = true
     active: boolean; // ReactiveEffect.active = false
     allowRecurse: boolean; // ReactiveEffect.allowRecurse = true
     deps: Array<Dep>; // ReactiveEffect.deps = []
     id: number; // ReactiveEffect.id = 123
     options: ReactiveEffectOptions; // ReactiveEffect.options = {}
     raw: () => T; // ReactiveEffect.raw = ReactiveEffect
    }
    export interface ReactiveEffectOptions {
     allowRecurse?: boolean;
     lazy?: boolean;
     onStop?: () => void;
     scheduler?: (job: ReactiveEffect) => void;
    }
    /**
     * 将 fn(普通副作用)转换为 ReactiveEffect(自动副作用),场景如下:
     * 1. setupRenderEffect 中,instance.update = effect(function componentEffect() {...})
     * 2. computed 中,this._value = (effect(getter, {}))()
     * 3. watch 中,newValue = (effect(getter, {}))()
     */
    export function effect(fn, options = {}) {
     if (fn && fn._isEffect === true) {
     fn = fn.raw;
     }
     const effect = createReactiveEffect(fn, options);
     // 如果不是 computed,先运行一下,触发对 fn 里使用到的数据的操作
     if (!options.lazy) {
     effect();
     }
     return effect;
    }
    function createReactiveEffect<T = any>(
     fn: () => T,
     options: ReactiveEffectOptions
    ): ReactiveEffect<T> {
     const effect = function reactiveEffect(): unknown {
     if (!effect.active) {
     return options.scheduler ? undefined : fn();
     }
     if (!effectStack.includes(effect)) {
     cleanup(effect);
     try {
     enableTracking();
     effectStack.push(effect);
     // 将自身设置为 activeEffect,方便 track 对自己进行追踪,从而进行依赖收集
     activeEffect = effect;
     // 每次调用 effect,都会执行 fn
     return fn();
     } finally {
     // 收集完了之后,就重置为收集之前的状态
     effectStack.pop();
     resetTracking();
     activeEffect = effectStack[effectStack.length - 1];
     }
     }
     } as ReactiveEffect;
     effect._isEffect = true;
     effect.active = true;
     effect.allowRecurse = !!options.allowRecurse;
     effect.deps = [];
     effect.id = uid++;
     effect.options = options;
     effect.raw = fn;
     return effect;
    }
    // 清除 effect
    function cleanup(effect: ReactiveEffect) {
     // 每个 effect 都保存了“自己是谁的订阅者”
     const { deps } = effect;
     if (deps.length) {
     for (let i = 0; i < deps.length; i++) {
     // 通知他们,把自己删掉;如果再更新的话,不要通知我
     deps[i].delete(effect);
     }
     // 清空 effect.deps
     deps.length = 0;
     }
    }
    /**
     * 终止 effect,使用场景如下:
     * 1. watcher 返回的函数中,用于取消 watcher 的 effect
     * 2. unmountComponent 时,用于清除组件的 effect
     */
    export function stop(effect: ReactiveEffect) {
     if (effect.active) {
     cleanup(effect);
     if (effect.options.onStop) {
     effect.options.onStop();
     }
     effect.active = false;
     }
    }
    

    异步队列 2.x

    queueWatcher 在 watcher.update 方法内调用,用来将 watcher 推入队列(缓冲在一个事件循环内的所有数据变化)。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    export function queueWatcher(watcher: Watcher) {
     // 获取 watcher 的唯一的 id
     const id = watcher.id;
     if (has[id] == null) {
     // 同一个 watcher 被多次 update,也只进入队列一次
     has[id] = true;
     if (!flushing) {
     queue.push(watcher);
     } else {
     // 如果正在清空队列,将新来的 watcher 添加到合适的位置
     let i = queue.length - 1;
     while (i > index && queue[i].id > watcher.id) {
     i--;
     }
     queue.splice(i + 1, 0, watcher);
     }
     if (!waiting) {
     waiting = true;
     // 在下个事件循环就清空队列并执行所有 watcher 回调
     nextTick(flushSchedulerQueue);
     }
     }
    }
    

    flushSchedulerQueue 刷新/清空队列,调用 watcher.run 执行副作用;完成 watcher 调用之后,调用组件 activated/updated 钩子函数

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    
    function flushSchedulerQueue() {
     currentFlushTimestamp = Date.now();
     flushing = true;
     let watcher, id;
     /**
     * 在清空之前,先按照 id 进行排序:
     * 1. 组件更新/渲染总是先父后子(父组件先创建)
     * 2. vm.$watch 比 renderWatch 先运行(因为 vm.$watch 先创建)
     * 3. 在父组件 watcher 运行期间,如果子组件被销毁,他的 watcher 会被跳过(watcher.active = false)
     */
     queue.sort((a, b) => a.id - b.id);
     // 不要缓存队列长度,因为随时可能会变
     for (index = 0; index < queue.length; index++) {
     watcher = queue[index];
     if (watcher.before) {
     // 2.x 里唯一用到的地方是 callHook(vm, 'beforeUpdate')
     watcher.before();
     }
     id = watcher.id;
     has[id] = null;
     watcher.run();
     if (has[id] != null) {
     circular[id] = (circular[id] || 0) + 1;
     if (circular[id] > 100) {
     // 判断是否是死循环
     break;
     }
     }
     }
     // 在重置状态之前保留队列的副本
     const activatedQueue = activatedChildren.slice();
     const updatedQueue = queue.slice();
     resetSchedulerState(); // 重置队列状态
     callActivatedHooks(activatedQueue); // callHook(vm, 'activated')
     callUpdatedHooks(updatedQueue); // callHook(vm, 'updated')
    }
    

    异步队列 3.x

     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    
    /**
     * 一共有三种队列:preFlushCbs、queue、postFlushCbs
     * 1. preFlushCbs,清空 queue 之前执行的回调队列(渲染之前需要的任务)
     * 2. queue,“组件渲染”任务队列
     * 3. postFlushCbs,清空 queue 之后执行的回调队列(渲染之后需要的任务)
     * 每次刷新(flush)的过程:清空 preFlushCbs -> 清空 queue -> 清空 postFlushCbs
     */
    export interface SchedulerJob {
     (): void;
     id?: number; // 唯一的 jobID,仅在 RAWEffect(例如,组件渲染)上出现
     /**
     * 是否允许 job 递归触发自身
     * 默认情况下,一个 job 不能触发自身
     * 因为一些内建方法的调用(例如,Array.prototype.push)会导致无限循环
     * 只有在“组件更新函数”和“watch 回调”的情况下,这个选项才可以为 true
     * 组件更新可能会导致子组件的 props 更新,进而触发子组件的“flush: pre”的 watch 回调
     */
     allowRecurse?: boolean;
    }
    export type SchedulerCb = Function & { id?: number };
    export type SchedulerCbs = SchedulerCb | SchedulerCb[];
    type CountMap = Map<SchedulerJob | SchedulerCb, number>;
    let isFlushing = false; // 是否正在刷新
    let isFlushPending = false; // 是否正在等待刷新
    // 还未被执行的 pre/post 任务,如果当前 tick 正在 flushPreCbs/flushPostCbs 的时候,有新的任务进来,会被添加到这个数组中
    const pendingPreFlushCbs: SchedulerCb[] = [];
    const pendingPostFlushCbs: SchedulerCb[] = [];
    // 正在被执行的任务,此时如果 pendingPreFlushCbs/pendingPostFlushCbs 中有新的任务进来的时候,会被合并到这个队列中被继续执行
    let activePreFlushCbs: SchedulerCb[] | null = null;
    let activePostFlushCbs: SchedulerCb[] | null = null;
    let preFlushIndex = 0;
    let postFlushIndex = 0;
    let currentFlushPromise: Promise<void> | null = null;
    let currentPreFlushParentJob: SchedulerJob | null = null;
    const resolvedPromise: Promise<any> = Promise.resolve();
    // 在下个事件循环刷新 job 队列
    function queueFlush() {
     // 在没有刷新队列和没有等待刷新队列的情况下,进入「刷新 job 队列」任务
     if (!isFlushing && !isFlushPending) {
     isFlushPending = true; // 正在等待刷新
     currentFlushPromise = resolvedPromise.then(flushJobs);
     }
    }
    let flushIndex = 0;
    // 异步渲染任务队列
    const queue: SchedulerJob[] = [];
    /**
     * 渲染任务入列:
     * 1. 强制组件重新渲染:vm.$forceUpdate: i => () => queueJob(i.update)
     * 2. 强制父组件重新渲染:queueJob(instance.parent.update)
     */
    export function queueJob(job: SchedulerJob) {
     /**
     * 重复数据消除搜索使用 Array.prototype.include 的 startIndex 参数
     * 判断队列中是否已有该 job,没有才会添加到任务队列中,防止任务重新执行
     * 默认情况下,搜索索引包含正在运行的当前 job,因此它无法再次递归触发自身
     */
     if (
     (!queue.length ||
     !queue.includes(
     job,
     isFlushing && job.allowRecurse ? flushIndex + 1 : flushIndex
     )) &&
     job !== currentPreFlushParentJob
     ) {
     // 就把 job 加入队列
     queue.push(job);
     // 并且在下个事件循环“刷新 job 队列”
     queueFlush();
     }
    }
    // 将失效的 job 从队列中剔除(主要用来避免子组件重新渲染)
    export function invalidateJob(job: SchedulerJob) {
     const i = queue.indexOf(job);
     if (i > -1) {
     queue.splice(i, 1);
     }
    }
    // 刷新 job 队列
    function flushJobs(seen?: CountMap) {
     isFlushPending = false;
     isFlushing = true; // 队列正在刷新
     flushPreFlushCbs(seen); // 刷新前置回调队列
     /**
     * 刷新队列前,先排序;确保:
     * 1. 组件的更新顺序是由父到子(父比子先创建,renderEffect 的优先级也应该如此)
     * 2. 如果组件在父级更新之后被卸载了,他就可以跳过更新
     */
     queue.sort((a, b) => getId(a) - getId(b));
     try {
     // 循环 job 队列
     for (flushIndex = 0; flushIndex < queue.length; flushIndex++) {
     const job = queue[flushIndex];
     if (job) {
     // 将 job 放到 try...catch 里执行
     callWithErrorHandling(job, null, ErrorCodes.SCHEDULER);
     }
     }
     } finally {
     flushIndex = 0;
     queue.length = 0;
     flushPostFlushCbs(seen); // 刷新后置回调队列
     isFlushing = false;
     currentFlushPromise = null;
     // 回调可能将新的 job 添加到了 job 队列,那就再次刷新 job 队列
     if (queue.length || pendingPostFlushCbs.length) {
     flushJobs(seen);
     }
     }
    }
    // 将回调函数加入队列
    function queueCb(
     cb: SchedulerCbs,
     activeQueue: SchedulerCb[] | null, // 激活的队列?
     pendingQueue: SchedulerCb[], // 等待的队列?
     index: number
    ) {
     // 向 pendingQueue 添加回调
     if (!isArray(cb)) {
     if (
     !activeQueue ||
     !activeQueue.includes(
     cb,
     (cb as SchedulerJob).allowRecurse ? index + 1 : index
     )
     ) {
     pendingQueue.push(cb);
     }
     } else {
     pendingQueue.push(...cb);
     }
     queueFlush();
    }
    // 将「渲染之“前”要执行的回调」入列(flash: pre)
    export function queuePreFlushCb(cb: SchedulerCb) {
     queueCb(cb, activePreFlushCbs, pendingPreFlushCbs, preFlushIndex);
    }
    // 将「渲染之“后”要执行的回调」入列
    export function queuePostFlushCb(cb: SchedulerCbs) {
     queueCb(cb, activePostFlushCbs, pendingPostFlushCbs, postFlushIndex);
    }
    /**
     * 刷新「渲染之“前”要执行的回调」
     * 渲染更新之前,触发因为 props 更新而运行的 watcher(flash: pre)
     */
    export function flushPreFlushCbs(
     seen?: CountMap,
     parentJob: SchedulerJob | null = null
    ) {
     if (pendingPreFlushCbs.length) {
     currentPreFlushParentJob = parentJob;
     activePreFlushCbs = [...new Set(pendingPreFlushCbs)];
     pendingPreFlushCbs.length = 0;
     for (
     preFlushIndex = 0;
     preFlushIndex < activePreFlushCbs.length;
     preFlushIndex++
     ) {
     activePreFlushCbs[preFlushIndex]();
     }
     activePreFlushCbs = null;
     preFlushIndex = 0;
     currentPreFlushParentJob = null;
     // 递归刷新,直到耗尽为止
     flushPreFlushCbs(seen, parentJob);
     }
    }
    // 刷新「渲染之“后”要执行的回调」
    export function flushPostFlushCbs(seen?: CountMap) {
     if (pendingPostFlushCbs.length) {
     const deduped = [...new Set(pendingPostFlushCbs)];
     pendingPostFlushCbs.length = 0;
     if (activePostFlushCbs) {
     activePostFlushCbs.push(...deduped);
     return;
     }
     activePostFlushCbs = deduped;
     activePostFlushCbs.sort((a, b) => getId(a) - getId(b));
     for (
     postFlushIndex = 0;
     postFlushIndex < activePostFlushCbs.length;
     postFlushIndex++
     ) {
     activePostFlushCbs[postFlushIndex]();
     }
     activePostFlushCbs = null;
     postFlushIndex = 0;
     }
    }
    

    依赖注入 2.x

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    
    /**
     * 在 Vue 初始化的过程中(Vue.prototype._init)
     * 先会调用 initInjections(vm) 再调用 initProvide(vm)
     * 1. Reflect.ownKeys/Object.keys 获取 inject 配置的 keys
     * 2. inject[keys[i]].from 获取对应的 provideKey
     * 3. 通过 vm.$parent 向上查找组件
     * 4. 直到 vm._provided && hasOwn(vm._provided, provideKey)
     * 5. 取出 vm._provided[provideKey] 的值或者 inject[key].default 的值
     * 6. 得到所有 inject 对应的值之后,调用 defineReactive 将其变为响应式
     */
    export function initProvide(vm: Component) {
     // 获取 provide 配置
     const provide = vm.$options.provide;
     if (provide) {
     // 兼容 provide 的两种写法
     vm._provided = typeof provide === 'function' ? provide.call(vm) : provide;
     }
    }
    export function initInjections(vm: Component) {
     // 解析 inject 获取结果
     const result = resolveInject(vm.$options.inject, vm);
     if (result) {
     toggleObserving(false);
     Object.keys(result).forEach(key => {
     defineReactive(vm, key, result[key]);
     });
     toggleObserving(true);
     }
    }
    export function resolveInject(inject: any, vm: Component): ?Object {
     if (inject) {
     const result = Object.create(null);
     /**
     * Reflect.ownKeys 自身可枚举属性 + 自身不可枚举属性 + Symbol 属性 + length
     * for...in... 自身可枚举属性 + 原型链
     * Object.keys 自身可枚举属性
     */
     const keys = hasSymbol ? Reflect.ownKeys(inject) : Object.keys(inject);
     for (let i = 0; i < keys.length; i++) {
     const key = keys[i]; // 获取注入的 key
     if (key === '__ob__') continue; // 跳过 __ob__
     const provideKey = inject[key].from; // 对应 provider 的 key
     let source = vm;
     while (source) {
     // 通过 $parent 向上查找
     if (source._provided && hasOwn(source._provided, provideKey)) {
     result[key] = source._provided[provideKey];
     break;
     }
     source = source.$parent;
     }
     // 没有找到?
     if (!source) {
     // 如果有默认值,使用默认值
     if ('default' in inject[key]) {
     const provideDefault = inject[key].default;
     result[key] =
     typeof provideDefault === 'function'
     ? provideDefault.call(vm)
     : provideDefault;
     } else {
     // 否则报错
     warn(`Injection "${key}" not found`, vm);
     }
     }
     }
     return result;
     }
    }
    

    依赖注入 3.x

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    
    export function provide(key, value) {
     if (!currentInstance) {
     warn(`provide() 只能在用在 setup() 里`);
     } else {
     let provides = currentInstance.provides;
     /**
     * 默认,实例会继承父级的 provides 对象
     * 组件会基于父级的 provides(作为原型)创建自己的 provides
     * 这样,在“Inject”的时候,我们可以简单地查找来自“直接父级”的注入,并让原型链来完成这项工作
     */
     const parentProvides =
     currentInstance.parent && currentInstance.parent.provides; // 找到父级的 provides
     if (parentProvides === provides) {
     // 基于父级的 povides 创建自己的 provides
     provides = currentInstance.provides = Object.create(parentProvides);
     }
     provides[key] = value;
     }
    }
    export function inject(
     key: InjectionKey<any> | string,
     defaultValue?: unknown,
     treatDefaultAsFactory = false
    ) {
     // 找到离自己最近的父组件的 provides 或着 vnode 的上下文
     const instance = currentInstance || currentRenderingInstance;
     if (instance) {
     const provides =
     instance.parent == null
     ? instance.vnode.appContext && instance.vnode.appContext.provides
     : instance.parent.provides;
     // 在 provies 里找到 inject 对应的 key 的值,返回即可
     if (provides && (key as string | symbol) in provides) {
     return provides[key as string];
     } else if (arguments.length > 1) {
     // 找不到的情况下,使用默认值
     return treatDefaultAsFactory && isFunction(defaultValue)
     ? defaultValue()
     : defaultValue;
     } else {
     warn(`找不到`);
     }
     } else {
     warn(`inject() 只能在用在 setup() 里`);
     }
    }
    

    生命周期 2.x

    所有生命周期,都是通过 callHook 调用的。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    export function callHook(vm: Component, hook: string) {
     pushTarget();
     const handlers = vm.$options[hook]; // 找到钩子函数
     const info = `${hook} hook`;
     if (handlers) {
     for (let i = 0, j = handlers.length; i < j; i++) {
     // 如果是 mixin/extend,handlers 有可能是数组
     invokeWithErrorHandling(handlers[i], vm, null, vm, info);
     }
     }
     if (vm._hasHookEvent) {
     /**
     * 如果有 vm.$on('hook:xxx', () => {})
     * vm._hasHookEvent 会被置为 true
     * 需要用 $emit 调用
     */
     vm.$emit('hook:' + hook);
     }
     popTarget();
    }
    

    Vue 是一个构造函数,在被 new 之前,需要经过初始化和混入操作。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    
    function Vue(options) {
     if (!(this instanceof Vue)) {
     warn('Vue 只能被 new 操作符调用');
     }
     this._init(options);
    }
    // Vue.prototype._init = ...
    initMixin(Vue);
    /**
     * Vue.prototype.$data = ...
     * Vue.prototype.$props = ...
     * Vue.prototype.$set = ...
     * Vue.prototype.$delete = ...
     * Vue.prototype.$watch = ...
     */
    stateMixin(Vue);
    /**
     * Vue.prototype.$on = ...
     * Vue.prototype.$once = ...
     * Vue.prototype.$off = ...
     * Vue.prototype.$emit = ...
     */
    eventsMixin(Vue);
    /**
     * Vue.prototype._update = ...
     * Vue.prototype.$forceUpdate = ...
     * Vue.prototype.$destroy = ...
     */
    lifecycleMixin(Vue);
    /**
     * Vue.prototype.$nextTick = ...
     * Vue.prototype._render = ...
     *
     */
    renderMixin(Vue);
    export function initMixin(Vue: Class<Component>) {
     Vue.prototype._init = function (options?: Object) {
     const vm: Component = this;
     let startTag, endTag;
     vm._uid = uid++; // 每个实例唯一的
     vm._isVue = true; // 不要 observe 我
     // 合并 options
     if (options && options._isComponent) {
     initInternalComponent(vm, options);
     } else {
     vm.$options = mergeOptions(
     resolveConstructorOptions(vm.constructor),
     options || {},
     vm
     );
     }
     vm._renderProxy = vm;
     vm._self = vm; // 我自己就是我自己
     initLifecycle(vm); // 初始化属性,定位非抽象父级
     initEvents(vm); // 更新父级给自己绑定的事件
     initRender(vm); // 处理插槽的渲染上下文
     callHook(vm, 'beforeCreate');
     initInjections(vm); // 在解析 data/props 之前先解析 injections
     initState(vm); // 按顺序初始化 props、methods、data、computed、watch
     initProvide(vm); // 在解析 data/props 之后先解析 provide
     callHook(vm, 'created');
     if (vm.$options.el) {
     vm.$mount(vm.$options.el); // 挂载,调用 mountComponent
     }
     };
    }
    

    created 钩子调用之后,会判断 vm.$options.el 是否存在,接着会调用 mountComponent。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    
    Vue.prototype.$mount = function (
     el?: string | Element,
     hydrating?: boolean
    ): Component {
     el = el && inBrowser ? query(el) : undefined;
     return mountComponent(this, el, hydrating);
    };
    export function mountComponent(
     vm: Component,
     el: ?Element,
     hydrating?: boolean
    ): Component {
     vm.$el = el;
     if (!vm.$options.render) {
     vm.$options.render = createEmptyVNode;
     }
     callHook(vm, 'beforeMount'); // 找到挂载点,render 准备好渲染的内容之后,调用“beforeMount”
     let updateComponent = () => {
     vm._update(vm._render(), hydrating);
     };
     // renderWatcher 被 render 用到的变量的 getter 收集
     new Watcher(
     vm,
     updateComponent,
     noop,
     {
     before() {
     /**
     * flushSchedulerQueue 的时候会执行这个函数
     * 在 watcher.before 之前执行
     * 如果组件已经挂载,则执行‘beforeUpdate’
     */
     if (vm._isMounted && !vm._isDestroyed) {
     callHook(vm, 'beforeUpdate');
     }
     }
     },
     true /* isRenderWatcher */
     );
     // renderWatcher 初始化完成之后,调用“mounted”
     if (vm.$vnode == null) {
     vm._isMounted = true;
     callHook(vm, 'mounted');
     }
     return vm;
    }
    

    updated 钩子会在前边总结的“异步队列”里调用;下面来看 destroy。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    
    Vue.prototype.$destroy = function () {
     const vm: Component = this;
     if (vm._isBeingDestroyed) {
     // 如果组件正在销毁,别往下走了
     return;
     }
     // 进入销毁流程
     callHook(vm, 'beforeDestroy');
     vm._isBeingDestroyed = true;
     const parent = vm.$parent;
     if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
     // 如果父级没有销毁,将自己从父级的子组件中删除
     remove(parent.$children, vm);
     }
     // 销毁 renderWatcher
     if (vm._watcher) {
     vm._watcher.teardown();
     }
     // 销毁 computedWatchers 和 其他的 watchers
     let i = vm._watchers.length;
     while (i--) {
     vm._watchers[i].teardown();
     }
     // 移除 observer
     if (vm._data.__ob__) {
     vm._data.__ob__.vmCount--;
     }
     // 我死了
     vm._isDestroyed = true;
     // 将组件对应的 VNode 置为 null
     vm.__patch__(vm._vnode, null);
     // 我真的死了
     callHook(vm, 'destroyed');
     // 移除所有事件绑定
     vm.$off();
     if (vm.$el) {
     vm.$el.__vue__ = null;
     }
     if (vm.$vnode) {
     vm.$vnode.parent = null;
     }
    };
    

    生命周期 3.x

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    
    export const enum LifecycleHooks {
     BEFORE_CREATE = 'bc',
     CREATED = 'c',
     BEFORE_MOUNT = 'bm',
     MOUNTED = 'm',
     BEFORE_UPDATE = 'bu',
     UPDATED = 'u',
     BEFORE_UNMOUNT = 'bum',
     UNMOUNTED = 'um',
     DEACTIVATED = 'da',
     ACTIVATED = 'a',
     RENDER_TRIGGERED = 'rtg',
     RENDER_TRACKED = 'rtc',
     ERROR_CAPTURED = 'ec'
    }
    export const createHook = lifecycle => (hook, target) =>
     injectHook(lifecycle, hook, target);
    export function injectHook(
     type: LifecycleHooks,
     hook: Function & { __weh?: Function },
     target: ComponentInternalInstance | null = currentInstance,
     prepend: boolean = false
    ): Function | undefined {
     if (target) {
     const hooks = target[type] || (target[type] = []); // 保存着实例对应的钩子函数的数组
     const wrappedHook =
     hook.__weh || // __weh 的意思是“带有错误处理的”
     (hook.__weh = (...args: unknown[]) => {
     if (target.isUnmounted) {
     return;
     }
     pauseTracking(); // 在 Hook 中禁用依赖收集
     /**
     * 在 Hook 调用期间,设置 currentInstance 为 target
     * 只有当用户做了一些 funky 操作,这条假设(钩子不会同步触发其他钩子)才不成立
     */
     setCurrentInstance(target);
     const res = callWithAsyncErrorHandling(hook, target, type, args);
     setCurrentInstance(null); // 释放 currentInstance
     resetTracking(); // 恢复依赖收集
     return res;
     });
     if (prepend) {
     hooks.unshift(wrappedHook);
     } else {
     hooks.push(wrappedHook);
     }
     return wrappedHook;
     }
    }
    // 在 setup 内部,没有 beforeCreate 和 created 钩子;setup 充当了这两个钩子的角色
    export const onBeforeMount = createHook(LifecycleHooks.BEFORE_MOUNT);
    export const onMounted = createHook(LifecycleHooks.MOUNTED);
    export const onBeforeUpdate = createHook(LifecycleHooks.BEFORE_UPDATE);
    export const onUpdated = createHook(LifecycleHooks.UPDATED);
    export const onBeforeUnmount = createHook(LifecycleHooks.BEFORE_UNMOUNT);
    export const onUnmounted = createHook(LifecycleHooks.UNMOUNTED);
    // 酱紫写,是为了区分 ErrorCapturedHook 与普通 Hook 的不同
    export type ErrorCapturedHook = (
     err: unknown,
     instance: ComponentPublicInstance | null,
     info: string
    ) => boolean | void;
    export const onErrorCaptured = (hook, target) => {
     injectHook(LifecycleHooks.ERROR_CAPTURED, hook, target);
    };
    

    在 setup 内部,没有 beforeCreate 和 created 钩子;setup 充当了这两个钩子的角色。对于 Options API,beforeCreate 和 created 继续存在;组件在 mountComponent 时会调用 setupComponent,最终会调用 applyOptions 将 options API 和 composition API 进行合并。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    
    export function applyOptions(
     instance: ComponentInternalInstance,
     options: ComponentOptions,
     deferredData: DataFn[] = [],
     deferredWatch: ComponentWatchOptions[] = [],
     deferredProvide: (Data | Function)[] = [],
     asMixin: boolean = false
    ) {
     // 在明确了上下文和 this 之后,应用“GlobalMixins”之前会调用“beforeCreate”
     if (!asMixin) {
     isInBeforeCreate = true;
     callSyncHook(
     'beforeCreate',
     LifecycleHooks.BEFORE_CREATE,
     options,
     instance,
     globalMixins
     );
     isInBeforeCreate = false;
     // 应用 GlobalMixins
     }
     // 应用 extendsOptions
     // 应用 mixins
     // 处理 injectOptions
     // 合并 methods
     // 处理 dataOptions
     // 处理 computedOptions
     // 处理 watchOptions
     // 处理 provideOptions
     if (!asMixin) {
     callSyncHook(
     'created',
     LifecycleHooks.CREATED,
     options,
     instance,
     globalMixins
     );
     }
     // 处理 lifecycleOptions
    }
    function callSyncHook(
     name: 'beforeCreate' | 'created',
     type: LifecycleHooks,
     options: ComponentOptions,
     instance: ComponentInternalInstance,
     globalMixins: ComponentOptions[]
    ) {
     // 循环 globalMixins 的深度,循环调用 mixins[i][name]
     callHookFromMixins(name, type, globalMixins, instance);
     const { extends: base, mixins } = options;
     if (base) {
     // 递归调用 base[name]
     callHookFromExtends(name, type, base, instance);
     }
     if (mixins) {
     // 循环 mixins 的深度,循环调用 mixins[i][name]
     callHookFromMixins(name, type, mixins, instance);
     }
     // 调用自身的 beforeCreate 或 created
     const selfHook = options[name];
     if (selfHook) {
     callWithAsyncErrorHandling(selfHook.bind(instance.proxy!), instance, type);
     }
    }
    

    onActivated 和 onDeactivated 会通过 registerKeepAliveHook 进行注册。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    
    export function onActivated(hook, target) {
     registerKeepAliveHook(hook, LifecycleHooks.ACTIVATED, target);
    }
    export function onDeactivated(hook, target) {
     registerKeepAliveHook(hook, LifecycleHooks.DEACTIVATED, target);
    }
    function registerKeepAliveHook(
     hook: Function & { __wdc?: Function },
     type: LifecycleHooks,
     target: ComponentInternalInstance | null = currentInstance
    ) {
     // __wdc 的意思是“带有失活检查的”,这玩意,调度器会删除重复的钩子
     const wrappedHook =
     hook.__wdc ||
     (hook.__wdc = () => {
     // 仅当目标实例不在 deactivated 状态中时才触发 Hook
     let current: ComponentInternalInstance | null = target;
     while (current) {
     if (current.isDeactivated) {
     return;
     }
     current = current.parent;
     }
     hook();
     });
     injectHook(type, wrappedHook, target);
     /**
     * 除了在目标实例上注册它之外
     * 向上遍历父链,并将其注册到‘are keep-alive roots’的所有祖先实例上
     * 这避免了在调用这些钩子时需要遍历整个组件树,更重要的是,避免了跟踪数组中的子组件
     */
     if (target) {
     let current = target.parent;
     while (current && current.parent) {
     if (isKeepAlive(current.parent.vnode)) {
     injectToKeepAliveRoot(wrappedHook, type, target, current);
     }
     current = current.parent;
     }
     }
    }
    

    computed 2.x

    computed 是定义在 vm 上的特殊 getter,computedWatcher 在创建的时候会将 dirty 和 lazy 属性设置为 true;当 computed 的值被访问,触发 computedGetter,computedGetter 会调用 watcher.evaluate 进行求值。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    
    export function initState (vm: Component) {
     // ...
     if (opts.computed) initComputed(vm, opts.computed)
     // ...
    }
    // 在 initState 过程中会调用 initComputed(vm, opts.computed)
    function initComputed(vm, computed) {
     // 实例的 computed 创建的 watcher 都保存在 vm._computedWatchers
     const watchers = (vm._computedWatchers = Object.create(null));
     // 循环组件内 computed 配置
     for (const key in computed) {
     /**
     * computed 有两种形式:
     * 1. a: () => someValue
     * 2. b: { get:..., set:... }
     * 最终,都会转化为 getter/setter
     */
     const userDef = computed[key];
     const getter = typeof userDef === 'function' ? userDef : userDef.get;
     if (getter == null) {
     warn('没有 getter?开什么玩笑');
     }
     // 为计算属性创建 watcher
     watchers[key] = new Watcher(vm, getter || noop, noop, { lazy: true });
     // 判断 computed 属性是否和 vm.$data、vm.$options.methods、vm.$options.props 冲突
     if (!(key in vm)) {
     defineComputed(vm, key, userDef);
     } else {
     // 冲突的话,错抱起来
     if (key in vm.$data) {
     warn(`already defined in data`);
     } else if (vm.$options.props && key in vm.$options.props) {
     warn(`already defined as a prop`);
     } else if (vm.$options.methods && key in vm.$options.methods) {
     warn(`already defined as a method`);
     }
     }
     }
    }
    export function defineComputed(target, key, userDef) {
     userDef.get = typeof userDef === 'function' ? userDef : userDef.get;
     Object.defineProperty(target, key, {
     get: createComputedGetter(key)
     set: userDef.set || noop
     });
    }
    function createComputedGetter(key) {
     return function computedGetter() {
     const watcher = this._computedWatchers && this._computedWatchers[key];
     if (watcher) {
     if (watcher.dirty) {
     // 用到再求值
     watcher.evaluate();
     }
     if (Dep.target) {
     // 依赖收集
     watcher.depend();
     }
     return watcher.value;
     }
     };
    }
    

    computed 3.x

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    
    // 处理两种参数形式
    export function computed<T>(
     getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
    ) {
     let getter: ComputedGetter<T>;
     let setter: ComputedSetter<T>;
     // 标准化参数
     if (isFunction(getterOrOptions)) {
     getter = getterOrOptions;
     setter = NOOP;
     } else {
     getter = getterOrOptions.get;
     setter = getterOrOptions.set;
     }
     // 返回 computed 对象
     return new ComputedRefImpl(
     getter,
     setter,
     isFunction(getterOrOptions) || !getterOrOptions.set
     ) as any;
    }
    class ComputedRefImpl<T> {
     private _value!: T;
     private _dirty = true;
     public readonly __v_isRef = true; // computed 是一个 ref
     public readonly effect: ReactiveEffect<T>;
     public readonly [ReactiveFlags.IS_READONLY]: boolean;
     constructor(
     getter: ComputedGetter<T>,
     private readonly _setter: ComputedSetter<T>,
     isReadonly: boolean
     ) {
     this.effect = effect(getter, {
     // 由于 lazy 为 true,effect 不会先执行一遍,而是等到 computedGetter 被访问时,才会运行 effect
     lazy: true,
     // 每当依赖更新,都会调用 scheduler,将 _dirty 置为 true,等待 getter 再次求值
     scheduler: () => {
     if (!this._dirty) {
     // 数据“脏”了
     this._dirty = true;
     // 派发通知,通知运行访问该计算属性的 activeEffect
     trigger(toRaw(this), 'set', 'value');
     }
     }
     });
     this['__v_isReadonly'] = isReadonly;
     }
     get value() {
     /**
     * 渲染开始时,computedGetter 被触发
     * 调用 this.effect,此时 activeEffect 被赋值
     * track 被调用,依赖被收集
     */
     if (this._dirty) {
     // 值是通过调用 effect 获得的
     this._value = this.effect();
     // 当被外界 get 之后,数据不再“脏”了
     this._dirty = false;
     }
     // 依赖收集,收集运行访问该计算属性的 activeEffect
     track(toRaw(this), 'get', 'value');
     return this._value;
     }
     set value(newValue: T) {
     this._setter(newValue);
     }
    }
    

    diff 2.x

     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    479
    480
    481
    482
    483
    484
    485
    486
    487
    488
    489
    490
    491
    492
    493
    494
    495
    496
    497
    498
    499
    500
    501
    502
    503
    504
    505
    506
    507
    508
    509
    510
    511
    512
    513
    514
    515
    516
    517
    518
    519
    520
    521
    522
    523
    524
    525
    526
    527
    528
    529
    530
    531
    532
    533
    534
    535
    536
    537
    538
    539
    540
    541
    542
    543
    544
    545
    546
    547
    548
    549
    550
    551
    552
    553
    554
    555
    556
    557
    558
    559
    560
    561
    562
    563
    564
    565
    566
    567
    568
    569
    570
    571
    572
    573
    574
    575
    576
    577
    578
    579
    580
    581
    582
    583
    584
    585
    586
    
    // 挂载
    export function mountComponent(
     vm: Component,
     el: ?Element,
     hydrating?: boolean
    ): Component {
     vm.$el = el;
     callHook(vm, 'beforeMount');
     let updateComponent = () => {
     vm._update(vm._render(), hydrating);
     };
     // 创建 renderWatcher
     new Watcher(
     vm,
     updateComponent,
     noop,
     {
     before() {
     if (vm._isMounted && !vm._isDestroyed) {
     // 如果是更新流程,调用“beforeUpdate”钩子
     callHook(vm, 'beforeUpdate');
     }
     }
     },
     true
     );
     hydrating = false;
     if (vm.$vnode == null) {
     vm._isMounted = true;
     callHook(vm, 'mounted');
     }
     return vm;
    }
    // 调用 render,返回 VNode
    Vue.prototype._render = function (): VNode {
     const vm: Component = this;
     const { render, _parentVnode } = vm.$options;
     if (_parentVnode) {
     vm.$scopedSlots = normalizeScopedSlots(
     _parentVnode.data.scopedSlots,
     vm.$slots,
     vm.$scopedSlots
     );
     }
     // 设置父节点,这允许 render 访问占位符节点上的数据
     vm.$vnode = _parentVnode;
     // 渲染自身
     let vnode;
     try {
     /**
     * 不需要维护栈的原因是,render 是依次分开被调用的
     * 当父级 patched 之后,嵌套组件的 render 才会被调用
     */
     currentRenderingInstance = vm;
     /**
     * vm._renderProxy = vm
     * vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
     */
     vnode = render.call(vm._renderProxy, vm.$createElement);
     } catch (e) {
     handleError(e, vm, `render`);
     if (vm.$options.renderError) {
     try {
     vnode = vm.$options.renderError.call(
     vm._renderProxy,
     vm.$createElement,
     e
     );
     } catch (e) {
     handleError(e, vm, `renderError`);
     vnode = vm._vnode;
     }
     } else {
     vnode = vm._vnode;
     }
     } finally {
     // 渲染完成,重置变量
     currentRenderingInstance = null;
     }
     if (Array.isArray(vnode) && vnode.length === 1) {
     vnode = vnode[0];
     }
     if (!(vnode instanceof VNode)) {
     if (Array.isArray(vnode)) {
     warn('2.x 里 render 不能返回多个节点,需要有一个根节点');
     }
     // 返回空节点代替
     vnode = createEmptyVNode();
     }
     // 设置父节点
     vnode.parent = _parentVnode;
     return vnode;
    };
    // h 函数,生成 VNode
    export function createElement(
     context: Component,
     tag: any,
     data: any,
     children: any,
     normalizationType: any,
     alwaysNormalize: boolean
    ): VNode | Array<VNode> {
     if (Array.isArray(data) || isPrimitive(data)) {
     normalizationType = children;
     children = data;
     data = undefined;
     }
     if (isTrue(alwaysNormalize)) {
     normalizationType = ALWAYS_NORMALIZE;
     }
     return _createElement(context, tag, data, children, normalizationType);
    }
    export function _createElement(
     context: Component,
     tag?: string | Class<Component> | Function | Object,
     data?: VNodeData,
     children?: any,
     normalizationType?: number
    ): VNode | Array<VNode> {
     if (isDef(data) && isDef(data.__ob__)) {
     return createEmptyVNode();
     }
     // <component :is="xxx" />
     if (isDef(data) && isDef(data.is)) {
     tag = data.is;
     }
     if (!tag) {
     return createEmptyVNode();
     }
     if (isDef(data) && isDef(data.key) && !isPrimitive(data.key)) {
     warn('key 只能为“原始值”');
     }
     // children 为数组并且第一项与函数
     if (Array.isArray(children) && typeof children[0] === 'function') {
     data = data || {};
     // 作为作用域插槽的默认值
     data.scopedSlots = { default: children[0] };
     children.length = 0;
     }
     if (normalizationType === ALWAYS_NORMALIZE) {
     children = normalizeChildren(children);
     } else if (normalizationType === SIMPLE_NORMALIZE) {
     children = simpleNormalizeChildren(children);
     }
     let vnode, ns;
     if (typeof tag === 'string') {
     let Ctor;
     ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag);
     if (config.isReservedTag(tag)) {
     // 平台原生/内建标签
     if (isDef(data) && isDef(data.nativeOn) && data.tag !== 'component') {
     warn('.native 修饰符只能用在组件上,而不是原生元素上');
     }
     vnode = new VNode(
     config.parsePlatformTagName(tag),
     data,
     children,
     undefined,
     undefined,
     context
     );
     } else if (
     (!data || !data.pre) &&
     isDef((Ctor = resolveAsset(context.$options, 'components', tag)))
     ) {
     // Vue 组件,使用 createComponent 创建 VNode
     vnode = createComponent(Ctor, data, context, children, tag);
     } else {
     // 未知元素
     vnode = new VNode(tag, data, children, undefined, undefined, context);
     }
     } else {
     // 组件选项/构造函数
     vnode = createComponent(tag, data, context, children);
     }
     if (Array.isArray(vnode)) {
     return vnode;
     } else if (isDef(vnode)) {
     if (isDef(ns)) applyNS(vnode, ns);
     if (isDef(data)) registerDeepBindings(data);
     return vnode;
     } else {
     return createEmptyVNode();
     }
    }
    export function createComponent(
     Ctor: Class<Component> | Function | Object | void,
     data: ?VNodeData,
     context: Component,
     children: ?Array<VNode>,
     tag?: string
    ): VNode | Array<VNode> | void {
     if (isUndef(Ctor)) {
     return;
     }
     const baseCtor = context.$options._base;
     if (isObject(Ctor)) {
     Ctor = baseCtor.extend(Ctor);
     }
     if (typeof Ctor !== 'function') {
     return;
     }
     // 异步组件
     let asyncFactory;
     if (isUndef(Ctor.cid)) {
     asyncFactory = Ctor;
     Ctor = resolveAsyncComponent(asyncFactory, baseCtor);
     if (Ctor === undefined) {
     return createAsyncPlaceholder(asyncFactory, data, context, children, tag);
     }
     }
     data = data || {};
     resolveConstructorOptions(Ctor);
     // v-model 转换为 props 和事件
     if (isDef(data.model)) {
     transformModel(Ctor.options, data);
     }
     // 提取 props
     const propsData = extractPropsFromVNodeData(data, Ctor, tag);
     if (isTrue(Ctor.options.functional)) {
     // 函数式组件
     return createFunctionalComponent(Ctor, propsData, data, context, children);
     }
     const listeners = data.on;
     // 使用 on.native 替代 nativeOn
     data.on = data.nativeOn;
     if (isTrue(Ctor.options.abstract)) {
     // 抽象组件除了 props、listeners、slot,什么都不管
     const slot = data.slot;
     data = {};
     if (slot) {
     data.slot = slot;
     }
     }
     installComponentHooks(data);
     const name = Ctor.options.name || tag;
     const vnode = new VNode(
     `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
     data,
     undefined,
     undefined,
     undefined,
     context,
     { Ctor, propsData, listeners, tag, children },
     asyncFactory
     );
     return vnode;
    }
    // 将 VNode 更新到 DOM
    Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
     const vm: Component = this;
     const prevEl = vm.$el; // 旧 VNode 挂载的 DOM 节点
     const prevVnode = vm._vnode; // 旧 VNode
     const restoreActiveInstance = setActiveInstance(vm);
     vm._vnode = vnode;
     if (!prevVnode) {
     // 初次渲染
     vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false);
     } else {
     // 应用更新
     vm.$el = vm.__patch__(prevVnode, vnode);
     }
     restoreActiveInstance();
     if (prevEl) {
     prevEl.__vue__ = null;
     }
     if (vm.$el) {
     vm.$el.__vue__ = vm;
     }
     // 如果父组件是告诫组件,更新他的 $el 属性
     if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
     vm.$parent.$el = vm.$el;
     }
    };
    /**
     * patch 在做什么?
     * 1. vnode 不存在,oldVnode 存在,销毁旧节点
     * 2. vnode 存在:
     * 2.1 oldVnode 不存在,组件首次渲染
     * 2.2 oldVnode 存在:
     * 2.2.1 oldVnode 是 DOM 元素,根组件的首次渲染
     * 2.2.2 oldVnode 不是 DOM 元素,组件更新:
     * 2.2.2.1 oldVnode 与 vnode 相同节点,调用 patchVNode
     * 2.2.2.2 oldVnode 与 vnode 不同节点,卸载 oldVnode 创建 vnode
     * 什么是相同节点?
     * 1. key 相等
     * 2. tag 相同
     * 3. 都(不)是注释节点
     * 4. data 都(没有)定义
     * 5. 如果是 input,type 要一致
     * 6. 如果都是异步组件,工厂函数要完全相同
     */
    Vue.prototype.__patch__ = function patch(
     oldVnode,
     vnode,
     hydrating,
     removeOnly
    ) {
     // 旧节点存在新节点不存在,触发旧节点的 destory
     if (isUndef(vnode)) {
     if (isDef(oldVnode)) invokeDestroyHook(oldVnode);
     return;
     }
     let isInitialPatch = false; // 是否是初次 patch
     const insertedVnodeQueue = [];
     if (isUndef(oldVnode)) {
     // 旧节点不存在?肯定是第一次 patch,创建新的根元素
     isInitialPatch = true;
     createElm(vnode, insertedVnodeQueue);
     } else {
     // 旧节点存在,属于更新流程
     const isRealElement = isDef(oldVnode.nodeType);
     if (!isRealElement && sameVnode(oldVnode, vnode)) {
     patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly);
     } else {
     if (isRealElement) {
     // oldVnode 是 DOM 节点
     if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {
     oldVnode.removeAttribute(SSR_ATTR);
     hydrating = true;
     }
     oldVnode = emptyNodeAt(oldVnode);
     }
     const oldElm = oldVnode.elm;
     const parentElm = nodeOps.parentNode(oldElm); // 旧 VNode 对应元素的父级
     // 创建一个 DOM 元素
     createElm(
     vnode,
     insertedVnodeQueue,
     oldElm._leaveCb ? null : parentElm,
     nodeOps.nextSibling(oldElm)
     );
     // 递归更新父级的占位节点元素
     if (isDef(vnode.parent)) {
     let ancestor = vnode.parent;
     const patchable = isPatchable(vnode);
     while (ancestor) {
     for (let i = 0; i < cbs.destroy.length; ++i) {
     cbs.destroy[i](ancestor);
     }
     ancestor.elm = vnode.elm;
     if (patchable) {
     for (let i = 0; i < cbs.create.length; ++i) {
     cbs.create[i](emptyNode, ancestor);
     }
     const insert = ancestor.data.hook.insert;
     if (insert.merged) {
     for (let i = 1; i < insert.fns.length; i++) {
     insert.fns[i]();
     }
     }
     } else {
     registerRef(ancestor);
     }
     ancestor = ancestor.parent;
     }
     }
     if (isDef(parentElm)) {
     removeVnodes([oldVnode], 0, 0); // 销毁旧节点
     } else if (isDef(oldVnode.tag)) {
     invokeDestroyHook(oldVnode); // 触发就节点的 destory 钩子
     }
     }
     }
     invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch);
     return vnode.elm;
    };
    // 当 oldVnode 与 vnode 相同节点,会调用 patchVNode 更新节点
    function patchVnode(
     oldVnode,
     vnode,
     insertedVnodeQueue,
     ownerArray,
     index,
     removeOnly
    ) {
     // 一摸一样?返回
     if (oldVnode === vnode) {
     return;
     }
     if (isDef(vnode.elm) && isDef(ownerArray)) {
     vnode = ownerArray[index] = cloneVNode(vnode);
     }
     const elm = (vnode.elm = oldVnode.elm);
     if (isTrue(oldVnode.isAsyncPlaceholder)) {
     // 旧 VNode 异步占位
     if (isDef(vnode.asyncFactory.resolved)) {
     hydrate(oldVnode.elm, vnode, insertedVnodeQueue);
     } else {
     vnode.isAsyncPlaceholder = true;
     }
     return;
     }
     // 如果是静态树,可以复用元素
     if (
     isTrue(vnode.isStatic) &&
     isTrue(oldVnode.isStatic) &&
     vnode.key === oldVnode.key &&
     (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))
     ) {
     // 新旧都是静态的,且 key 相同
     vnode.componentInstance = oldVnode.componentInstance;
     return;
     }
     let i;
     const data = vnode.data;
     // 调用 prepatch 钩子(更新之前的钩子)
     if (isDef(data) && isDef((i = data.hook)) && isDef((i = i.prepatch))) {
     i(oldVnode, vnode);
     }
     const oldCh = oldVnode.children; // 旧 VNode 子节点
     const ch = vnode.children; // 新 VNode 子节点
     if (isDef(data) && isPatchable(vnode)) {
     // VNode 可修补
     for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode);
     if (isDef((i = data.hook)) && isDef((i = i.update))) i(oldVnode, vnode); // 调用 VNode 的 Update
     }
     if (isUndef(vnode.text)) {
     if (isDef(oldCh) && isDef(ch)) {
     // 新旧子节点不同,需要更新
     if (oldCh !== ch)
     updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly);
     } else if (isDef(ch)) {
     // 旧子节点是文本?清空文本,添加新子节点
     if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '');
     addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);
     } else if (isDef(oldCh)) {
     // 没有新子节点,仅有旧的子节点,那么删除旧的就好
     removeVnodes(oldCh, 0, oldCh.length - 1);
     } else if (isDef(oldVnode.text)) {
     // 清空旧的文本子节点
     nodeOps.setTextContent(elm, '');
     }
     } else if (oldVnode.text !== vnode.text) {
     // 如果新旧子节点都为文本节点,更新文字就好
     nodeOps.setTextContent(elm, vnode.text);
     }
     if (isDef(data)) {
     // 调用 postpatch 钩子(更新之后的钩子)
     if (isDef((i = data.hook)) && isDef((i = i.postpatch))) i(oldVnode, vnode);
     }
    }
    // diff(更新新旧 VNode 的子节点)
    function updateChildren(
     parentElm, // VNode 对应的 DOM 元素
     oldCh, // 旧 VNode 的子元素
     newCh, // 新 VNode 的子元素
     insertedVnodeQueue,
     removeOnly
    ) {
     // ...
     let oldStartIdx = 0;
     let oldEndIdx = oldCh.length - 1;
     let oldStartVnode = oldCh[0]; // 下一个未经 patch 的旧子 VNode 节点,在此索引之前的旧子 VNode 都已经处理完毕
     let oldEndVnode = oldCh[oldEndIdx]; // 最后一个未经 patch 的旧子 VNode 节点,在此索引之后的旧子 VNode 都已经处理完毕
     // ...
     let newStartIdx = 0;
     let newEndIdx = newCh.length - 1;
     let newStartVnode = newCh[0]; // 下一个未经 patch 的新子 VNode 节点,在此索引之前的新子 VNode 都已经处理完毕
     let newEndVnode = newCh[newEndIdx]; // 最后一个未经 patch 的新子 VNode 节点,在此索引之前的新子 VNode 都已经处理完毕
     // ...
     let oldKeyToIdx, idxInOld, vnodeToMove, refElm;
     // 只有 <transition-group> 用到,确保在 transitions 期间移除元素呆在正确的相对位置
     const canMove = !removeOnly;
     /**
     * 当下一个未经处理的(新/旧)节点的索引小于等于最后一个未经处理的(新/旧)节点的索引
     * 每次循环会进行四次比较:
     * 1. sameVnode(oldStartVnode, newStartVnode)
     * 2. sameVnode(oldEndVnode, newEndVnode)
     * 3. sameVnode(oldStartVnode, newEndVnode)
     * 4. sameVnode(oldEndVnode, newStartVnode)
     */
     while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
     if (isUndef(oldStartVnode)) {
     oldStartVnode = oldCh[++oldStartIdx]; // 下一个旧的
     } else if (isUndef(oldEndVnode)) {
     oldEndVnode = oldCh[--oldEndIdx]; // 上一个旧的
     } else if (sameVnode(oldStartVnode, newStartVnode)) {
     patchVnode(
     oldStartVnode,
     newStartVnode,
     insertedVnodeQueue,
     newCh,
     newStartIdx
     );
     oldStartVnode = oldCh[++oldStartIdx]; // 下一个旧的
     newStartVnode = newCh[++newStartIdx]; // 下一个新的
     } else if (sameVnode(oldEndVnode, newEndVnode)) {
     patchVnode(
     oldEndVnode,
     newEndVnode,
     insertedVnodeQueue,
     newCh,
     newEndIdx
     );
     oldEndVnode = oldCh[--oldEndIdx]; // 上一个旧的
     newEndVnode = newCh[--newEndIdx]; // 上一个新的
     } else if (sameVnode(oldStartVnode, newEndVnode)) {
     // 下一个未经 patch 的旧子 VNode 节点 和 最后一个未经 patch 的新子 VNode 节点 相同
     patchVnode(
     oldStartVnode,
     newEndVnode,
     insertedVnodeQueue,
     newCh,
     newEndIdx
     );
     canMove &&
     nodeOps.insertBefore(
     parentElm,
     oldStartVnode.elm,
     nodeOps.nextSibling(oldEndVnode.elm)
     );
     oldStartVnode = oldCh[++oldStartIdx];
     newEndVnode = newCh[--newEndIdx];
     } else if (sameVnode(oldEndVnode, newStartVnode)) {
     // 最后一个未经 patch 的旧子 VNode 节点 和 下一个未经 patch 的新子 VNode 节点 相同
     patchVnode(
     oldEndVnode,
     newStartVnode,
     insertedVnodeQueue,
     newCh,
     newStartIdx
     );
     canMove &&
     nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);
     oldEndVnode = oldCh[--oldEndIdx];
     newStartVnode = newCh[++newStartIdx];
     } else {
     if (isUndef(oldKeyToIdx)) {
     oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx);
     }
     idxInOld = isDef(newStartVnode.key)
     ? oldKeyToIdx[newStartVnode.key]
     : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx);
     if (isUndef(idxInOld)) {
     createElm(
     newStartVnode,
     insertedVnodeQueue,
     parentElm,
     oldStartVnode.elm,
     false,
     newCh,
     newStartIdx
     );
     } else {
     vnodeToMove = oldCh[idxInOld];
     if (sameVnode(vnodeToMove, newStartVnode)) {
     patchVnode(
     vnodeToMove,
     newStartVnode,
     insertedVnodeQueue,
     newCh,
     newStartIdx
     );
     oldCh[idxInOld] = undefined;
     canMove &&
     nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm);
     } else {
     createElm(
     newStartVnode,
     insertedVnodeQueue,
     parentElm,
     oldStartVnode.elm,
     false,
     newCh,
     newStartIdx
     );
     }
     }
     newStartVnode = newCh[++newStartIdx];
     }
     }
     if (oldStartIdx > oldEndIdx) {
     refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm;
     addVnodes(
     parentElm,
     refElm,
     newCh,
     newStartIdx,
     newEndIdx,
     insertedVnodeQueue
     );
     } else if (newStartIdx > newEndIdx) {
     removeVnodes(oldCh, oldStartIdx, oldEndIdx);
     }
    }
    

    diff 3.x

     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
     100
     101
     102
     103
     104
     105
     106
     107
     108
     109
     110
     111
     112
     113
     114
     115
     116
     117
     118
     119
     120
     121
     122
     123
     124
     125
     126
     127
     128
     129
     130
     131
     132
     133
     134
     135
     136
     137
     138
     139
     140
     141
     142
     143
     144
     145
     146
     147
     148
     149
     150
     151
     152
     153
     154
     155
     156
     157
     158
     159
     160
     161
     162
     163
     164
     165
     166
     167
     168
     169
     170
     171
     172
     173
     174
     175
     176
     177
     178
     179
     180
     181
     182
     183
     184
     185
     186
     187
     188
     189
     190
     191
     192
     193
     194
     195
     196
     197
     198
     199
     200
     201
     202
     203
     204
     205
     206
     207
     208
     209
     210
     211
     212
     213
     214
     215
     216
     217
     218
     219
     220
     221
     222
     223
     224
     225
     226
     227
     228
     229
     230
     231
     232
     233
     234
     235
     236
     237
     238
     239
     240
     241
     242
     243
     244
     245
     246
     247
     248
     249
     250
     251
     252
     253
     254
     255
     256
     257
     258
     259
     260
     261
     262
     263
     264
     265
     266
     267
     268
     269
     270
     271
     272
     273
     274
     275
     276
     277
     278
     279
     280
     281
     282
     283
     284
     285
     286
     287
     288
     289
     290
     291
     292
     293
     294
     295
     296
     297
     298
     299
     300
     301
     302
     303
     304
     305
     306
     307
     308
     309
     310
     311
     312
     313
     314
     315
     316
     317
     318
     319
     320
     321
     322
     323
     324
     325
     326
     327
     328
     329
     330
     331
     332
     333
     334
     335
     336
     337
     338
     339
     340
     341
     342
     343
     344
     345
     346
     347
     348
     349
     350
     351
     352
     353
     354
     355
     356
     357
     358
     359
     360
     361
     362
     363
     364
     365
     366
     367
     368
     369
     370
     371
     372
     373
     374
     375
     376
     377
     378
     379
     380
     381
     382
     383
     384
     385
     386
     387
     388
     389
     390
     391
     392
     393
     394
     395
     396
     397
     398
     399
     400
     401
     402
     403
     404
     405
     406
     407
     408
     409
     410
     411
     412
     413
     414
     415
     416
     417
     418
     419
     420
     421
     422
     423
     424
     425
     426
     427
     428
     429
     430
     431
     432
     433
     434
     435
     436
     437
     438
     439
     440
     441
     442
     443
     444
     445
     446
     447
     448
     449
     450
     451
     452
     453
     454
     455
     456
     457
     458
     459
     460
     461
     462
     463
     464
     465
     466
     467
     468
     469
     470
     471
     472
     473
     474
     475
     476
     477
     478
     479
     480
     481
     482
     483
     484
     485
     486
     487
     488
     489
     490
     491
     492
     493
     494
     495
     496
     497
     498
     499
     500
     501
     502
     503
     504
     505
     506
     507
     508
     509
     510
     511
     512
     513
     514
     515
     516
     517
     518
     519
     520
     521
     522
     523
     524
     525
     526
     527
     528
     529
     530
     531
     532
     533
     534
     535
     536
     537
     538
     539
     540
     541
     542
     543
     544
     545
     546
     547
     548
     549
     550
     551
     552
     553
     554
     555
     556
     557
     558
     559
     560
     561
     562
     563
     564
     565
     566
     567
     568
     569
     570
     571
     572
     573
     574
     575
     576
     577
     578
     579
     580
     581
     582
     583
     584
     585
     586
     587
     588
     589
     590
     591
     592
     593
     594
     595
     596
     597
     598
     599
     600
     601
     602
     603
     604
     605
     606
     607
     608
     609
     610
     611
     612
     613
     614
     615
     616
     617
     618
     619
     620
     621
     622
     623
     624
     625
     626
     627
     628
     629
     630
     631
     632
     633
     634
     635
     636
     637
     638
     639
     640
     641
     642
     643
     644
     645
     646
     647
     648
     649
     650
     651
     652
     653
     654
     655
     656
     657
     658
     659
     660
     661
     662
     663
     664
     665
     666
     667
     668
     669
     670
     671
     672
     673
     674
     675
     676
     677
     678
     679
     680
     681
     682
     683
     684
     685
     686
     687
     688
     689
     690
     691
     692
     693
     694
     695
     696
     697
     698
     699
     700
     701
     702
     703
     704
     705
     706
     707
     708
     709
     710
     711
     712
     713
     714
     715
     716
     717
     718
     719
     720
     721
     722
     723
     724
     725
     726
     727
     728
     729
     730
     731
     732
     733
     734
     735
     736
     737
     738
     739
     740
     741
     742
     743
     744
     745
     746
     747
     748
     749
     750
     751
     752
     753
     754
     755
     756
     757
     758
     759
     760
     761
     762
     763
     764
     765
     766
     767
     768
     769
     770
     771
     772
     773
     774
     775
     776
     777
     778
     779
     780
     781
     782
     783
     784
     785
     786
     787
     788
     789
     790
     791
     792
     793
     794
     795
     796
     797
     798
     799
     800
     801
     802
     803
     804
     805
     806
     807
     808
     809
     810
     811
     812
     813
     814
     815
     816
     817
     818
     819
     820
     821
     822
     823
     824
     825
     826
     827
     828
     829
     830
     831
     832
     833
     834
     835
     836
     837
     838
     839
     840
     841
     842
     843
     844
     845
     846
     847
     848
     849
     850
     851
     852
     853
     854
     855
     856
     857
     858
     859
     860
     861
     862
     863
     864
     865
     866
     867
     868
     869
     870
     871
     872
     873
     874
     875
     876
     877
     878
     879
     880
     881
     882
     883
     884
     885
     886
     887
     888
     889
     890
     891
     892
     893
     894
     895
     896
     897
     898
     899
     900
     901
     902
     903
     904
     905
     906
     907
     908
     909
     910
     911
     912
     913
     914
     915
     916
     917
     918
     919
     920
     921
     922
     923
     924
     925
     926
     927
     928
     929
     930
     931
     932
     933
     934
     935
     936
     937
     938
     939
     940
     941
     942
     943
     944
     945
     946
     947
     948
     949
     950
     951
     952
     953
     954
     955
     956
     957
     958
     959
     960
     961
     962
     963
     964
     965
     966
     967
     968
     969
     970
     971
     972
     973
     974
     975
     976
     977
     978
     979
     980
     981
     982
     983
     984
     985
     986
     987
     988
     989
     990
     991
     992
     993
     994
     995
     996
     997
     998
     999
    1000
    1001
    1002
    1003
    1004
    1005
    1006
    1007
    1008
    1009
    1010
    1011
    1012
    1013
    1014
    1015
    1016
    1017
    1018
    1019
    1020
    1021
    1022
    1023
    1024
    1025
    1026
    1027
    1028
    1029
    1030
    1031
    1032
    1033
    1034
    1035
    1036
    1037
    1038
    1039
    1040
    1041
    1042
    1043
    1044
    1045
    1046
    1047
    1048
    1049
    1050
    1051
    1052
    1053
    1054
    1055
    1056
    1057
    1058
    1059
    1060
    1061
    1062
    1063
    1064
    1065
    1066
    1067
    1068
    1069
    1070
    1071
    1072
    1073
    1074
    1075
    1076
    1077
    1078
    1079
    1080
    1081
    1082
    1083
    1084
    1085
    1086
    1087
    1088
    1089
    1090
    1091
    1092
    1093
    1094
    1095
    1096
    1097
    1098
    1099
    1100
    1101
    1102
    1103
    1104
    1105
    1106
    1107
    1108
    1109
    1110
    1111
    1112
    1113
    1114
    1115
    1116
    1117
    1118
    1119
    1120
    1121
    1122
    1123
    1124
    1125
    1126
    1127
    1128
    1129
    1130
    1131
    1132
    1133
    1134
    1135
    1136
    1137
    1138
    1139
    1140
    1141
    1142
    1143
    1144
    1145
    1146
    1147
    1148
    1149
    1150
    1151
    1152
    1153
    1154
    1155
    1156
    1157
    1158
    1159
    1160
    1161
    1162
    1163
    1164
    1165
    1166
    1167
    1168
    1169
    1170
    1171
    1172
    1173
    1174
    1175
    1176
    1177
    1178
    1179
    1180
    1181
    1182
    1183
    1184
    1185
    1186
    1187
    1188
    1189
    1190
    1191
    1192
    1193
    1194
    1195
    1196
    1197
    1198
    1199
    1200
    1201
    1202
    1203
    1204
    1205
    1206
    1207
    1208
    1209
    1210
    1211
    1212
    1213
    1214
    1215
    1216
    1217
    1218
    1219
    1220
    1221
    1222
    1223
    1224
    1225
    1226
    1227
    1228
    1229
    1230
    1231
    1232
    1233
    1234
    1235
    1236
    1237
    1238
    1239
    1240
    1241
    1242
    1243
    1244
    1245
    1246
    1247
    1248
    1249
    1250
    1251
    1252
    1253
    1254
    1255
    1256
    1257
    1258
    1259
    1260
    1261
    1262
    1263
    1264
    1265
    1266
    1267
    1268
    1269
    1270
    1271
    1272
    1273
    1274
    1275
    1276
    1277
    1278
    1279
    1280
    1281
    1282
    1283
    1284
    1285
    1286
    1287
    1288
    1289
    1290
    1291
    1292
    1293
    1294
    1295
    1296
    1297
    1298
    1299
    1300
    1301
    1302
    1303
    
    // 入口
    export const createApp = ((...args) => {
     /**
     * patchProp 更新 props 方法集合
     * forcePatchProp = (_, key) => key === 'value'
     * nodeOps DOM 操作方法集合
     */
     const rendererOptions = extend({ patchProp, forcePatchProp }, nodeOps);
     const { render } = baseCreateRenderer(rendererOptions);
     const createApp = createAppAPI(render);
     const app = createApp(...args);
     const { mount } = app;
     // 根组件挂载节点,ShadowRoot 接口是一个 DOM 子树的根节点,它与文档的主 DOM 树分开渲染
     app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
     const container = normalizeContainer(containerOrSelector); // HTMLElement || null
     if (!container) return;
     const component = app._component;
     if (!isFunction(component) && !component.render && !component.template) {
     component.template = container.innerHTML;
     }
     // 挂载之前清空 container
     container.innerHTML = '';
     const proxy = mount(container);
     if (container instanceof Element) {
     container.removeAttribute('v-cloak');
     container.setAttribute('data-v-app', '');
     }
     return proxy;
     };
     return app;
    }) as CreateAppFunction<Element>;
    export function createAppAPI<HostElement>(
     render: RootRenderFunction
    ): CreateAppFunction<HostElement> {
     return function createApp(rootComponent, rootProps = null) {
     if (rootProps != null && !isObject(rootProps)) {
     rootProps = null;
     }
     const context = createAppContext();
     const installedPlugins = new Set();
     let isMounted = false;
     const app: App = (context.app = {
     _uid: uid++,
     _component: rootComponent as ConcreteComponent,
     _props: rootProps,
     _container: null,
     _context: context,
     get config() {
     return context.config;
     },
     set config(v) {
     warn('app.config 不能被覆盖');
     },
     use(plugin: Plugin, ...options: any[]) {
     if (installedPlugins.has(plugin)) {
     warn(`插件已安装`);
     } else if (plugin && isFunction(plugin.install)) {
     installedPlugins.add(plugin);
     plugin.install(app, ...options);
     } else if (isFunction(plugin)) {
     installedPlugins.add(plugin);
     plugin(app, ...options);
     } else {
     warn('插件应该是一个函数或者一个有 install 方法的对象');
     }
     return app;
     },
     mixin(mixin: ComponentOptions) {
     if (!context.mixins.includes(mixin)) {
     context.mixins.push(mixin);
     if (mixin.props || mixin.emits) {
     context.deopt = true;
     }
     } else {
     warn('mixin 过了');
     }
     return app;
     },
     component(name: string, component?: Component): any {
     if (!component) {
     return context.components[name];
     }
     if (context.components[name]) {
     warn('组件已经注册过了');
     }
     context.components[name] = component;
     return app;
     },
     directive(name: string, directive?: Directive) {
     if (!directive) {
     return context.directives[name] as any;
     }
     if (context.directives[name]) {
     warn('指令已经注册过了');
     }
     context.directives[name] = directive;
     return app;
     },
     mount(rootContainer: HostElement): any {
     if (!isMounted) {
     const vnode = createVNode(
     rootComponent as ConcreteComponent,
     rootProps
     );
     // 在根节点上存储 app context,在第一次渲染的,会被设为根实例
     vnode.appContext = context;
     // 将 VNode 借助 patch 渲染到 container
     render(vnode, rootContainer);
     isMounted = true;
     app._container = rootContainer;
     return vnode.component!.proxy;
     }
     },
     unmount() {
     if (isMounted) {
     // 直接渲染 null???
     render(null, app._container);
     } else {
     warn(`没有挂载过,怎么卸载?`);
     }
     },
     provide(key, value) {
     if ((key as string | symbol) in context.provides) {
     warn('App 已经 provides 这个属性了,小心被覆盖');
     }
     context.provides[key as string] = value;
     return app;
     }
     });
     return app;
     };
    }
    function baseCreateRenderer(
     options: RendererOptions,
     createHydrationFns?: typeof createHydrationFunctions
    ): any {
     // ...
     const patch: PatchFn = (
     n1, // 旧的 VNode
     n2, // 新的 VNode
     container, // DOM 容器(挂载到的地方)
     anchor = null, // 锚点
     parentComponent = null,
     parentSuspense = null,
     isSVG = false,
     optimized = false
     ) => {
     // 存在旧节点且新旧节点不是同种类型,先卸载旧的
     if (n1 && !isSameVNodeType(n1, n2)) {
     anchor = getNextHostNode(n1);
     unmount(n1, parentComponent, parentSuspense, true);
     n1 = null;
     }
     if (n2.patchFlag === PatchFlags.BAIL) {
     optimized = false;
     n2.dynamicChildren = null;
     }
     const { type, ref, shapeFlag } = n2;
     switch (type) {
     case Text: // 处理文本节点
     processText(n1, n2, container, anchor);
     break;
     case Comment: // 处理注释节点
     processCommentNode(n1, n2, container, anchor);
     break;
     case Static: // 处理静态节点
     if (n1 == null) {
     mountStaticNode(n2, container, anchor, isSVG);
     } else {
     patchStaticNode(n1, n2, container, isSVG);
     }
     break;
     case Fragment: // 处理 Fragment
     processFragment(
     n1,
     n2,
     container,
     anchor,
     parentComponent,
     parentSuspense,
     isSVG,
     optimized
     );
     break;
     default:
     if (shapeFlag & ShapeFlags.ELEMENT) {
     // 处理元素
     processElement(
     n1,
     n2,
     container,
     anchor,
     parentComponent,
     parentSuspense,
     isSVG,
     optimized
     );
     } else if (shapeFlag & ShapeFlags.COMPONENT) {
     // 处理组件
     processComponent(
     n1,
     n2,
     container,
     anchor,
     parentComponent,
     parentSuspense,
     isSVG,
     optimized
     );
     } else if (shapeFlag & ShapeFlags.TELEPORT) {
     // 处理 teleport
     (type as typeof TeleportImpl).process(
     n1 as TeleportVNode,
     n2 as TeleportVNode,
     container,
     anchor,
     parentComponent,
     parentSuspense,
     isSVG,
     optimized,
     internals
     );
     } else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
     // 处理 Suspense
     (type as typeof SuspenseImpl).process(
     n1,
     n2,
     container,
     anchor,
     parentComponent,
     parentSuspense,
     isSVG,
     optimized,
     internals
     );
     }
     }
     // 更新 ref
     if (ref != null && parentComponent) {
     setRef(ref, n1 && n1.ref, parentSuspense, n2);
     }
     };
     // ...
     const render: RootRenderFunction = (vnode, container) => {
     if (vnode == null) {
     if (container._vnode) {
     unmount(container._vnode, null, null, true);
     }
     } else {
     patch(container._vnode || null, vnode, container);
     }
     flushPostFlushCbs();
     container._vnode = vnode;
     };
     // ...
    }
    /**
     * 元素挂载 patch-processElement-mountElement
     * mountElement(n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized)
     */
    const mountElement = (
     vnode: VNode,
     container: RendererElement,
     anchor: RendererNode | null,
     parentComponent: ComponentInternalInstance | null,
     parentSuspense: SuspenseBoundary | null,
     isSVG: boolean,
     optimized: boolean
    ) => {
     let el: RendererElement;
     let vnodeHook: VNodeHook | undefined | null;
     const { type, props, shapeFlag, transition, scopeId, patchFlag, dirs } =
     vnode;
     if (
     vnode.el &&
     hostCloneNode !== undefined &&
     patchFlag === PatchFlags.HOISTED
     ) {
     /**
     * 如果 VNode 有非空的 el,则意味着它正在重复使用
     * 只有静态节点可以重复使用,所以它挂载的 DOM 节点应该完全相同
     * 可以在这里进行简单的克隆
     */
     el = vnode.el = hostCloneNode(vnode.el);
     } else {
     el = vnode.el = hostCreateElement(
     vnode.type as string,
     isSVG,
     props && props.is
     );
     // 首先挂载子节点,因为一些 props 可能依赖子内容的渲染,例如:<select value>
     if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
     hostSetElementText(el, vnode.children as string);
     } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
     /**
     * 内部会遍历 children 并递归调用 patch 挂载 child
     * 调用 patch 而不是 mountElement 的原因是:
     * 子节点可能有其他类型的 VNode,比如普通组件
     */
     mountChildren(
     vnode.children as VNodeArrayChildren,
     el,
     null,
     parentComponent,
     parentSuspense,
     isSVG && type !== 'foreignObject',
     optimized || !!vnode.dynamicChildren
     );
     }
     // 触发节点上绑定的自定义指令的“created”钩子
     if (dirs) {
     invokeDirectiveHook(vnode, null, parentComponent, 'created');
     }
     // 处理 props,例如:class、style、event...
     if (props) {
     for (const key in props) {
     if (!isReservedProp(key)) {
     hostPatchProp(
     el,
     key,
     null,
     props[key],
     isSVG,
     vnode.children as VNode[],
     parentComponent,
     parentSuspense,
     unmountChildren
     );
     }
     }
     if ((vnodeHook = props.onVnodeBeforeMount)) {
     invokeVNodeHook(vnodeHook, parentComponent, vnode);
     }
     }
     setScopeId(el, scopeId, vnode, parentComponent);
     }
     // 触发节点上绑定的自定义指令的“beforeMount”钩子
     if (dirs) {
     invokeDirectiveHook(vnode, null, parentComponent, 'beforeMount');
     }
     // ...
     /**
     * 把创建的 DOM 挂载到 container 上
     * anchor ? parent.insertBefore(child, anchor) : parent.appendChild(child)
     */
     hostInsert(el, container, anchor);
     // ...
    };
    /**
     * 元素更新 patch-processElement-patchElement
     * patchElement(n1, n2, parentComponent, parentSuspense, isSVG, optimized)
     */
    const patchElement = (
     n1: VNode,
     n2: VNode,
     parentComponent: ComponentInternalInstance | null,
     parentSuspense: SuspenseBoundary | null,
     isSVG: boolean,
     optimized: boolean
    ) => {
     const el = (n2.el = n1.el!);
     let { patchFlag, dynamicChildren, dirs } = n2;
     patchFlag |= n1.patchFlag & PatchFlags.FULL_PROPS;
     const oldProps = n1.props || EMPTY_OBJ;
     const newProps = n2.props || EMPTY_OBJ;
     let vnodeHook: VNodeHook | undefined | null;
     if ((vnodeHook = newProps.onVnodeBeforeUpdate)) {
     invokeVNodeHook(vnodeHook, parentComponent, n2, n1);
     }
     if (dirs) {
     // 触发节点上绑定的自定义指令的“beforeUpdate”钩子
     invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate');
     }
     if (patchFlag > 0) {
     /**
     * patchFlag 的存在意味着该元素的 render 代码是由编译器生成的,可以走捷径
     * 这种情况下,新旧节点有相同的形状???
     */
     if (patchFlag & PatchFlags.FULL_PROPS) {
     // 元素绑定了动态 key,需要 diff
     patchProps(
     el,
     n2,
     oldProps,
     newProps,
     parentComponent,
     parentSuspense,
     isSVG
     );
     } else {
     // 元素绑定了动态 class
     if (patchFlag & PatchFlags.CLASS) {
     if (oldProps.class !== newProps.class) {
     hostPatchProp(el, 'class', null, newProps.class, isSVG);
     }
     }
     // 元素绑定了动态 style
     if (patchFlag & PatchFlags.STYLE) {
     hostPatchProp(el, 'style', oldProps.style, newProps.style, isSVG);
     }
     // 元素绑定了动态 prop/attr 而不是 class 和 style
     if (patchFlag & PatchFlags.PROPS) {
     const propsToUpdate = n2.dynamicProps!;
     for (let i = 0; i < propsToUpdate.length; i++) {
     const key = propsToUpdate[i];
     const prev = oldProps[key];
     const next = newProps[key];
     if (
     next !== prev ||
     (hostForcePatchProp && hostForcePatchProp(el, key))
     ) {
     hostPatchProp(
     el,
     key,
     prev,
     next,
     isSVG,
     n1.children as VNode[],
     parentComponent,
     parentSuspense,
     unmountChildren
     );
     }
     }
     }
     }
     // 当前元素仅有动态文本子元素
     if (patchFlag & PatchFlags.TEXT) {
     if (n1.children !== n2.children) {
     hostSetElementText(el, n2.children as string);
     }
     }
     } else if (!optimized && dynamicChildren == null) {
     // 没办法优化,全量 diff
     patchProps(
     el,
     n2,
     oldProps,
     newProps,
     parentComponent,
     parentSuspense,
     isSVG
     );
     }
     const areChildrenSVG = isSVG && n2.type !== 'foreignObject';
     if (dynamicChildren) {
     // 只需要对比 block 中的动态子节点
     patchBlockChildren(
     n1.dynamicChildren!,
     dynamicChildren,
     el,
     parentComponent,
     parentSuspense,
     areChildrenSVG
     );
     } else if (!optimized) {
     // 全量更新子节点
     patchChildren(
     n1,
     n2,
     el,
     null,
     parentComponent,
     parentSuspense,
     areChildrenSVG
     );
     }
     if ((vnodeHook = newProps.onVnodeUpdated) || dirs) {
     queuePostRenderEffect(() => {
     vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, n2, n1);
     dirs && invokeDirectiveHook(n2, n1, parentComponent, 'updated');
     }, parentSuspense);
     }
    };
    /**
     * 更新子节点 patch-processElement-patchElement-patchChildren
     * patchChildren(n1, n2, el, null, parentComponent, parentSuspense, areChildrenSVG)
     */
    const patchChildren: PatchChildrenFn = (
     n1,
     n2,
     container,
     anchor,
     parentComponent,
     parentSuspense,
     isSVG,
     optimized = false
    ) => {
     const c1 = n1 && n1.children;
     const prevShapeFlag = n1 ? n1.shapeFlag : 0;
     const c2 = n2.children;
     const { patchFlag, shapeFlag } = n2;
     if (patchFlag > 0) {
     if (patchFlag & PatchFlags.KEYED_FRAGMENT) {
     patchKeyedChildren(
     c1 as VNode[],
     c2 as VNodeArrayChildren,
     container,
     anchor,
     parentComponent,
     parentSuspense,
     isSVG,
     optimized
     );
     return;
     } else if (patchFlag & PatchFlags.UNKEYED_FRAGMENT) {
     patchUnkeyedChildren(
     c1 as VNode[],
     c2 as VNodeArrayChildren,
     container,
     anchor,
     parentComponent,
     parentSuspense,
     isSVG,
     optimized
     );
     return;
     }
     }
     // 子节点有 3 种可能情况:文本、数组、空
     if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
     // 数组 -> 文本,则删除之前的子节点
     if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {
     unmountChildren(c1 as VNode[], parentComponent, parentSuspense);
     }
     if (c2 !== c1) {
     // 文本对比不同,则替换为新文本
     hostSetElementText(container, c2 as string);
     }
     } else {
     if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {
     // 之前的子节点是数组
     if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
     // 新的子节点仍然是数组,则做完整地 diff
     patchKeyedChildren(
     c1 as VNode[],
     c2 as VNodeArrayChildren,
     container,
     anchor,
     parentComponent,
     parentSuspense,
     isSVG,
     optimized
     );
     } else {
     // 数组 -> 空,则仅仅删除之前的子节点
     unmountChildren(c1 as VNode[], parentComponent, parentSuspense, true);
     }
     } else {
     /**
     * 之前的子节点是文本节点或者为空
     * 新的子节点是数组或者为空
     */
     if (prevShapeFlag & ShapeFlags.TEXT_CHILDREN) {
     // 如果之前子节点是文本,则把它清空
     hostSetElementText(container, '');
     }
     // 如果新的子节点是数组,则挂载新子节点
     if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
     mountChildren(
     c2 as VNodeArrayChildren,
     container,
     anchor,
     parentComponent,
     parentSuspense,
     isSVG,
     optimized
     );
     }
     }
     }
    };
    // 完整 diff patch-processElement-patchElement-patchChildren-patchKeyedChildren
    const patchKeyedChildren = (
     c1: VNode[],
     c2: VNodeArrayChildren,
     container: RendererElement,
     parentAnchor: RendererNode | null,
     parentComponent: ComponentInternalInstance | null,
     parentSuspense: SuspenseBoundary | null,
     isSVG: boolean,
     optimized: boolean
    ) => {
     const l2 = c2.length;
     let i = 0; // 头部索引
     let e1 = c1.length - 1; // 旧子节点列表尾部索引
     let e2 = l2 - 1; // 新子节点列表尾部索引
     /**
     * 1. 同步头部节点(从头部开始,依次对比新旧节点)
     * (a b) c
     * (a b) d e
     */
     while (i <= e1 && i <= e2) {
     const n1 = c1[i];
     const n2 = (c2[i] = optimized
     ? cloneIfMounted(c2[i] as VNode)
     : normalizeVNode(c2[i]));
     // n1.type === n2.type && n1.key === n2.key
     if (isSameVNodeType(n1, n2)) {
     // 相同节点,递归执行 patch 更新节点
     patch(
     n1,
     n2,
     container,
     null,
     parentComponent,
     parentSuspense,
     isSVG,
     optimized
     );
     } else {
     break;
     }
     i++;
     }
     /**
     * 2. 同步尾部节点(从尾部开始,依次对比新旧节点)
     * a (b c)
     * d e (b c)
     */
     while (i <= e1 && i <= e2) {
     const n1 = c1[e1];
     const n2 = (c2[e2] = optimized
     ? cloneIfMounted(c2[e2] as VNode)
     : normalizeVNode(c2[e2]));
     if (isSameVNodeType(n1, n2)) {
     patch(
     n1,
     n2,
     container,
     null,
     parentComponent,
     parentSuspense,
     isSVG,
     optimized
     );
     } else {
     break;
     }
     e1--;
     e2--;
     }
     /**
     * 经过“同步头部节点”和“同步尾部节点”,只剩下三种情况要处理:
     * 1. 新节点有剩余,要添加剩余节点
     * 2. 旧节点有剩余,要删除多余节点
     * 3. 未知子序列
     */
     if (i > e1) {
     if (i <= e2) {
     /**
     * 3. “新节点有剩余,要添加剩余节点”
     * (a b)
     * (a b) c
     * i = 2, e1 = 1, e2 = 2
     * (a b)
     * c (a b)
     * i = 0, e1 = -1, e2 = 0
     */
     const nextPos = e2 + 1;
     const anchor = nextPos < l2 ? (c2[nextPos] as VNode).el : parentAnchor;
     while (i <= e2) {
     patch(
     null,
     (c2[i] = optimized
     ? cloneIfMounted(c2[i] as VNode)
     : normalizeVNode(c2[i])),
     container,
     anchor,
     parentComponent,
     parentSuspense,
     isSVG
     );
     i++;
     }
     }
     } else if (i > e2) {
     /**
     * 4. “旧节点有剩余,要删除多余节点”
     * (a b) c
     * (a b)
     * i = 2, e1 = 2, e2 = 1
     * a (b c)
     * (b c)
     * i = 0, e1 = 0, e2 = -1
     */
     while (i <= e1) {
     unmount(c1[i], parentComponent, parentSuspense, true);
     i++;
     }
     } else {
     /**
     * 5. “未知子序列,可能需要移动”
     * [i ... e1 + 1]: a b [c d e] f g
     * [i ... e2 + 1]: a b [e d c h] f g
     * i = 2, e1 = 4, e2 = 5
     */
     const s1 = i; // 旧子节点列表起始索引
     const s2 = i; // 新子节点列表起始索引
     // 5.1 根据 key 为新子节点构建索引图 {key: index}(空间换时间,O(n^2) 降到 O(n))
     const keyToNewIndexMap: Map<string | number, number> = new Map();
     for (i = s2; i <= e2; i++) {
     const nextChild = (c2[i] = optimized
     ? cloneIfMounted(c2[i] as VNode)
     : normalizeVNode(c2[i]));
     if (nextChild.key != null) {
     keyToNewIndexMap.set(nextChild.key, i);
     }
     }
     // 5.2 循环剩下的旧子节点,更新匹配的节点,删除不再存在的节点
     let j;
     let patched = 0;
     const toBePatched = e2 - s2 + 1;
     let moved = false;
     // 用来追踪是否有节点需要移动
     let maxNewIndexSoFar = 0;
     // 储存新子序列节点的索引和旧子序列节点的索引之间的映射关系,并确定是否有移动
     const newIndexToOldIndexMap = new Array(toBePatched);
     for (i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0;
     for (i = s1; i <= e1; i++) {
     const prevChild = c1[i];
     if (patched >= toBePatched) {
     // 所有新的子节点都更新过了,所以剩下的删除掉就好
     unmount(prevChild, parentComponent, parentSuspense, true);
     continue;
     }
     let newIndex; // 得到旧节点在的新位置
     if (prevChild.key != null) {
     newIndex = keyToNewIndexMap.get(prevChild.key);
     } else {
     for (j = s2; j <= e2; j++) {
     if (
     newIndexToOldIndexMap[j - s2] === 0 &&
     isSameVNodeType(prevChild, c2[j] as VNode)
     ) {
     newIndex = j;
     break;
     }
     }
     }
     if (newIndex === undefined) {
     // 旧节点没用(在新节点里没有容身之处)
     unmount(prevChild, parentComponent, parentSuspense, true);
     } else {
     newIndexToOldIndexMap[newIndex - s2] = i + 1;
     if (newIndex >= maxNewIndexSoFar) {
     maxNewIndexSoFar = newIndex;
     } else {
     moved = true;
     }
     patch(
     prevChild,
     c2[newIndex] as VNode,
     container,
     null,
     parentComponent,
     parentSuspense,
     isSVG,
     optimized
     );
     patched++;
     }
     }
     // 5.3 移动和挂载,仅当节点已移动时才生成最长稳定递增子序列
     const increasingNewIndexSequence = moved
     ? getSequence(newIndexToOldIndexMap)
     : EMPTY_ARR;
     j = increasingNewIndexSequence.length - 1;
     // 向后循环,以便我们可以使用最后一个修补的节点作为锚点
     for (i = toBePatched - 1; i >= 0; i--) {
     const nextIndex = s2 + i;
     const nextChild = c2[nextIndex] as VNode;
     const anchor =
     nextIndex + 1 < l2 ? (c2[nextIndex + 1] as VNode).el : parentAnchor;
     if (newIndexToOldIndexMap[i] === 0) {
     patch(
     null,
     nextChild,
     container,
     anchor,
     parentComponent,
     parentSuspense,
     isSVG
     );
     } else if (moved) {
     // 没有最长递增子序列(reverse 场景)或者当前的节点索引不在最长递增子序列中,需要移动
     if (j < 0 || i !== increasingNewIndexSequence[j]) {
     move(nextChild, container, anchor, MoveType.REORDER);
     } else {
     j--;
     }
     }
     }
     }
    };
    const patchUnkeyedChildren = (
     c1: VNode[],
     c2: VNodeArrayChildren,
     container: RendererElement,
     anchor: RendererNode | null,
     parentComponent: ComponentInternalInstance | null,
     parentSuspense: SuspenseBoundary | null,
     isSVG: boolean,
     optimized: boolean
    ) => {
     c1 = c1 || EMPTY_ARR;
     c2 = c2 || EMPTY_ARR;
     const oldLength = c1.length;
     const newLength = c2.length;
     const commonLength = Math.min(oldLength, newLength);
     let i;
     for (i = 0; i < commonLength; i++) {
     const nextChild = (c2[i] = optimized
     ? cloneIfMounted(c2[i] as VNode)
     : normalizeVNode(c2[i]));
     patch(
     c1[i],
     nextChild,
     container,
     null,
     parentComponent,
     parentSuspense,
     isSVG,
     optimized
     );
     }
     if (oldLength > newLength) {
     // 移除旧的
     unmountChildren(
     c1,
     parentComponent,
     parentSuspense,
     true,
     false,
     commonLength
     );
     } else {
     // 挂载新的
     mountChildren(
     c2,
     container,
     anchor,
     parentComponent,
     parentSuspense,
     isSVG,
     optimized,
     commonLength
     );
     }
    };
    /**
     * 组件挂载 patch-processComponent-mountComponent
     * mountComponent(n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized)
     * 1. 创建组件实例
     * 2. 设置组件实例
     * 3. 设置组件渲染副作用
     */
    const mountComponent: MountComponentFn = (
     initialVNode,
     container,
     anchor,
     parentComponent,
     parentSuspense,
     isSVG,
     optimized
    ) => {
     // 创建组件实例
     const instance: ComponentInternalInstance = (initialVNode.component =
     createComponentInstance(initialVNode, parentComponent, parentSuspense));
     // 为 KeepAlive 注入渲染器内部
     if (isKeepAlive(initialVNode)) {
     (instance.ctx as KeepAliveContext).renderer = internals;
     }
     setupComponent(instance);
     // setup() 是异步的,组件依赖待异步逻辑
     setupRenderEffect(
     instance,
     initialVNode,
     container,
     anchor,
     parentSuspense,
     isSVG,
     optimized
     );
    };
    export function createComponentInstance(
     vnode: VNode,
     parent: ComponentInternalInstance | null,
     suspense: SuspenseBoundary | null
    ) {
     const type = vnode.type as ConcreteComponent;
     // 继承父组件的 appContext;如果是根组件,则直接从根 vnode 中取
     const appContext =
     (parent ? parent.appContext : vnode.appContext) || emptyAppContext;
     // 字面量的方式不同于 2.x 中的 new Vue
     const instance: ComponentInternalInstance = {
     uid: uid++, // 组件唯一 id
     vnode, // 组件 vnode
     type, // vnode 节点类型
     parent, // 父组件实例
     appContext, // 上下文
     root: null!, // // 根组件实例,会被立即赋值
     next: null, // 新的组件 vnode
     subTree: null!, // 子节点/子树,创建后将同步设置
     update: null!, // 带副作用更新函数,创建后将同步设置
     render: null, // 渲染函数
     proxy: null, // 渲染上下文代理
     exposed: null,
     withProxy: null, // 带有 with 的渲染上下文代理
     effects: null, // 响应式相关对象
     provides: parent ? parent.provides : Object.create(appContext.provides), // 从父级继承 provides
     accessCache: null!, // 渲染代理的属性访问缓存
     renderCache: [], // 渲染缓存
     components: null, // 注册的组件
     directives: null, // 注册的指令
     propsOptions: normalizePropsOptions(type, appContext),
     emitsOptions: normalizeEmitsOptions(type, appContext),
     emit: null as any, // 会被立即赋值
     emitted: null,
     // 组件 state 相关
     ctx: EMPTY_OBJ,
     data: EMPTY_OBJ,
     props: EMPTY_OBJ,
     attrs: EMPTY_OBJ,
     slots: EMPTY_OBJ,
     refs: EMPTY_OBJ,
     setupState: EMPTY_OBJ, // setup 返回的响应式状态
     setupContext: null, // setup 上下文数据
     // 组件 suspense 相关
     suspense,
     suspenseId: suspense ? suspense.pendingId : 0,
     asyncDep: null, // suspense 异步依赖
     asyncResolved: false, // suspense 异步依赖是否都已处理
     // 生命周期钩子,不用枚举类型是因为会导致计算属性???
     isMounted: false,
     isUnmounted: false,
     isDeactivated: false,
     bc: null,
     c: null,
     bm: null,
     m: null,
     bu: null,
     u: null,
     um: null,
     bum: null,
     da: null,
     a: null,
     rtg: null,
     rtc: null,
     ec: null
     };
     instance.ctx = { _: instance }; // 实例上下文
     instance.root = parent ? parent.root : instance; // 指向根组件的指针
     instance.emit = emit.bind(null, instance); // emit 方法
     return instance;
    }
    export function setupComponent(
     instance: ComponentInternalInstance,
     isSSR = false
    ) {
     isInSSRComponentSetup = isSSR;
     const { props, children, shapeFlag } = instance.vnode;
     // 是否是一个有状态的组件
     const isStateful = shapeFlag & ShapeFlags.STATEFUL_COMPONENT;
     // 初始化 props/attrs
     initProps(instance, props, isStateful, isSSR);
     // 初始化 slots
     initSlots(instance, children);
     // 获取 setup 返回值
     const setupResult = isStateful
     ? setupStatefulComponent(instance, isSSR)
     : undefined;
     isInSSRComponentSetup = false;
     return setupResult;
    }
    function setupStatefulComponent(
     instance: ComponentInternalInstance,
     isSSR: boolean
    ) {
     const Component = instance.type as ComponentOptions;
     // 创建渲染代理的属性访问缓存
     instance.accessCache = Object.create(null);
     // 创建公共实例/渲染代理
     instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers);
     const { setup } = Component;
     if (setup) {
     // 创建 setup 上下文
     const setupContext = (instance.setupContext =
     // {attrs、slots、emit、expose}
     setup.length > 1 ? createSetupContext(instance) : null);
     currentInstance = instance;
     pauseTracking();
     // 调用 setup
     const setupResult = callWithErrorHandling(
     setup,
     instance,
     ErrorCodes.SETUP_FUNCTION,
     [instance.props, setupContext]
     );
     resetTracking();
     currentInstance = null;
     // 处理 setup 结果
     if (isPromise(setupResult)) {
     // ...
     } else {
     /**
     * 如果 setupResult 是 VNode,报错
     * 如果 setupResult 是函数,instance.render = setupResult
     * 如果 setupResult 是对象,instance.setupState = proxyRefs(setupResult)
     */
     handleSetupResult(instance, setupResult, isSSR);
     }
     } else {
     /**
     * 设置 instance.render
     * 创建 instance.withProxy
     * 兼容 OptionsAPI
     */
     finishComponentSetup(instance, isSSR);
     }
    }
    /**
     * patch-processComponent-mountComponent-setupRenderEffect
     * 初次渲染组件的时候,会在组件实例上创建一个渲染副作用(包含了挂载副作用和更新副作用)
     * 在 updateComponent 时会调用 instance.update
     * setupRenderEffect(instance, initialVNode, container, anchor, parentSuspense, isSVG, optimized)
     */
    const setupRenderEffect: SetupRenderEffectFn = (
     instance,
     initialVNode,
     container,
     anchor,
     parentSuspense,
     isSVG,
     optimized
    ) => {
     instance.update = effect(
     // 组件 effect 的回调函数
     function componentEffect() {
     if (!instance.isMounted) {
     let vnodeHook: VNodeHook | null | undefined;
     const { el, props } = initialVNode;
     const { bm, m, parent } = instance;
     if (bm) {
     invokeArrayFns(bm); // 触发 beforeMount
     }
     if ((vnodeHook = props && props.onVnodeBeforeMount)) {
     invokeVNodeHook(vnodeHook, parent, initialVNode);
     }
     /**
     * 渲染组件生成的子树
     * subTree(_vnode):组件内部整个 DOM 节点对应的 VNode;
     * initialVNode($vnode):组件 VNode。
     * renderComponentRoot 执行组件 render 函数创建整个组件树内部的 VNode;
     * 把这个 VNode 再经过内部一层标准化,就得到了该函数的返回结果:subTree。
     */
     const subTree = (instance.subTree = renderComponentRoot(instance));
     if (el && hydrateNode) {
     hydrateNode(
     initialVNode.el as Node,
     subTree,
     instance,
     parentSuspense
     );
     } else {
     // 把 subTree 挂载到 container 中
     patch(
     null,
     subTree,
     container,
     anchor,
     instance,
     parentSuspense,
     isSVG
     );
     initialVNode.el = subTree.el;
     }
     if (m) {
     queuePostRenderEffect(m, parentSuspense); // 触发 mounted
     }
     if ((vnodeHook = props && props.onVnodeMounted)) {
     const scopedInitialVNode = initialVNode;
     queuePostRenderEffect(() => {
     invokeVNodeHook(vnodeHook!, parent, scopedInitialVNode);
     }, parentSuspense);
     }
     const { a } = instance;
     if (
     a &&
     initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
     ) {
     queuePostRenderEffect(a, parentSuspense);
     }
     instance.isMounted = true;
     initialVNode = container = anchor = null as any;
     } else {
     // 更新组件,组件自身状态的改变触发或者父组件调用 processComponent
     let { next, bu, u, parent, vnode } = instance;
     let originNext = next;
     let vnodeHook: VNodeHook | null | undefined;
     if (next) {
     // next 表示组件新的 VNode
     next.el = vnode.el;
     // 更新组件 vnode 节点信息
     updateComponentPreRender(instance, next, optimized);
     } else {
     next = vnode;
     }
     if (bu) {
     invokeArrayFns(bu); // 触发 beforeUpdate
     }
     if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {
     invokeVNodeHook(vnodeHook, parent, next, vnode);
     }
     const nextTree = renderComponentRoot(instance); // 渲染新的子树 VNode
     const prevTree = instance.subTree; // 缓存旧的子树 VNode
     instance.subTree = nextTree; // 根据新旧子树 VNode 做 patch
     // 核心逻辑,根据新旧子树 vnode 做 patch
     patch(
     prevTree,
     nextTree,
     // 如果在 Teleport 中,父级可能改变
     hostParentNode(prevTree.el!)!,
     // 如果在 Fragment 中,锚点可能改变
     getNextHostNode(prevTree),
     instance,
     parentSuspense,
     isSVG
     );
     next.el = nextTree.el;
     if (originNext === null) {
     updateHOCHostEl(instance, nextTree.el);
     }
     if (u) {
     queuePostRenderEffect(u, parentSuspense); // 触发 updated
     }
     // onVnodeUpdated
     if ((vnodeHook = next.props && next.props.onVnodeUpdated)) {
     queuePostRenderEffect(() => {
     invokeVNodeHook(vnodeHook!, parent, next!, vnode);
     }, parentSuspense);
     }
     }
     },
     {
     // 递归的原因:在子组件修改(在非生命周期内)注入(inject)的 ref 值,没有响应式
     allowRecurse: true,
     scheduler: queueJob
     }
     );
    };
    /**
     * 组件更新 patch-processComponent-updateComponent
     * updateComponent(n1, n2, optimized)
     */
    const updateComponent = (n1: VNode, n2: VNode, optimized: boolean) => {
     const instance = (n2.component = n1.component)!;
     // 是否应该更新
     if (shouldUpdateComponent(n1, n2, optimized)) {
     if (instance.asyncDep && !instance.asyncResolved) {
     updateComponentPreRender(instance, n2, optimized);
     return;
     } else {
     instance.next = n2;
     // 子组件也可能因为数据变化被添加到更新队列里了,移除它们防止对一个子组件重复更新
     invalidateJob(instance.update);
     // instance.update 是副作用 runner
     instance.update();
     }
     } else {
     // 没啥需要更新,把属性拷贝过去
     n2.component = n1.component;
     n2.el = n1.el;
     instance.vnode = n2;
     }
    };
    // patch-processComponent-updateComponent-updateComponentPreRender
    const updateComponentPreRender = (
     instance: ComponentInternalInstance,
     nextVNode: VNode,
     optimized: boolean
    ) => {
     // 新组件 vnode 的 component 属性指向组件实例
     nextVNode.component = instance;
     // 旧组件 vnode 的 props 属性
     const prevProps = instance.vnode.props;
     // 组件实例的 vnode 属性指向新的组件 vnode
     instance.vnode = nextVNode;
     // 清空 next 属性,为了下一次重新渲染准备
     instance.next = null;
     // 更新 props
     updateProps(instance, nextVNode.props, prevProps, optimized);
     // 更新插槽
     updateSlots(instance, nextVNode.children);
     flushPreFlushCbs(undefined, instance.update);
    };
    // 渲染代理 proxy 配置
    export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
     get({ _: instance }: ComponentRenderContext, key: string) {
     const { ctx, setupState, data, props, accessCache, type, appContext } =
     instance;
     // @vue/reactivity 永远不应该观察 Vue 公共实例
     if (key === ReactiveFlags.SKIP) {
     return true;
     }
     // 在渲染期间,对访问渲染上下文的任何属性的访问都会调用 getter
     let normalizedProps;
     // 如果 key 不以 $ 开头
     if (key[0] !== '$') {
     const n = accessCache![key]; // 缓存
     if (n !== undefined) {
     // setupState >> data >> ctx >> props
     switch (n) {
     case AccessTypes.SETUP:
     // setupState 是 setup 返回的数据
     return setupState[key];
     case AccessTypes.DATA:
     return data[key];
     case AccessTypes.CONTEXT:
     return ctx[key];
     case AccessTypes.PROPS:
     return props![key];
     }
     } else if (setupState !== EMPTY_OBJ && hasOwn(setupState, key)) {
     accessCache![key] = AccessTypes.SETUP;
     return setupState[key];
     } else if (data !== EMPTY_OBJ && hasOwn(data, key)) {
     accessCache![key] = AccessTypes.DATA;
     return data[key];
     } else if (
     (normalizedProps = instance.propsOptions[0]) &&
     hasOwn(normalizedProps, key)
     ) {
     accessCache![key] = AccessTypes.PROPS;
     return props![key];
     } else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) {
     accessCache![key] = AccessTypes.CONTEXT;
     return ctx[key];
     } else if (!__FEATURE_OPTIONS_API__ || !isInBeforeCreate) {
     accessCache![key] = AccessTypes.OTHER;
     }
     }
     const publicGetter = publicPropertiesMap[key];
     let cssModule, globalProperties;
     // $xxx 属性
     if (publicGetter) {
     if (key === '$attrs') {
     track(instance, TrackOpTypes.GET, key);
     }
     return publicGetter(instance);
     } else if (
     (cssModule = type.__cssModules) &&
     (cssModule = cssModule[key])
     ) {
     return cssModule;
     } else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) {
     // 用户可能会在 this 上挂 $自定义属性
     accessCache![key] = AccessTypes.CONTEXT;
     return ctx[key];
     } else if (
     // 全局属性
     ((globalProperties = appContext.config.globalProperties),
     hasOwn(globalProperties, key))
     ) {
     return globalProperties[key];
     }
     },
     set(
     { _: instance }: ComponentRenderContext,
     key: string,
     value: any
     ): boolean {
     const { data, setupState, ctx } = instance;
     if (setupState !== EMPTY_OBJ && hasOwn(setupState, key)) {
     setupState[key] = value;
     } else if (data !== EMPTY_OBJ && hasOwn(data, key)) {
     data[key] = value;
     }
     if (key[0] === '$' && key.slice(1) in instance) {
     return false;
     } else {
     ctx[key] = value;
     }
     return true;
     },
     has(
     {
     _: { data, setupState, accessCache, ctx, appContext, propsOptions }
     }: ComponentRenderContext,
     key: string
     ) {
     let normalizedProps;
     return (
     accessCache![key] !== undefined ||
     (data !== EMPTY_OBJ && hasOwn(data, key)) ||
     (setupState !== EMPTY_OBJ && hasOwn(setupState, key)) ||
     ((normalizedProps = propsOptions[0]) && hasOwn(normalizedProps, key)) ||
     hasOwn(ctx, key) ||
     hasOwn(publicPropertiesMap, key) ||
     hasOwn(appContext.config.globalProperties, key)
     );
     }
    };
    

    keep-alive 2.x

     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    
    // 获取组件 name
    function getComponentName(opts: ?VNodeComponentOptions): ?string {
     return opts && (opts.Ctor.options.name || opts.tag);
    }
    // name 是否匹配 pattern
    function matches(pattern, name) {
     if (Array.isArray(pattern)) {
     return pattern.indexOf(name) > -1;
     } else if (typeof pattern === 'string') {
     return pattern.split(',').indexOf(name) > -1;
     } else if (isRegExp(pattern)) {
     return pattern.test(name);
     }
     return false;
    }
    type CacheEntry = {
     componentInstance: Component;
     name: ?string;
     tag: ?string;
    };
    type CacheEntryMap = { [key: string]: ?CacheEntry };
    // 清理缓存
    function pruneCache(keepAliveInstance: any, filter: Function) {
     const { cache, keys, _vnode } = keepAliveInstance;
     for (const key in cache) {
     const entry = cache[key];
     if (entry) {
     const { name } = entry;
     if (name && !filter(name)) {
     pruneCacheEntry(cache, key, keys, _vnode);
     }
     }
     }
    }
    // 清理缓存入口
    function pruneCacheEntry(
     cache: CacheEntryMap,
     key: string,
     keys: Array<string>,
     current?: VNode
    ) {
     const entry = cache[key];
     if (entry && (!current || entry.tag !== current.tag)) {
     entry.componentInstance.$destroy();
     }
     cache[key] = null;
     remove(keys, key);
    }
    const patternTypes: Array<Function> = [String, RegExp, Array];
    // <keep-alive :include="xxx" :exclude="yyy" :max="zzz">
    export default {
     name: 'keep-alive',
     abstract: true, // 抽象组件(在组件实例建立父子关系的时候会被忽略)
     props: {
     include: patternTypes,
     exclude: patternTypes,
     max: [String, Number]
     },
     methods: {
     // 仅在 render 执行过后才执行(mounted/updated)
     cacheVNode() {
     const { cache, keys, vnodeToCache, keyToCache } = this;
     if (vnodeToCache) {
     const { tag, componentInstance, componentOptions } = vnodeToCache;
     // 将 vnode 中的 名称、tag、实例缓存起来
     cache[keyToCache] = {
     name: getComponentName(componentOptions),
     tag,
     componentInstance
     };
     keys.push(keyToCache);
     // 清理老旧的缓存
     if (this.max && keys.length > parseInt(this.max)) {
     pruneCacheEntry(cache, keys[0], keys, this._vnode);
     }
     this.vnodeToCache = null;
     }
     }
     },
     created() {
     this.cache = Object.create(null); // 缓存 VNode
     this.keys = []; // 缓存中的 key
     },
     destroyed() {
     // 清空缓存
     for (const key in this.cache) {
     /**
     * 找到要删除的组件实例并调用 destory 钩子
     * 从缓存中删除 key 对应的缓存
     * 从 keys 中删除 key
     */
     pruneCacheEntry(this.cache, key, this.keys);
     }
     },
     mounted() {
     this.cacheVNode();
     // 监听黑白名单
     this.$watch('include', val => {
     pruneCache(this, name => matches(val, name));
     });
     this.$watch('exclude', val => {
     pruneCache(this, name => !matches(val, name));
     });
     },
     updated() {
     this.cacheVNode();
     },
     render() {
     const slot = this.$slots.default;
     /**
     * 处理默认插槽的第一个子组件
     * 所以常和 component 和 router-view 一起使用
     */
     const vnode: VNode = getFirstComponentChild(slot);
     const componentOptions = vnode && vnode.componentOptions;
     if (componentOptions) {
     // 获取 VNode 对应的组件名称
     const name: ?string = getComponentName(componentOptions);
     const { include, exclude } = this;
     // 组件名称不在白名单(include)内或者在黑名单(exclude)内
     if (
     (include && (!name || !matches(include, name))) ||
     (exclude && name && matches(exclude, name))
     ) {
     return vnode; // 直接返回
     }
     const { cache, keys } = this;
     // 想办法拼凑一个独一无二的 key
     const key =
     vnode.key == null
     ? componentOptions.Ctor.cid +
     (componentOptions.tag ? `::${componentOptions.tag}` : '')
     : vnode.key;
     /**
     * 如果命中缓存,VNode 的组件实例从缓存里取,并调整 key 的顺序(放在最后一个)
     * 调整 key 的原因是,keys[0] 对应的 key 一定是很久没用的缓存对应的 key
     */
     if (cache[key]) {
     vnode.componentInstance = cache[key].componentInstance;
     remove(keys, key);
     keys.push(key);
     } else {
     // 没有缓存的话,设置缓存
     this.vnodeToCache = vnode;
     this.keyToCache = key;
     }
     /**
     * 对于 keepAlive 标识的组件,被缓存组件初次渲染时会走正常流程(因为 VNode.componentInstance 为空)
     * 当组件切换,命中缓存时;被缓存组件实例存在,会直接返回;不会执行 created、mounted 等钩子,而是执行 activated
     * 对应的也不会执行 destory,而是会执行 deactivate 钩子
     */
     vnode.data.keepAlive = true;
     }
     // 通过插槽渲染组件
     return vnode || (slot && slot[0]);
     }
    };
    

    keep-alive 3.x

    实现 keep-alive 的关键 API 是 _KeepAlive;其缓存原则是“如果经常被访问,则能够快速命中;而不常被访问,则在不满足条件的情况下被释放”。

     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    
    type Cache = Map<CacheKey, VNode>; // 缓存结构
    type Keys = Set<CacheKey>; // 保存缓存 key 值
    const KeepAliveImpl = {
     name: `KeepAlive`,
     // 用于渲染器内部特殊处理的标记
     __isKeepAlive: true,
     inheritRef: true,
     props: {
     include: [String, RegExp, Array],
     exclude: [String, RegExp, Array],
     max: [String, Number]
     },
     setup(props: KeepAliveProps, { slots }: SetupContext) {
     const instance = getCurrentInstance()!;
     const parentSuspense = instance.suspense;
     const keys: Keys = new Set();
     const cache: Cache = new Map();
     let current: VNode | null = null;
     /**
     * KeepAlive 通过在其内部传递 ctx 与实例化的渲染器通信
     * KeepAlive 实例公开 activate/deactivate 的实现
     * 这么做的目的就是避免在 renderer 直接引入 KeepAlive
     * 实现 tree-shaking
     */
     const sharedContext = instance.ctx as KeepAliveContext;
     const {
     renderer: {
     p: patch,
     m: move,
     um: _unmount,
     o: { createElement }
     }
     } = sharedContext;
     const storageContainer = createElement('div');
     sharedContext.activate = (vnode, container, anchor, isSVG, optimized) => {
     const instance = vnode.component!;
     move(vnode, container, anchor, MoveType.ENTER, parentSuspense);
     // 万一 props 变了
     patch(
     instance.vnode,
     vnode,
     container,
     anchor,
     instance,
     parentSuspense,
     isSVG,
     optimized
     );
     queuePostRenderEffect(() => {
     instance.isDeactivated = false;
     if (instance.a) {
     invokeArrayFns(instance.a);
     }
     const vnodeHook = vnode.props && vnode.props.onVnodeMounted;
     if (vnodeHook) {
     invokeVNodeHook(vnodeHook, instance.parent, vnode);
     }
     }, parentSuspense);
     };
     sharedContext.deactivate = (vnode: VNode) => {
     const instance = vnode.component!;
     // 仅仅移除 DOM,没有卸载
     move(vnode, storageContainer, null, MoveType.LEAVE, parentSuspense);
     queuePostRenderEffect(() => {
     if (instance.da) {
     invokeArrayFns(instance.da);
     }
     const vnodeHook = vnode.props && vnode.props.onVnodeUnmounted;
     if (vnodeHook) {
     invokeVNodeHook(vnodeHook, instance.parent, vnode);
     }
     instance.isDeactivated = true;
     }, parentSuspense);
     };
     function unmount(vnode: VNode) {
     resetShapeFlag(vnode);
     _unmount(vnode, instance, parentSuspense);
     }
     function pruneCache(filter?: (name: string) => boolean) {
     cache.forEach((vnode, key) => {
     const name = getComponentName(vnode.type as ConcreteComponent);
     if (name && (!filter || !filter(name))) {
     pruneCacheEntry(key);
     }
     });
     }
     function pruneCacheEntry(key: CacheKey) {
     const cached = cache.get(key) as VNode;
     if (!current || cached.type !== current.type) {
     unmount(cached);
     } else if (current) {
     // 当前已激活的实例不再 kept-alive
     resetShapeFlag(current);
     }
     cache.delete(key);
     keys.delete(key);
     }
     // prop 变化,对缓存进行修剪
     watch(
     () => [props.include, props.exclude],
     ([include, exclude]) => {
     include && pruneCache(name => matches(include, name));
     exclude && pruneCache(name => !matches(exclude, name));
     },
     { flush: 'post', deep: true }
     );
     let pendingCacheKey: CacheKey | null = null;
     const cacheSubtree = () => {
     if (pendingCacheKey != null) {
     cache.set(pendingCacheKey, getInnerChild(instance.subTree));
     }
     };
     // 渲染/更新完成之后,缓存子树
     onMounted(cacheSubtree);
     onUpdated(cacheSubtree);
     onBeforeUnmount(() => {
     cache.forEach(cached => {
     const { subTree, suspense } = instance;
     const vnode = getInnerChild(subTree);
     if (cached.type === vnode.type) {
     resetShapeFlag(vnode);
     // 触发 deactivated 钩子
     const da = vnode.component!.da;
     da && queuePostRenderEffect(da, suspense);
     return;
     }
     unmount(cached);
     });
     });
     // KeepAlive 的渲染函数
     return () => {
     pendingCacheKey = null;
     if (!slots.default) {
     return null;
     }
     // 拿到 KeepAlive 包裹的子组件
     const children = slots.default();
     // KeepAlive 是抽象组件,它本身不渲染成实体节点,而是渲染它的第一个子节点
     const rawVNode = children[0];
     if (children.length > 1) {
     console.warn('KeepAlive 应该只包含一个子组件');
     current = null;
     return children;
     } else if (
     !isVNode(rawVNode) ||
     (!(rawVNode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) &&
     !(rawVNode.shapeFlag & ShapeFlags.SUSPENSE))
     ) {
     current = null;
     return rawVNode;
     }
     let vnode = getInnerChild(rawVNode);
     const comp = vnode.type as ConcreteComponent;
     const name = getComponentName(comp);
     // 缓存控制
     const { include, exclude, max } = props;
     if (
     (include && (!name || !matches(include, name))) ||
     (exclude && name && matches(exclude, name))
     ) {
     // 直接返回
     current = vnode;
     return rawVNode;
     }
     const key = vnode.key == null ? comp : vnode.key;
     // 获取缓存
     const cachedVNode = cache.get(key);
     // 如果重复使用 VNode,则克隆它;因为我们将对其进行修改
     if (vnode.el) {
     vnode = cloneVNode(vnode);
     if (rawVNode.shapeFlag & ShapeFlags.SUSPENSE) {
     rawVNode.ssContent = vnode;
     }
     }
     pendingCacheKey = key;
     if (cachedVNode) {
     // 缓存存在
     vnode.el = cachedVNode.el;
     vnode.component = cachedVNode.component;
     if (vnode.transition) {
     setTransitionHooks(vnode, vnode.transition!);
     }
     // 避免 VNode 节点作为新节点被挂载
     vnode.shapeFlag |= ShapeFlags.COMPONENT_KEPT_ALIVE;
     // 删除再添加,是为了???让 key 排在靠后的位置
     keys.delete(key);
     keys.add(key);
     } else {
     // 缓存不存在
     keys.add(key);
     // 舍弃最早的缓存
     if (max && keys.size > parseInt(max as string, 10)) {
     pruneCacheEntry(keys.values().next().value);
     }
     }
     // 避免 VNode 被卸载,给组件打上 tag
     vnode.shapeFlag |= ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE;
     current = vnode;
     return rawVNode;
     };
     }
    };
    export const KeepAlive = KeepAliveImpl as any as {
     __isKeepAlive: true;
     new (): {
     $props: VNodeProps & KeepAliveProps;
     };
    };
    

    nextTick 2.x

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    
    Vue.prototype.$nextTick = function (fn) {
     return nextTick(fn, this);
    };
    Vue.nextTick = nextTick;
    const callbacks = [];
    let pending = false;
    let timerFunc;
    // 执行 callbacks 里所有的回调函数
    function flushCallbacks() {
     pending = false;
     const copies = callbacks.slice(0);
     callbacks.length = 0;
     for (let i = 0; i < copies.length; i++) {
     copies[i]();
     }
    }
    if (typeof Promise !== 'undefined' && isNative(Promise)) {
     const p = Promise.resolve();
     timerFunc = () => {
     p.then(flushCallbacks);
     if (isIOS) setTimeout(noop);
     };
     isUsingMicroTask = true;
    } else if (
     !isIE &&
     typeof MutationObserver !== 'undefined' &&
     (isNative(MutationObserver) ||
     MutationObserver.toString() === '[object MutationObserverConstructor]')
    ) {
     let counter = 1;
     const observer = new MutationObserver(flushCallbacks);
     const textNode = document.createTextNode(String(counter));
     observer.observe(textNode, {
     characterData: true
     });
     timerFunc = () => {
     counter = (counter + 1) % 2;
     textNode.data = String(counter);
     };
     isUsingMicroTask = true;
    } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
     timerFunc = () => {
     setImmediate(flushCallbacks);
     };
    } else {
     timerFunc = () => {
     setTimeout(flushCallbacks, 0);
     };
    }
    /**
     * $nextTick 有三种调用方式:
     * 1. $nextTick(cb)
     * 2. $nextTick.then(cb)
     * 3. async $nextTick()
     */
    export function nextTick(cb, ctx) {
     let _resolve;
     callbacks.push(() => {
     if (cb) {
     try {
     cb.call(ctx);
     } catch (e) {
     handleError(e, ctx, 'nextTick');
     }
     } else if (_resolve) {
     // _resolve 将 ctx 传递给 then 里边的 cb
     _resolve(ctx);
     }
     });
     if (!pending) {
     pending = true;
     /**
     * timerFunc 有四种运行 flushCallbacks 的方式:
     * 1. Promise(微任务)
     * 2. MutationObserver(微任务)
     * 3. setImmediate(宏任务)
     * 4. setTimeout(宏任务)
     */
     timerFunc();
     }
     if (!cb && typeof Promise !== 'undefined') {
     return new Promise(resolve => {
     // 当调用 _resolve 时,会走 then(cb)
     _resolve = resolve;
     });
     }
    }
    

    nextTick 3.x

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    const resolvedPromise: Promise<any> = Promise.resolve();
    let currentFlushPromise: Promise<void> | null = null;
    // nextTick 的实现就是 promise
    export function nextTick(
     this: ComponentPublicInstance | void,
     fn?: () => void
    ): Promise<void> {
     const p = currentFlushPromise || resolvedPromise;
     return fn ? p.then(this ? fn.bind(this) : fn) : p;
    }
    

    vnode 2.x

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    
    /**
     * 调用场景:
     * 1. createComponent 中 new VNode(...)
     * 2. _createElement 中
     * 如果是注册的组件,调用 createComponent
     * 如果是 HTML 元素,new VNode(...)
     * 如果是未知元素,new VNode(...)
     * 3. 创建空节点,emptyNode = new VNode('', {}, [])
     * 4. 创建文本节点,new VNode(undefined, undefined, undefined, String(val))
     * 5. cloneVNode,源码就在下面
     */
    export default class VNode {
     constructor(
     tag,
     data,
     children,
     text,
     elm,
     context,
     componentOptions,
     asyncFactory
     ) {
     this.tag = tag; // `vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
     this.data = data;
     this.children = children;
     this.text = text;
     this.elm = elm;
     this.ns = undefined; // 命名空间
     this.context = context; // 渲染组件的作用域
     this.fnContext = undefined; // 函数节点的上下文
     this.fnOptions = undefined; // SSR 缓存相关
     this.fnScopeId = undefined; // 函数作用域 ID 支持
     this.key = data && data.key;
     this.componentOptions = componentOptions;
     this.componentInstance = undefined; // 组件实例
     /**
     * vm.$options.parent:子组件渲染执行 vm.__patch__ 时当前活跃的组件实例,也就是子组件实例的父组件实例
     * vm.$parent:组件实例的第一个“非抽象”的父组件实例
     */
     this.parent = undefined; // 组件占位 vnode,不会出现在 DOM
     this.raw = false; // 是否包含原生 HTML?
     this.isStatic = false; // 是否是静态提升节点
     this.isRootInsert = true; // 进入 transition 检查所必需的
     this.isComment = false; // 是否是空的注释占位符?
     this.isCloned = false; // 是否是克隆节点?
     this.isOnce = false; // 是否是 v-once 节点?
     this.asyncFactory = asyncFactory; // 异步组件工厂函数
     this.asyncMeta = undefined;
     this.isAsyncPlaceholder = false;
     }
    }
    // 创建空白 VNode
    export const createEmptyVNode = (text = '') => {
     const node = new VNode();
     node.text = text;
     node.isComment = true;
     return node;
    };
    // 创建文本 VNode
    export function createTextVNode(val) {
     return new VNode(undefined, undefined, undefined, String(val));
    }
    /**
     * 适用于静态节点和插槽节点,因为他们可能被多个 render 重用
     * 当 DOM 操作依赖它们的 elm 引用时,克隆它们可以避免错误
     */
    export function cloneVNode(vnode) {
     const cloned = new VNode(
     vnode.tag,
     vnode.data,
     vnode.children && vnode.children.slice(),
     vnode.text,
     vnode.elm,
     vnode.context,
     vnode.componentOptions,
     vnode.asyncFactory
     );
     cloned.ns = vnode.ns;
     cloned.isStatic = vnode.isStatic;
     cloned.key = vnode.key;
     cloned.isComment = vnode.isComment;
     cloned.fnContext = vnode.fnContext;
     cloned.fnOptions = vnode.fnOptions;
     cloned.fnScopeId = vnode.fnScopeId;
     cloned.asyncMeta = vnode.asyncMeta;
     cloned.isCloned = true;
     return cloned;
    }
    

    vnode 3.x

     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    
    function _createVNode(
     type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
     props: (Data & VNodeProps) | null = null,
     children: unknown = null,
     patchFlag: number = 0,
     dynamicProps: string[] | null = null,
     isBlockNode = false
    ): VNode {
     if (!type || type === NULL_DYNAMIC_COMPONENT) {
     if (!type) {
     warn(`非法 type 类型:${type}.`);
     }
     type = Comment;
     }
     if (isVNode(type)) {
     /**
     * <component :is="vnode"/>,这种情况下,createVNode 接受一个存在的 VNode
     * 在克隆前后时,合并 VNode 的 ref
     */
     const cloned = cloneVNode(type, props, true /* mergeRef: true */);
     if (children) {
     normalizeChildren(cloned, children);
     }
     return cloned;
     }
     // 规范化类组件
     if (isClassComponent(type)) {
     type = type.__vccOpts;
     }
     // 规范化 class 和 style
     if (props) {
     // 响应式/代理 props,在修改之前应该先克隆
     if (isProxy(props) || '__vInternal' in props) {
     props = extend({}, props); // Object.assign
     }
     let { class: klass, style } = props;
     if (klass && !isString(klass)) {
     // {...class}/[...class] => "...class"
     props.class = normalizeClass(klass);
     }
     if (isObject(style)) {
     if (isProxy(style) && !isArray(style)) {
     style = extend({}, style);
     }
     // unknown => {}
     props.style = normalizeStyle(style);
     }
     }
     // 将 VNode 类型信息编码为位图
     const shapeFlag = isString(type)
     ? ShapeFlags.ELEMENT
     : __FEATURE_SUSPENSE__ && isSuspense(type)
     ? ShapeFlags.SUSPENSE
     : isTeleport(type)
     ? ShapeFlags.TELEPORT
     : isObject(type)
     ? ShapeFlags.STATEFUL_COMPONENT
     : isFunction(type)
     ? ShapeFlags.FUNCTIONAL_COMPONENT
     : 0;
     if (shapeFlag & ShapeFlags.STATEFUL_COMPONENT && isProxy(type)) {
     type = toRaw(type);
     }
     // 创建 VNode
     const vnode: VNode = {
     __v_isVNode: true,
     __v_skip: true, // 表示不能变成“响应式”数据,例如 Object.freeze()、Date、VNode
     type,
     props,
     key: props && normalizeKey(props),
     ref: props && normalizeRef(props),
     scopeId: currentScopeId,
     children: null,
     component: null,
     suspense: null,
     ssContent: null,
     ssFallback: null,
     dirs: null,
     transition: null,
     el: null,
     anchor: null,
     target: null,
     targetAnchor: null,
     staticCount: 0,
     shapeFlag,
     patchFlag,
     dynamicProps,
     dynamicChildren: null,
     appContext: null
     };
     // vnode.key 不能为 NaN
     if (vnode.key !== vnode.key) {
     warn(`VNode created with invalid key (NaN)`);
     }
     // 标准化子节点,把不同数据类型的 children 转成数组或者文本类型
     normalizeChildren(vnode, children);
     if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
     const { content, fallback } = normalizeSuspenseChildren(vnode);
     vnode.ssContent = content;
     vnode.ssFallback = fallback;
     }
     // 添加动态 VNode 到 currentBlock 中
     if (
     shouldTrack > 0 &&
     // 避免块节点跟踪自身
     !isBlockNode &&
     // 具有当前父块
     currentBlock &&
     /**
     * 存在 patchFlag 表示此节点需要在更新时修补;
     * 组件节点也应始终进行修补;因为,即使组件不需要更新,
     * 它需要将实例持久化到下一个 VNode,以便以后可以正确卸载???
     */
     (patchFlag > 0 || shapeFlag & ShapeFlags.COMPONENT) &&
     patchFlag !== PatchFlags.HYDRATE_EVENTS
     ) {
     /**
     * 如果 VNode 是一个动态节点,则添加到 currentBlock 中
     * block 的目的就是收集动态 VNode
     * 在 patch 阶段,更新粒度由原来的组件缩小为 block
     * 仅仅对比动态节点,静态节点得以提升,优化性能
     */
     currentBlock.push(vnode);
     }
     return vnode;
    }
    

    watch 2.x

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    
    // 在 initState 的过程中会判断组件 options 有没有 watch 要处理
    export function initState(vm: Component) {
     // ...
     if (opts.watch && opts.watch !== nativeWatch) {
     initWatch(vm, opts.watch);
     }
    }
    function initWatch(vm: Component, watch: Object) {
     for (const key in watch) {
     const handler = watch[key];
     // 如果对一个 getter 注册了多个 watcher,这些 watcher 的 handler 会拼成一个数组
     if (Array.isArray(handler)) {
     for (let i = 0; i < handler.length; i++) {
     // 循环创建 watcher
     createWatcher(vm, key, handler[i]);
     }
     } else {
     createWatcher(vm, key, handler);
     }
     }
    }
    function createWatcher(
     vm: Component,
     expOrFn: string | Function,
     handler: any,
     options?: Object
    ) {
     // {..., handler, ...}
     if (isPlainObject(handler)) {
     options = handler;
     handler = handler.handler;
     }
     // 去 methods 找 handler
     if (typeof handler === 'string') {
     handler = vm[handler];
     }
     // 调用 this.$watch
     return vm.$watch(expOrFn, handler, options);
    }
    export function stateMixin(Vue: Class<Component>) {
     // ...
     Vue.prototype.$watch = function (
     expOrFn: string | Function,
     cb: any,
     options?: Object
     ): Function {
     const vm: Component = this;
     if (isPlainObject(cb)) {
     return createWatcher(vm, expOrFn, cb, options);
     }
     options = options || {};
     options.user = true; // this.$watch 这种调用方式的 user 均为 true
     const watcher = new Watcher(vm, expOrFn, cb, options);
     if (options.immediate) {
     const info = `callback for immediate watcher "${watcher.expression}"`;
     pushTarget();
     invokeWithErrorHandling(cb, vm, [watcher.value], vm, info);
     popTarget();
     }
     return function unwatchFn() {
     watcher.teardown();
     };
     };
    }
    

    watch 3.x

    watch 需要侦听特定的数据源,并在单独的回调函数中(惰性)执行副作用;watchEffect 立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。

     1
     2
     3
     4
     5
     6
     7
     8
     9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    
    // watchers 在未定义初始值时触发的初始值
    const INITIAL_WATCHER_VALUE = {};
    export interface WatchOptionsBase {
     flush?: 'pre' | 'post' | 'sync';
     onTrack?: ReactiveEffectOptions['onTrack'];
     onTrigger?: ReactiveEffectOptions['onTrigger'];
    }
    /**
     * watchEffect((onInvalidate) => {...}, {...})
     * Vue 会自动追踪 fn 依赖 instance 中的数据
     * 也就是自动生成 getter(需要先运行一次)
     */
    export function watchEffect(fn, options) {
     return doWatch(fn, null, options);
    }
    /**
     * watch(() => state.count, (count, prevCount, onInvalidate) => {...}, {...})
     * 1. 懒执行副作用;
     * 2. 更具体地说明什么状态应该触发侦听器重新运行;
     * 3. 访问侦听状态变化前后的值。
     */
    export function watch(source, cb, options) {
     return doWatch(source, cb, options);
    }
    // vm.$watch
    export function instanceWatch(
     this: ComponentInternalInstance,
     source: string | Function,
     cb: WatchCallback,
     options?: WatchOptions
    ): WatchStopHandle {
     const publicThis = this.proxy as any;
     const getter = isString(source)
     ? () => publicThis[source]
     : source.bind(publicThis);
     return doWatch(getter, cb.bind(publicThis), options, this);
    }
    // 最终实现
    function doWatch(
     source,
     cb,
     { immediate, deep, flush, onTrack, onTrigger } = {},
     instance = currentInstance
    ) {
     if (!cb) {
     if (immediate !== undefined) {
     warn('只有 watch(source, callback, options?) 才支持 immediate');
     }
     if (deep !== undefined) {
     warn('只有 watch(source, callback, options?) 才支持 deep');
     }
     }
     let getter: () => any;
     let forceTrigger = false;
     // 根据不同的 soure,生成不同的 getter,用于触发 Proxy 的拦截器,进行依赖收集
     if (isRef(source)) {
     // 如果 source 为 ref,自动展开
     getter = () => source.value;
     forceTrigger = !!source._shallow;
     } else if (isReactive(source)) {
     // 如果 source 为 reactive,自动 deep
     getter = () => source;
     deep = true;
     } else if (isArray(source)) {
     // 如果 source 为数组,对每一项做处理并返回
     getter = () =>
     source.map(s => {
     if (isRef(s)) {
     return s.value;
     } else if (isReactive(s)) {
     return traverse(s);
     } else if (isFunction(s)) {
     return callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER);
     }
     });
     } else if (isFunction(source)) {
     if (cb) {
     // watch
     getter = () =>
     callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER);
     } else {
     // watchEffect
     getter = () => {
     if (instance && instance.isUnmounted) {
     return;
     }
     if (cleanup) {
     cleanup();
     }
     return callWithErrorHandling(
     source,
     instance,
     ErrorCodes.WATCH_CALLBACK,
     [onInvalidate]
     );
     };
     }
     } else {
     getter = NOOP;
     }
     if (cb && deep) {
     // 深度遍历 getter 值
     const baseGetter = getter;
     getter = () => traverse(baseGetter());
     }
     // 用来注册清理失效时的回调
     let cleanup: () => void;
     const onInvalidate: InvalidateCbRegistrator = (fn: () => void) => {
     cleanup = runner.options.onStop = () => {
     callWithErrorHandling(fn, instance, ErrorCodes.WATCH_CLEANUP);
     };
     };
     // 初始值是:[] 或 {}
     let oldValue = isArray(source) ? [] : INITIAL_WATCHER_VALUE;
     const job: SchedulerJob = () => {
     if (!runner.active) {
     return;
     }
     if (cb) {
     // 求最新值并执行回调
     const newValue = runner();
     if (deep || forceTrigger || hasChanged(newValue, oldValue)) {
     // 执行 cb 之前,先清除上一次运行回调的“垃圾”
     if (cleanup) {
     cleanup();
     }
     callWithAsyncErrorHandling(cb, instance, ErrorCodes.WATCH_CALLBACK, [
     // cb 接受的三个参数
     newValue,
     oldValue === INITIAL_WATCHER_VALUE ? undefined : oldValue,
     onInvalidate
     ]);
     oldValue = newValue; // 新值变旧值
     }
     } else {
     runner(); // 求最新值
     }
     };
     // 将 job 标记为 watcher 的回调,以便 scheduler 知道允许它自触发???
     job.allowRecurse = !!cb;
     /**
     * watch 的 flush 选项:
     * 1. pre,默认值,回调应该在渲染前被调用,允许回调在模板运行前更新了其他值,回调缓存在 preFlushCbs
     * 2. post,将回调推迟到渲染之后的,回调缓存在 postFlushCbs
     * 3. sync,一旦值发生了变化,回调将被同步调用
     */
     let scheduler: ReactiveEffectOptions['scheduler'];
     if (flush === 'sync') {
     scheduler = job;
     } else if (flush === 'post') {
     // flush:post
     scheduler = () => queuePostRenderEffect(job, instance && instance.suspense);
     } else {
     // flush:pre
     scheduler = () => {
     if (!instance || instance.isMounted) {
     // 如果实例不存在或者实例已经挂载
     queuePreFlushCb(job);
     } else {
     // 实例存在但是还没挂载,同步执行就好
     job();
     }
     };
     }
     // 求值函数,getter 会被转换为 ReactiveEffect,getter 被调用的过程中,会进行依赖收集
     const runner = effect(getter, {
     lazy: true,
     onTrack,
     onTrigger,
     // 根据 Options 的不同,scheduler 有三种不同的取值
     scheduler
     });
     if (instance) {
     (instance.effects || (instance.effects = [])).push(runner);
     }
     if (cb) {
     if (immediate) {
     job();
     } else {
     oldValue = runner();
     }
     } else if (flush === 'post') {
     queuePostRenderEffect(runner, instance && instance.suspense);
     } else {
     runner();
     }
     // 停止监听函数
     return () => {
     stop(runner);
     if (instance) {
     remove(instance.effects!, runner);
     }
     };
    }
    


沪ICP备19023445号-2号
友情链接