Question presented to candidate:
"After migrating a component from the Options API to <script setup>, a parent that used this.$refs.editor.content started reading undefined, and a child that used this.$parent.save() broke too. Explain why <script setup> components are closed by default, what defineExpose() changes, and whether you would just expose everything to fix it."
What a strong answer should cover:
- An Options API component is open: its public instance (through template refs or
$parent) exposes data, computed and methods. A<script setup>component is closed: none of its bindings are visible, only built-ins such as$eland$props. - How it is done: in dev the compiler returns bindings for the template but calls
expose()with no arguments; in production the template is inlined and the bindings never leave thesetupclosure. defineExpose({ ... })re-opens a chosen part: the exposed object replaces the public instance's own properties, and refs in it are unwrapped.- Why closed is the better default: encapsulation and safe refactoring, no reaching into children or parents, a public API that can be typed, and the same behaviour in dev and prod builds.
- Options API components can opt in to the same model with the
expose: [...]option. - Exposing everything is the wrong fix; move the data to props/events,
v-model, provide/inject or a store, and expose only commands.
Clarifying questions expected:
- "What does the parent actually need from the editor: its content, or the ability to trigger something like save?"
- "Are there tests that rely on reading internal state?"
Code / implementation expected: Optional β show the same child open, closed and exposing a single method, or describe the compiled output.