Vue 3 JavaScript 最佳实践指南

Vue 3 JavaScript 最佳实践指南

1. 组件结构规范

1.1 文件命名

  • 使用 PascalCase 命名组件文件:UserProfile.vue
  • 使用 kebab-case 命名非组件文件:user-profile.js

1.2 组件模板结构

xml 复制代码
<template>
  <!-- 单根元素 -->
  <div class="component-name">
    <!-- 语义化 HTML 标签 -->
    <header class="component-header">
      <h1>{{ title }}</h1>
    </header>
​
    <main class="component-content">
      <!-- 内容区域 -->
    </main>
​
    <footer class="component-footer">
      <!-- 底部区域 -->
    </footer>
  </div>
</template>

2. Script Setup 最佳实践

2.1 导入顺序

xml 复制代码
<script setup>
// 1. Vue 核心导入
import { ref, computed, watch, onMounted } from 'vue'
​
// 2. 第三方库导入
import { useRouter } from 'vue-router'
import { useStore } from 'vuex'
​
// 3. 组件导入
import UserCard from './UserCard.vue'
import Button from './Button.vue'
​
// 4. 工具函数导入
import { formatDate } from '@/utils/date'
import { validateEmail } from '@/utils/validation'
​
// 5. 类型定义导入
import type { User } from '@/types/user'
</script>

2.2 响应式数据定义

xml 复制代码
<script setup>
// 基本类型使用 ref
const count = ref(0)
const name = ref('')
const isLoading = ref(false)
​
// 对象类型使用 reactive
const user = reactive({
  id: 1,
  name: 'John',
  email: 'john@example.com'
})
​
// 数组使用 ref
const items = ref([])
​
// 计算属性
const fullName = computed(() => `${user.firstName} ${user.lastName}`)
const itemCount = computed(() => items.value.length)
​
// 方法定义
const increment = () => {
  count.value++
}
​
const fetchData = async () => {
  isLoading.value = true
  try {
    const response = await api.get('/data')
    items.value = response.data
  } catch (error) {
    console.error('Failed to fetch data:', error)
  } finally {
    isLoading.value = false
  }
}
​
// 生命周期
onMounted(() => {
  fetchData()
})
</script>

3. Props 和 Emits 规范

3.1 Props 定义

typescript 复制代码
<script setup>
// 运行时声明
defineProps({
  // 必填属性
  title: {
    type: String,
    required: true
  },
  // 可选属性
  description: {
    type: String,
    default: ''
  },
  // 数字类型
  count: {
    type: Number,
    default: 0
  },
  // 数组类型
  items: {
    type: Array,
    default: () => []
  },
  // 对象类型
  config: {
    type: Object,
    default: () => ({})
  },
  // 函数类型
  onSuccess: {
    type: Function,
    default: () => {}
  }
})
​
// 或者使用 TypeScript 类型
defineProps<{
  title: string
  description?: string
  count?: number
  items?: any[]
  config?: Record<string, any>
  onSuccess?: () => void
}>()
</script>

3.2 Emits 定义

xml 复制代码
<script setup>
// 定义 emits
const emit = defineEmits<{
  // 无参数事件
  (e: 'close'): void
  // 带参数事件
  (e: 'update:modelValue', value: string): void
  (e: 'submit', data: FormData): void
  // 可选参数事件
  (e: 'error', message?: string): void
}>()
​
// 使用 emits
const handleClose = () => {
  emit('close')
}
​
const handleSubmit = (data) => {
  emit('submit', data)
}
</script>

4. 响应式数据处理

4.1 避免直接修改响应式对象

javascript 复制代码
// ❌ 不推荐 - 直接修改
user.name = 'New Name'
​
// ✅ 推荐 - 使用 Object.assign 或展开运算符
Object.assign(user, { name: 'New Name' })
// 或者
user.name = 'New Name'

4.2 数组操作

scss 复制代码
const list = ref([])
​
// ✅ 推荐的操作方式
list.value.push(newItem)           // 添加
list.value.splice(index, 1)        // 删除
list.value[index] = newValue       // 修改
list.value = [...list.value, item] // 不可变操作

4.3 解构响应式对象

php 复制代码
const user = reactive({
  name: 'John',
  age: 30,
  address: {
    city: 'New York',
    country: 'USA'
  }
})
​
// ❌ 失去响应性
const { name, age } = user
​
// ✅ 保持响应性
const name = toRef(user, 'name')
const age = toRef(user, 'age')
​
// ✅ 或者使用 computed
const userName = computed(() => user.name)
const userAge = computed(() => user.age)

5. 组合式函数 (Composables)

5.1 创建组合式函数

javascript 复制代码
// composables/useCounter.js
export function useCounter(initialValue = 0) {
  const count = ref(initialValue)
​
  const increment = () => count.value++
  const decrement = () => count.value--
  const reset = () => count.value = initialValue
​
  return {
    count,
    increment,
    decrement,
    reset
  }
}
​
// composables/useApi.js
export function useApi(url) {
  const data = ref(null)
  const error = ref(null)
  const isLoading = ref(false)
​
  const fetchData = async () => {
    isLoading.value = true
    error.value = null
​
    try {
      const response = await fetch(url)
      data.value = await response.json()
    } catch (err) {
      error.value = err
    } finally {
      isLoading.value = false
    }
  }
​
  return {
    data,
    error,
    isLoading,
    fetchData
  }
}

