Skip to content

shallowRef/triggerRef

什么是浅层的 ref
html
<template>
  <div>
    <p>{{ simpleRef.count }}</p>
    <button @click="increment">shallowRef value increment</button>
    <button @click="setIncrement">shallowRef reset value</button>
  </div>
</template>

<script setup>
  import { shallowRef, effect } from 'vue';
  const simpleRef = shallowRef({ count: 0 });

  effect(() => {
    console.log('simpleRef.value.count:', simpleRef.value.count);
  });

  function increment() {
    // 改变 ref 的 value 的值不会触发 effect
    // 如果是 用 ref() 函数定义的值, 改变 value 会触发 effect
    simpleRef.value.count++;
  }

  function setIncrement() {
    // 重新给 ref 的 value 赋值会触发 effect
    simpleRef.value = {
      count: Math.random(),
    };
  }
</script>

什么叫强制触发依赖于一个浅层 ref 的副作用(effect)
html
<template>
  <div>
    <p>{{ simpleRef.count }}</p>
    <button @click="increment">shallowRef value increment</button>
    <button @click="setIncrement">shallowRef reset value</button>
  </div>
</template>

<script setup>
  import { shallowRef, effect, triggerRef } from 'vue';
  const simpleRef = shallowRef({ count: 0 });

  effect(() => {
    console.log('simpleRef.value.count:', simpleRef.value.count);
  });

  function increment() {
    // 默认不会触发
    simpleRef.value.count++;

    // 强制触发这个 shallowRef 的所有依赖(effect)
    triggerRef(simpleRef);
  }

  function setIncrement() {
    // 会触发 effect
    simpleRef.value = {
      count: Math.random(),
    };
  }
</script>

customRef

  • customRef: 自定义 ref 收集依赖和触发依赖
html
<template>
  <div>{{ evenRef }}</div>
  <button @click="increment(1)">increment 1</button>
  <button @click="increment(2)">increment 2</button>
</template>

<script setup>
  import { customRef, effect } from 'vue';

  function useEvenRef(value, delay = 2000) {
    return customRef((track, trigger) => {
      return {
        get() {
          // 自定义如何收集依赖
          console.log('getter', value);
          track();
          return value;
        },
        set(newValue) {
          // 自定义如何触发依赖
          value = newValue;
          console.log('setter', value);
          if (typeof value === 'number' && value % 2 === 0) {
            trigger();
          }
        },
      };
    });
  }

  const evenRef = useEvenRef(0);

  effect(() => {
    console.log('---effect---', evenRef.value);
  });

  function increment(step) {
    evenRef.value += step;
  }
</script>

effectScope

什么是 effectScope
html
<template>
  <div>{{ state.count }}</div>
  <button @click="incrementCount">increment</button>
  <button @click="scopeStop">stopScope</button>
</template>
<script setup>
  import { effectScope, reactive, effect, watchEffect, getCurrentScope } from 'vue';
  const effectScope1 = effectScope();

  const state = reactive({
    count: 0,
  });

  // 1.1 获取外界的顶层的 scope 上下文对象
  const topScope = getCurrentScope();
  topScope.__mark = 'topScope';
  console.log('setup topScope:', topScope);

  effect(() => {
    console.info('[outside-effect]', state.count);
  });

  // setup 顶层的 effectScope 作用域不用手动调用 run, effect 会生效
  // 但是用 effectScope() 执行生成的作用域必须手动调用 run, 才能让其中的 effect 生效
  effectScope1.run(() => {
    // 1.2 获取 effectScope1 里面的 scope 上下文对象
    const effectScope1 = getCurrentScope();
    effectScope1.__mark = 'effectScope1';
    console.log('effectScope1:', effectScope1);

    // 在作用域内部创建响应式数据和副作用函数
    effect(() => {
      console.log('[scope-run-effect]:', state.count);
    });

    watchEffect(() => {
      console.log('[scope-run-watchEffect]:', state.count);
    });
  });

  // 2.1. 当 scope.run 执行之后, 如果响应式数据发生改变
  // 会执行这个作用域中的 effect 和 watchEffect
  function incrementCount() {
    state.count++;
  }

  // 2.2. 当 scope.stop 执行之后, 如果响应式数据发生改变
  // 就不会再执行这个作用域中的 effect 和 watchEffect
  // 但是外界的 outside-effect 不受影响
  function scopeStop() {
    effectScope1.stop();
    // outsideScope.stop(); // 如果把外界最顶层的 scope 调用 stop 就会让所有的 effect 失效
  }
</script>

preview

Released under the MIT License.