Default <script setup> + Composition API. Stacks other than Vue (build tools, CSS, state libraries, TS version) follow the project; do not specify them in this skill.
Do not write APIs from training data. First determine the minor version, then read the corresponding reference.
Determine version
- Read the installed
vueversion. - Take minor(
3.4.x→3.4), open the file in the table below, and write code according to that file.
| minor | File |
|---|---|
| 3.4 | references/3.4.md |
| 3.5 | references/3.5.md |
For newer minors not listed: start from the highest covered minor, then consult the official changelog / docs for that minor. For older minors: do not use the new APIs in this skill.
Choose reactive APIs
Do not ref for everything. Choose based on whether the whole value is replaced, whether fields are modified in place, and whether deep tracking is needed.
| API | Use for |
|---|---|
ref |
values that will bewholly replaced: primitive types, toggles, current id, objects replaced as a whole |
reactive |
structures that modify fields in place, especially forms, drafts, and a group of related fields |
shallowRef |
Only care about .value replacement: large lists, third-party instances, immutable snapshots. Internal field changes do not notify |
shallowReactive |
Only care about root properties; treat nested as immutable |
markRaw |
Never tracked |
triggerRef |
When it must notify after modifying the internals of shallowRef (prefer directly replacing .value) |
customRef |
Control track/trigger yourself (e.g., debounce) |
computed |
See the next section,not needed by default |
| Regular variables | Do not drive the view, do not participate in derivation |
ref(obj) Internally it also deep-proxies, just with an extra layer of .value. For forms use reactive, not ref({ name, email })。
If a list needs to track element fields, use ref([]); if it only updates when the whole list is replaced, use shallowRef。
computed
Fixed structure with only one reactive segment embedded (such as a propsin config) → pass the object literal by reference, do not wrap it in computed. Otherwise the dependency is attached to the outer layer, and changing the outer layer re-renders the whole page.
const field = { component: Input, label: "姓名", props: form };
Do not { ...form }. Use computed only for a new list after filtering/sorting, or for expensive derivations reused in multiple places.
Components and data flow
- Do not let a single file grow into a giant (about 500 lines); put orchestration in routes/entry, split UI blocks and
useXxxout. - Props down, events up. Across layers:
provide/injector the project's existing store. - Reactive objects between componentspass directly. Do not copy and then sync them yourself; when reset is needed, save an initial snapshot separately.
- For child component
v-modelusedefineModel, do not hand-writemodelValue+update:modelValue:
const model = defineModel<string>({ required: true });
const title = defineModel<string>("title");
const [value, modifiers] = defineModel({ set: (v) => (modifiers.trim ? v.trim() : v) });
- Use shorthand for same-name bindings:
<img :id :src :alt>。 defineOptions/defineSlots/<script setup generic="T">use directly.- For composable parameters use
MaybeRefOrGetter, internallytoValue(). Clean up side effects inonScopeDispose(do not write onlyonUnmounted)。
Update cost
- For list
:keyuse stable ids. - Keep props passed to list items as stable as possible: compute
activebefore passing, do not have every item receive a changingactiveId。 - Heavy, rarely used components behind pagination:
defineAsyncComponent(For the SSR hydration strategy, see 3.5). - Drag /
mousemove/ Scroll: isolate hot state into small child components orshallowRef, and do not let the entire page template re-run.