Vue 接触式依赖的简单例子

接触式依赖收集的最基础实现

如下例子:
当点击按钮做对象赋值,能够触发render函数的重新执行

1
2
3
4
5
6
7
8
9
10
<!-- GLOBAL Html -->
<div style="padding: 30px;">
<div id="renderDiv1"></div>
<div id="renderDiv2" style="margin-top: 10px;"></div>
<div style="margin: 20px 0;">
<button onclick="window.reactiveObj.a = new Date()">
点击查看最新时间
</button>
</div>
</div>

收集需要被依赖的target

  1. 对需要响应式的数据做getter setter处理
  2. 对可能存在依赖的方法,通过一个代理方法去执行
  3. 代理方法会先把要执行的方法,传如到外层作用域的target
  4. 然后执行该方法,如果触发了getter方法,会向SubPub订阅一个target方法 (会为每一个属性创建一个SubPub实例)
  5. 执行完成,释放掉target
  6. 当set值的时候,发布变更通知,取出所有订阅的target方法再次执行 (实际就是重新执行被get接触过的target方法)
    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

    /* GLOBAL js */
    let target = null;

    class SubPub {
    constructor() {
    this.subList = [];
    }

    sub() {
    if (target && !this.subList.includes(target)) {
    this.subList.push(target);
    }
    }

    pub() {
    this.subList.forEach(sub => sub());
    }
    }

    window.reactiveObj = {};
    const dependA = new SubPub();
    Object.defineProperty(window.reactiveObj, 'a', {
    get() {
    dependA.sub();
    return this.$$a || new Date();
    },
    set(val) {
    this.$$a = val;
    dependA.pub();
    }
    });

    // target 是通过代理方法执行传入的
    function run(fn) {
    target = fn;
    target();
    target = null;
    }

    // render1
    function render1() {
    document.getElementById('renderDiv1').innerText = `当前时间:${String(window.reactiveObj.a)}`;
    }

    // render1
    function render2() {
    document.getElementById('renderDiv2').innerText = `当前时间戳:${+window.reactiveObj.a}`;
    }


    run(render1);
    run(render2);