SyntaxStudy
Sign Up
Vue.js Shallow Refs and Advanced Reactivity Utilities
Vue.js Beginner 1 min read

Shallow Refs and Advanced Reactivity Utilities

For performance-sensitive scenarios with large data structures, Vue provides `shallowRef` and `shallowReactive`. These create reactive containers that only track changes at the top level — nested object properties are not made reactive. When you work with immutable or external data you intend to replace wholesale (like large arrays returned from an API), shallow reactivity avoids the overhead of deep proxying. `readonly` and `shallowReadonly` create read-only proxies that throw warnings if mutated in development. They are commonly combined with `provide`/`inject` to guarantee that consuming components cannot accidentally corrupt shared state. `markRaw` permanently marks an object as non-reactive, useful for third-party class instances (such as a map SDK) that should not be proxied. `triggerRef` is an escape hatch: after manually mutating the inner value of a `shallowRef` you call `triggerRef(myRef)` to force Vue to process any dependent effects. `isRef`, `isReactive`, and `isReadonly` let you inspect reactivity at runtime, which comes in handy when writing generic composable utilities that accept either reactive or plain values.
Example
import {
  shallowRef, shallowReactive, readonly,
  markRaw, triggerRef, isRef, isReactive
} from 'vue';

// ── shallowRef: only .value change is tracked ─
const bigList = shallowRef([/* thousands of rows */]);
// Mutate inner array then manually trigger
bigList.value.push({ id: 999, name: 'New' });
triggerRef(bigList);            // force update

// ── shallowReactive: top-level props only ─────
const state = shallowReactive({ count: 0, nested: { deep: 1 } });
state.count++;                  // tracked ✓
state.nested.deep++;            // NOT tracked ✗

// ── readonly: prevent mutations ───────────────
const immutable = readonly({ token: 'abc123' });
// immutable.token = 'xyz'; // Vue warning in dev

// ── markRaw: skip proxy for third-party objects
class MySDK { constructor() { this.id = Math.random(); } }
const sdk = markRaw(new MySDK());
const store = shallowReactive({ sdk });
console.log(isReactive(store.sdk)); // false — intentional

// ── Runtime checks ─────────────────────────────
import { ref } from 'vue';
const r = ref(0);
console.log(isRef(r));       // true
console.log(isRef(0));       // false