Appearance
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 应用程序。