Skip to content
On this page

Vue.js 最佳实践

Vue.js 最佳实践涵盖了开发过程中应该遵循的指导原则和模式,以确保代码质量、可维护性和性能。

项目结构

推荐的目录结构

src/
├── assets/           # 静态资源
├── components/       # 公共组件
├── views/           # 页面级组件
├── router/          # 路由配置
├── store/           # Vuex/Pinia 状态管理
├── services/        # API 服务
├── utils/           # 工具函数
├── composables/     # 组合式函数 (Vue 3)
└── styles/          # 全局样式

组件设计

单文件组件最佳实践

vue
<!-- 推荐:清晰的组件结构 -->
<template>
  <div class="user-card">
    <h3>{{ user.name }}</h3>
    <p>{{ user.email }}</p>
  </div>
</template>

<script setup>
import { computed } from 'vue'

// Props 验证
const props = defineProps({
  user: {
    type: Object,
    required: true,
    default: () => ({})
  }
})

// 计算属性
const displayName = computed(() => {
  return props.user.name || '未知用户'
})
</script>

<style scoped>
.user-card {
  padding: 1rem;
  border: 1px solid #ccc;
  border-radius: 4px;
}
</style>

组件通信

vue
<!-- 父组件 -->
<template>
  <ChildComponent 
    :user="currentUser"
    @update-user="handleUserUpdate"
  />
</template>

<script setup>
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'

const currentUser = ref({ id: 1, name: 'John' })

const handleUserUpdate = (userData) => {
  currentUser.value = userData
}
</script>

状态管理

Pinia 最佳实践

javascript
// stores/user.js
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    profile: null,
    isAuthenticated: false
  }),
  
  getters: {
    fullName: (state) => {
      return state.profile ? `${state.profile.firstName} ${state.profile.lastName}` : ''
    }
  },
  
  actions: {
    async login(credentials) {
      try {
        const response = await api.login(credentials)
        this.profile = response.data
        this.isAuthenticated = true
        return response
      } catch (error) {
        throw error
      }
    },
    
    logout() {
      this.profile = null
      this.isAuthenticated = false
    }
  }
})

性能优化

组件懒加载

javascript
import { createRouter, createWebHistory } from 'vue-router'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('../views/Home.vue') // 懒加载
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('../views/About.vue')
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

使用 v-memo (Vue 3.2+)

vue
<template>
  <div v-for="item in list" :key="item.id">
    <div v-memo="[item.id, item.selected]">
      <ExpensiveComponent :item="item" />
    </div>
  </div>
</template>

组合式函数 (Composables)

创建可复用逻辑

javascript
// composables/useCounter.js
import { ref, computed } from 'vue'

export function useCounter(initialValue = 0) {
  const count = ref(initialValue)
  
  const increment = () => {
    count.value++
  }
  
  const decrement = () => {
    count.value--
  }
  
  const reset = () => {
    count.value = initialValue
  }
  
  const isPositive = computed(() => count.value > 0)
  
  return {
    count,
    increment,
    decrement,
    reset,
    isPositive
  }
}

API 数据获取

javascript
// composables/useApi.js
import { ref, onMounted } from 'vue'

export function useApi(url) {
  const data = ref(null)
  const loading = ref(true)
  const error = ref(null)
  
  const fetchData = async () => {
    try {
      loading.value = true
      error.value = null
      
      const response = await fetch(url)
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`)
      }
      
      data.value = await response.json()
    } catch (err) {
      error.value = err.message
    } finally {
      loading.value = false
    }
  }
  
  onMounted(fetchData)
  
  return {
    data,
    loading,
    error,
    refetch: fetchData
  }
}

TypeScript 集成

类型安全的组件

vue
<script setup lang="ts">
interface User {
  id: number
  name: string
  email: string
}

interface Props {
  user: User
  editable?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  editable: false
})

interface Emits {
  (e: 'update:user', user: User): void
  (e: 'delete'): void
}

const emit = defineEmits<Emits>()

const updateUser = (updatedUser: User) => {
  emit('update:user', updatedUser)
}
</script>

测试策略

组件测试

javascript
// tests/components/UserCard.spec.js
import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import UserCard from '@/components/UserCard.vue'

describe('UserCard', () => {
  it('renders user information correctly', () => {
    const user = { id: 1, name: 'John Doe', email: 'john@example.com' }
    const wrapper = mount(UserCard, {
      props: { user }
    })
    
    expect(wrapper.text()).toContain('John Doe')
    expect(wrapper.text()).toContain('john@example.com')
  })
  
  it('emits update event when user is updated', async () => {
    const user = { id: 1, name: 'John Doe', email: 'john@example.com' }
    const wrapper = mount(UserCard, {
      props: { user }
    })
    
    await wrapper.find('.edit-button').trigger('click')
    
    expect(wrapper.emitted('update')).toBeTruthy()
  })
})

错误处理

全局错误处理

javascript
// main.js
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

// 全局错误处理
app.config.errorHandler = (err, instance, info) => {
  console.error('Global error:', err)
  console.error('Error Info:', info)
  
  // 发送错误到监控服务
  Sentry.captureException(err)
}

app.mount('#app')

组件级错误处理

vue
<script setup>
import { onErrorCaptured, ref } from 'vue'

const errorMessage = ref(null)

onErrorCaptured((error) => {
  errorMessage.value = error.message
  console.error('Component error:', error)
  
  // 返回 false 阻止错误继续冒泡
  return false
})
</script>

安全最佳实践

XSS 防护

vue
<template>
  <!-- Vue 会自动转义 HTML,但要注意以下情况 -->
  <div>{{ userContent }}</div>  <!-- 安全 -->
  
  <!-- 使用 v-html 时要格外小心 -->
  <div v-html="trustedContent"></div>  <!-- 潜在危险 -->
  
  <!-- 如果必须使用 HTML,先进行净化 -->
  <div v-html="sanitizedContent"></div>
</template>

<script setup>
import DOMPurify from 'dompurify'

const rawContent = '<script>alert("XSS")</script><p>Safe content</p>'
const sanitizedContent = DOMPurify.sanitize(rawContent)
</script>

部署优化

生产环境配置

javascript
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['vue', 'vue-router'],
          ui: ['element-plus', 'ant-design-vue']
        }
      }
    },
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true,
        drop_debugger: true
      }
    }
  }
})

遵循这些最佳实践可以帮助你构建更健壮、可维护和高性能的 Vue.js 应用程序。