5.2 使用组合式函数

xml 复制代码
<script setup>
import { useCounter, useApi } from '@/composables'
​
// 使用计数器
const { count, increment } = useCounter(0)
​
// 使用 API
const { data: users, isLoading, fetchData } = useApi('/api/users')
​
onMounted(() => {
  fetchData()
})
</script>

6. 事件处理

6.1 事件命名规范

yaml 复制代码
// ✅ 推荐的事件命名
const emit = defineEmits<{
  'update:modelValue': [value: string]
  'input-change': [value: string, event: Event]
  'form-submit': [data: FormData]
  'item-selected': [item: Item, index: number]
}>()

6.2 事件处理函数

csharp 复制代码
<script setup>
const handleClick = (event) => {
  // 阻止默认行为
  event.preventDefault()
​
  // 停止事件冒泡
  event.stopPropagation()
​
  // 业务逻辑
  emit('click', event)
}
​
const handleInput = (event) => {
  const value = event.target.value
  emit('update:modelValue', value)
}
​
// 防抖处理
const debouncedSearch = useDebounceFn((query) => {
  search(query)
}, 300)
</script>

7. 样式和 CSS 最佳实践

7.1 作用域样式

xml 复制代码
<style scoped>
/* 使用 BEM 命名规范 */
.component {
  &__header {
    padding: 1rem;
  }
​
  &__content {
    margin: 1rem 0;
  }
​
  &--disabled {
    opacity: 0.5;
    pointer-events: none;
  }
}
​
/* 使用 CSS 变量 */
:root {
  --primary-color: #42b883;
  --secondary-color: #35495e;
}
​
.component {
  color: var(--primary-color);
  background-color: var(--secondary-color);
}
</style>

8. 性能优化

8.1 条件渲染优化

xml 复制代码
<template>
  <!-- 使用 v-show 频繁切换 -->
  <div v-show="isVisible">频繁切换的内容</div>
​
  <!-- 使用 v-if 一次性渲染 -->
  <div v-if="shouldRender">一次性渲染的内容</div>
​
  <!-- 列表渲染使用 key -->
  <div
    v-for="item in items"
    :key="item.id"
    class="item"
  >
    {{ item.name }}
  </div>
</template>

8.2 计算属性缓存

ini 复制代码
// ✅ 推荐 - 使用计算属性
const filteredItems = computed(() => {
  return items.value.filter(item =>
    item.name.includes(searchTerm.value)
  )
})
​
// ❌ 不推荐 - 在模板中直接计算
// <div v-for="item in items.filter(i => i.name.includes(searchTerm))">

9. 错误处理

9.1 异步错误处理

javascript 复制代码
const fetchUser = async (userId) => {
  try {
    const user = await api.getUser(userId)
    return user
  } catch (error) {
    console.error('Failed to fetch user:', error)
    // 显示错误信息给用户
    showError('加载用户信息失败')
    throw error // 重新抛出错误
  }
}

9.2 组件错误边界

xml 复制代码
// ErrorBoundary.vue
<script setup>
import { ref, onErrorCaptured } from 'vue'
​
const error = ref(null)
​
onErrorCaptured((err, instance, info) => {
  error.value = err
  console.error('Error captured:', err, info)
  // 阻止错误继续传播
  return false
})
</script>
​
<template>
  <div v-if="error" class="error-boundary">
    <h3>Something went wrong</h3>
    <p>{{ error.message }}</p>
    <button @click="error = null">Try Again</button>
  </div>
  <slot v-else />
</template>

10. 代码组织原则

10.1 单一职责原则

  • 每个组件只负责一个特定的功能
  • 复杂的组件拆分为多个小组件
  • 业务逻辑提取到组合式函数中

10.2 可复用性原则

  • 创建通用的基础组件
  • 使用组合式函数封装可复用逻辑
  • 通过 props 和 slots 提供灵活性

10.3 可测试性原则

  • 组件逻辑与 UI 分离
  • 使用依赖注入而不是硬编码
  • 编写单元测试和组件测试

通过遵循这些最佳实践,可以创建出可维护、可测试、高性能的 Vue 3 应用程序。

相关推荐
nightunderblackcat8 小时前
四大名著智能可视化推演平台
前端·网络·爬虫·python·状态模式
Mintopia8 小时前
Next.js 与 Serverless 架构思维:无状态的优雅与冷启动的温柔
前端·后端·全栈
小白而已8 小时前
事件分发机制
前端
zerosrat8 小时前
从零实现一个 JavaScript 引擎
javascript
Keepreal4968 小时前
使用 Three.js 和 GSAP 动画库实现3D 名字抽奖
javascript·vue.js·three.js
蝙蝠编人生8 小时前
TailwindCSS vs UnoCSS 性能深度对决:究竟快多少
前端
ZXH01228 小时前
浏览器兼容性问题处理
前端
鹏多多8 小时前
flutter-切换状态显示不同组件10种实现方案全解析
android·前端·ios
向葭奔赴♡9 小时前
HTML的本质——网页的“骨架”
前端·javascript·html