vue-best-practices

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Vue 3 Best Practices

Vue 3 最佳实践

Quick Reference

快速参考

TopicWhen to UseReference
TypeScriptProps extraction, generic components, useTemplateRef, JSDoc, reactive props destructuretypescript.md
VolarIDE config, strictTemplates, CSS modules, directive comments, Volar 3.0 migrationvolar.md
ComponentsdefineModel, deep watch, onWatcherCleanup, useId, deferred teleportcomponents.md
ToolingmoduleResolution, HMR SSR, duplicate plugin detectiontooling.md
TestingPinia store mocking, setup stores, Vue Router typed paramstesting.md
主题使用场景参考链接
TypeScriptProps提取、泛型组件、useTemplateRef、JSDoc、响应式Props解构typescript.md
VolarIDE配置、strictTemplates、CSS modules、指令注释、Volar 3.0迁移volar.md
组件defineModel、深度监听、onWatcherCleanup、useId、延迟Teleportcomponents.md
工具链moduleResolution、HMR SSR、重复插件检测tooling.md
测试Pinia store模拟、setup stores、Vue Router类型化参数testing.md

Essential Patterns

核心模式

Extract Component Props

提取组件Props

typescript
import type { ComponentProps } from 'vue-component-type-helpers'
import MyButton from './MyButton.vue'

type Props = ComponentProps<typeof MyButton>
typescript
import type { ComponentProps } from 'vue-component-type-helpers'
import MyButton from './MyButton.vue'

type Props = ComponentProps<typeof MyButton>

Reactive Props Destructure (Vue 3.5+)

响应式Props解构(Vue 3.5+)

vue
<script setup lang="ts">
// Destructured props are reactive - preferred in Vue 3.5+
const { name, count = 0 } = defineProps<{ name: string; count?: number }>()
</script>
vue
<script setup lang="ts">
// 解构后的Props是响应式的——在Vue 3.5+中推荐使用
const { name, count = 0 } = defineProps<{ name: string; count?: number }>()
</script>

useTemplateRef (Vue 3.5+)

useTemplateRef(Vue 3.5+)

vue
<script setup lang="ts">
import { useTemplateRef, onMounted } from 'vue'

const inputRef = useTemplateRef('input')  // Auto-typed
onMounted(() => inputRef.value?.focus())
</script>
<template><input ref="input" /></template>
vue
<script setup lang="ts">
import { useTemplateRef, onMounted } from 'vue'

const inputRef = useTemplateRef('input')  // 自动类型推导
onMounted(() => inputRef.value?.focus())
</script>
<template><input ref="input" /></template>

onWatcherCleanup (Vue 3.5+)

onWatcherCleanup(Vue 3.5+)

typescript
import { watch, onWatcherCleanup } from 'vue'

watch(query, async (q) => {
  const controller = new AbortController()
  onWatcherCleanup(() => controller.abort())
  await fetch(`/api?q=${q}`, { signal: controller.signal })
})
typescript
import { watch, onWatcherCleanup } from 'vue'

watch(query, async (q) => {
  const controller = new AbortController()
  onWatcherCleanup(() => controller.abort())
  await fetch(`/api?q=${q}`, { signal: controller.signal })
})

defineModel with Required

带必填项的defineModel

typescript
// Returns Ref<Item> instead of Ref<Item | undefined>
const model = defineModel<Item>({ required: true })
typescript
// 返回Ref<Item>而非Ref<Item | undefined>
const model = defineModel<Item>({ required: true })

Deep Watch with Numeric Depth

带数值深度的深度监听

typescript
// Vue 3.5+ - watch array mutations without full traversal
watch(items, handler, { deep: 1 })
typescript
// Vue 3.5+ - 无需完整遍历即可监听数组变更
watch(items, handler, { deep: 1 })

Pinia Store Test Setup

Pinia Store测试搭建

typescript
import { createTestingPinia } from '@pinia/testing'
import { vi } from 'vitest'

mount(Component, {
  global: {
    plugins: [createTestingPinia({ createSpy: vi.fn })]
  }
})
typescript
import { createTestingPinia } from '@pinia/testing'
import { vi } from 'vitest'

mount(Component, {
  global: {
    plugins: [createTestingPinia({ createSpy: vi.fn })]
  }
})

Common Mistakes

常见错误

  1. Using
    InstanceType<typeof Component>['$props']
    - Use
    ComponentProps
    instead
  2. Missing
    createSpy
    in createTestingPinia
    - Required in @pinia/testing 1.0+
  3. Using
    withDefaults
    with union types
    - Use Reactive Props Destructure
  4. strictTemplates
    in wrong tsconfig
    - Add to
    tsconfig.app.json
    , not root
  5. ts_ls with Volar 3.0 - Use vtsls instead (Neovim)
  6. deep: true
    on large structures
    - Use numeric depth for performance
  7. Watching destructured props directly - Wrap in getter:
    watch(() => count, ...)
  8. Random IDs in SSR - Use
    useId()
    for hydration-safe IDs
  1. 使用
    InstanceType<typeof Component>['$props']
    ——应改用
    ComponentProps
  2. createTestingPinia中缺少
    createSpy
    ——@pinia/testing 1.0+版本必填
  3. withDefaults
    与联合类型一起使用
    ——应使用响应式Props解构
  4. 在错误的tsconfig中设置
    strictTemplates
    ——应添加到
    tsconfig.app.json
    ,而非根目录的tsconfig
  5. Volar 3.0搭配ts_ls使用——应改用vtsls(适用于Neovim)
  6. 对大型结构设置
    deep: true
    ——为提升性能应使用数值深度
  7. 直接监听解构后的Props——应包裹在 getter 中:
    watch(() => count, ...)
  8. SSR中使用随机ID——应使用
    useId()
    确保 hydration 安全的ID