Loading...
Loading...
Vue SFC Structure Specification. Mandatory script setup organization order and responsibility layering rules when processing **/*.vue files: best practices including definition order, shallowRef selection, template binding functions, attrs inheritance, etc.
npx skill4agent add soybeanjs/skills vue-sfc-structure.vuescript setup**/*.vue<script setup><script setup>defineOptionsimport typedefinePropsimport typedefineEmitsimport typedefineSlotsinitwatchwatchEffectdefineExposedefineOptionsnametypes.tstypes.tsdefineXxx<Type>()const xxx = defineXxx<Type>()propsprops.xxxuseRouteuseRouterrefcomputedshallowRefrefref.valueshallowRefshallowRefrefinitprovideXxxwatchwatchEffectcomputedinit()defineExposescript setup@click="handleSelect(item.id)"@click="() => handleSelect(item.id)"inheritAttrs: falseuseAttrs()v-bind="attrs"inheritAttrs: falseuseAttrs()defineOptionsdefinePropsdefineEmitsdefineSlotsinheritAttrs: falseuseAttrs()script setupshallowRefinitdefineExpose<script setup lang="ts">
import { computed, onMounted, ref, shallowRef, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import type { OptionItem } from './types';
import { provideFilterContext } from './context';
// 1. defineOptions
defineOptions({ name: 'FilterPanel' });
// 2-3. props 类型定义 + defineProps
interface Props {
modelValue: string;
options: OptionItem[];
}
const props = defineProps<Props>();
// 4-5. emits 类型定义 + defineEmits
interface Emits {
(e: 'update:modelValue', value: string): void;
(e: 'change', value: OptionItem): void;
}
const emit = defineEmits<Emits>();
// 6. hooks / composables 初始化
const route = useRoute();
const router = useRouter();
// 7. 组件业务逻辑(按语义分块,shallowRef 用于不需要深层响应式的数据)
const listRef = shallowRef<OptionItem[]>(props.options);
const keyword = ref('');
const filtered = computed(() => {
const kw = keyword.value.trim().toLowerCase();
return kw ? listRef.value.filter(item => item.label.toLowerCase().includes(kw)) : listRef.value;
});
// 8. template 绑定的函数:在脚本中定义,模板只负责引用
function handleSelect(item: OptionItem) {
emit('update:modelValue', item.value);
emit('change', item);
}
// 9. init 函数(仅在确实存在初始化流程时)
function init() {
const initial = route.query.keyword;
if (typeof initial === 'string') {
keyword.value = initial;
}
}
// 10. context provider
provideFilterContext({ keyword, filtered });
// 11. watch / watchEffect(仅在必要时引入)
watch(keyword, value => {
router.replace({ query: { ...route.query, keyword: value || undefined } });
});
// 12. 生命周期 hooks
onMounted(() => {
init();
});
// 13. defineExpose(仅在需要暴露实例 API 时)
defineExpose({ reset: () => (keyword.value = '') });
</script>
<template>
<div class="filter-panel">
<input v-model="keyword" placeholder="搜索" />
<ul>
<li
v-for="item in filtered"
:key="item.value"
@click="handleSelect(item)"
>
{{ item.label }}
</li>
</ul>
</div>
</template>defineOptionslistRefshallowRefkeywordref@click="handleSelect(item)"inheritAttrs: false