Rendering & Caching — Series 2

Preview — 3 of 10 questions

Why does wrapping COUNTRY_LIST in Object.freeze() before putting it in ref() reduce Vue's overhead, compared to leaving it unfrozen?

javascript
import { ref } from 'vue'

const COUNTRY_LIST = Object.freeze([
  { code: 'US', name: 'United States' },
  { code: 'FR', name: 'France' },
  // ... hundreds more, genuinely never modified after this point
])

const countries = ref(COUNTRY_LIST)
AVue's reactivity conversion specifically detects frozen objects (via Object.isFrozen()) and skips wrapping them in a reactive Proxy — since this data is known to never change, skipping the (otherwise recursive, potentially expensive for large/deeply-nested data) reactive conversion avoids real, measurable setup cost for data that would never benefit from being reactive in the first place
BObject.freeze() has no interaction with Vue's reactivity system at all — this code behaves identically to passing the unfrozen array directly
CObject.freeze() makes the array reactive at a deeper level than normal — every nested object gains additional reactive tracking it wouldn't otherwise have
DThis throws a runtime error — ref() cannot accept a frozen value as its initial argument

Why does this pattern avoid redundant work when expensiveLookup is called with the same arguments repeatedly across different renders?

javascript
import { computed } from 'vue'

const cache = new Map()

function expensiveLookup(a, b) {
  const key = `${a}:${b}`
  if (cache.has(key)) return cache.get(key)

  const result = /* ... some genuinely expensive computation using a and b ... */
  cache.set(key, result)
  return result
}

const derivedValue = computed(() => expensiveLookup(inputA.value, inputB.value))
AThe outer Map-based cache persists across every recomputation of derivedValue, keyed by the actual argument values — so if inputA/inputB cycle back to a combination that's been seen before (even after other values were computed in between), the expensive work is skipped entirely on that repeat, something computed's own caching (which only remembers its single most recent result) can't provide by itself
BThis is redundant — a plain computed already memoizes per-argument-combination automatically, without needing a manual Map cache inside the function it calls
CMap-based caching inside a function called from a computed breaks Vue's reactivity tracking entirely — derivedValue would never update when inputA/inputB change
DThis pattern only works if cache itself is wrapped in reactive() — a plain Map, as shown, wouldn't actually cache anything

What does this CSS property do for the (currently offscreen) sections of a long page?

javascript
<template>
  <article v-for="section in longArticleSections" :key="section.id" class="section">
    <h2>{{ section.title }}</h2>
    <p>{{ section.body }}</p>
  </article>
</template>

<style scoped>
.section {
  content-visibility: auto;
  contain-intrinsic-size: 0 500px; /* an estimated placeholder height */
}
</style>
Acontent-visibility: auto tells the browser to skip rendering (layout, paint, and more) work for an element's contents while it's off-screen, only doing that work once the element scrolls near the viewport — similar in spirit to virtual scrolling, but implemented entirely by the browser's own rendering engine rather than by JavaScript conditionally mounting/unmounting elements. contain-intrinsic-size provides a placeholder size so the page's scrollbar/layout doesn't jump around before the real content has been measured
Bcontent-visibility: auto is a Vue-specific CSS extension — it has no meaning or effect outside of a Vue application
Ccontent-visibility: auto completely removes offscreen elements from the DOM, identical to v-if="false" — they need to be recreated from scratch once scrolled into view
DThis property only affects <img> elements — it has no effect on text content like <h2>/<p>

Sign up free to play

Answer all 10 questions (7 more), see explanations for every answer, and track your score.