Vue中文社区

Vue 3 的 18 个实用技巧

让你从 Vue 新手秒变老司机的实用秘籍!

1. Composition API 优雅组织代码

Before(选项式API - 代码分散):

exportdefault {
  data() {
return { count: 0, name: '' }
  },
methods: {
    increment() { this.count++ }
  },
computed: {
    doubleCount() { returnthis.count * 2 }
  },
  mounted() { console.log('组件挂载') }
}

After(组合式API - 逻辑集中):

import { ref, computed, onMounted } from'vue'

exportdefault {
  setup() {
const count = ref(0)
const name = ref('')

const doubleCount = computed(() => count.value * 2)

const increment = () => { count.value++ }

    onMounted(() => { console.log('组件挂载') })

return { count, name, doubleCount, increment }
  }
}

2. <script setup> 语法糖 - 真香!

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

// 变量自动暴露给模板
const count = ref(0)
const name = ref('Vue3')

// 函数自动暴露
const increment = () => { count.value++ }

// 计算属性自动暴露
const greeting = computed(() => `Hello, ${name.value}!`)

// 组件自动注册(无需components选项)
import MyComponent from './MyComponent.vue'
</script>

<template>
  <div>
    <MyComponent />
    <button @click="increment">{{ count }}</button>
    <p>{{ greeting }}</p>
  </div>
</template>

3. reactive 和 ref 的正确选择

import { ref, reactive } from'vue'

// ✅ ref 用于基本类型
const count = ref(0)
const name = ref('')

// ✅ reactive 用于对象
const user = reactive({
name: '张三',
age: 25,
address: {
city: '北京'
  }
})

// ❌ 不要这样用 reactive
const count = reactive({ value: 0 }) // 太啰嗦!

// ✅ 正确:解构 reactive 保持响应性
import { toRefs } from'vue'
const { name, age } = toRefs(user)

4. 计算属性的高级用法

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

const price = ref(100)
const quantity = ref(2)
const discount = ref(0.1)

// 可写的计算属性
const total = computed({
  get: () => price.value * quantity.value * (1 - discount.value),
  set: (value) => {
    price.value = value / quantity.value / (1 - discount.value)
  }
})

// 修改 total 会自动更新 price
const updatePrice = () => {
  total.value = 500 // 自动重新计算 price
}
</script>

5. Watch 的精准监听

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

const count = ref(0)
const user = reactive({ name: '张三', age: 25 })

// 1. 监听单个 ref
watch(count, (newVal, oldVal) => {
  console.log(`count从${oldVal}变为${newVal}`)
})

// 2. 监听多个值
watch([count, () => user.name], ([newCount, newName], [oldCount, oldName]) => {
  console.log('count或name变化了')
})

// 3. 深度监听对象
watch(user, (newUser) => {
  console.log('user对象变化了', newUser)
}, { deep: true })

// 4. watchEffect - 自动追踪依赖
watchEffect(() => {
  console.log(`count是${count.value}, name是${user.name}`)
  // 自动追踪 count 和 user.name 的变化
})
</script>

6. 组件通信的多种方式

Props + Emits(父子通信)

<!-- 子组件 -->
<script setup>
defineProps(['title', 'count'])
defineEmits(['update:title', 'count-change'])

const updateTitle = () => {
  // 触发事件更新父组件
  emit('update:title', '新标题')
}
</script>

<!-- 父组件 -->
<template>
  <ChildComponent 
    :title="pageTitle" 
    :count="pageCount"
    @update:title="pageTitle = $event"
    @count-change="handleCountChange"
  />
</template>

provide/inject(跨级通信)

<!-- 祖先组件 -->
<script setup>
import { provide, ref } from 'vue'

const theme = ref('dark')
const user = ref({ name: '张三' })

// 提供数据给所有后代组件
provide('theme', theme)
provide('user', user)
provide('updateTheme', (newTheme) => {
  theme.value = newTheme
})
</script>

<!-- 后代组件 -->
<script setup>
import { inject } from 'vue'

const theme = inject('theme')
const user = inject('user')
const updateTheme = inject('updateTheme')

// 修改主题
const toggleTheme = () => {
  updateTheme(theme.value === 'dark' ? 'light' : 'dark')
}
</script>

7. 自定义 Hooks - 逻辑复用神器

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

exportfunctionuseCounter(initialValue = 0) {
const count = ref(initialValue)

const increment = () => count.value++
const decrement = () => count.value--
const reset = () => count.value = initialValue
const double = computed(() => count.value * 2)

return { count, increment, decrement, reset, double }
}

// 在组件中使用
<script setup>
import { useCounter } from'@/hooks/useCounter'

const { count, increment, double } = useCounter(10)
</script>

8. 模板引用(Template Refs)技巧

<template>
  <input ref="inputRef" type="text">
  <div ref="divRef">内容</div>
  <ChildComponent ref="childRef" />

  <!-- 动态 ref -->
  <div v-for="item in list" :ref="setItemRef">
    {{ item }}
  </div>
</template>

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

// 1. 静态 ref
const inputRef = ref(null)
const divRef = ref(null)
const childRef = ref(null)

// 2. 动态 ref 数组
const itemRefs = ref([])
const setItemRef = el => {
  if (el) itemRefs.value.push(el)
}

onMounted(() => {
  // 组件挂载后可以访问 DOM 元素
  inputRef.value.focus()
  console.log(childRef.value) // 子组件实例
})
</script>

9. 条件渲染的性能优化

<template>
  <!-- ✅ 使用 v-show 频繁切换 -->
  <div v-show="isVisible">频繁显示隐藏的内容</div>

  <!-- ✅ 使用 v-if 初始条件 -->
  <div v-if="hasData">一次性判断的内容</div>

  <!-- ✅ 使用 Teleport 传送门 -->
  <Teleport to="body">
    <Modal v-if="showModal" />
  </Teleport>

  <!-- ✅ 使用 Suspense 异步组件 -->
  <Suspense>
    <template #default>
      <AsyncComponent />
    </template>
    <template #fallback>
      <div>加载中...</div>
    </template>
  </Suspense>
</template>

10. 样式和作用域技巧

<template>
  <div class="container" :class="{ active: isActive, 'text-error': hasError }">
    <p :style="{ color: textColor, fontSize: fontSize + 'px' }">动态样式</p>
  </div>
</template>

<style scoped>
/* 作用域样式 */
.container {
  padding: 20px;
}

/* 深度选择器 */
.container :deep(.child-element) {
  color: red;
}

/* 全局样式 */
.container :global(.global-class) {
  margin: 10px;
}

/* CSS 变量 */
.container {
  --primary-color: #42b983;
  color: var(--primary-color);
}
</style>

11. 事件处理的高级技巧

<template>
  <!-- 事件修饰符 -->
  <button @click.stop="handleClick">阻止冒泡</button>
  <form @submit.prevent="handleSubmit">阻止默认</form>
  <input @keyup.enter="submit" @keyup.esc="cancel">

  <!-- 多个事件 -->
  <div @click="handleClick" @mouseenter="handleMouseEnter"></div>

  <!-- 自定义事件 -->
  <MyComponent @custom-event="handleCustom" />
</template>

<script setup>
// 事件处理函数
const handleClick = (event) => {
  console.log('点击事件', event.target)
}

// 带参数的事件
const handleItemClick = (item, index, event) => {
  console.log('点击项目:', item, index)
}
</script>

12. 生命周期的最佳实践

<script setup>
import { onMounted, onUnmounted, onUpdated } from 'vue'

// 组件挂载
onMounted(() => {
  console.log('组件挂载完成')
  // 初始化操作、监听事件、获取数据
  window.addEventListener('resize', handleResize)
})

// 组件更新
onUpdated(() => {
  console.log('组件更新了')
  // DOM 更新后的操作
})

// 组件卸载
onUnmounted(() => {
  console.log('组件卸载')
  // 清理操作:移除事件监听、清除定时器
  window.removeEventListener('resize', handleResize)
})

// 错误处理
onErrorCaptured((error) => {
  console.error('捕获到错误:', error)
  return false // 阻止错误继续向上传播
})
</script>

13. 路由和状态管理技巧

<script setup>
import { useRoute, useRouter } from 'vue-router'
import { useStore } from 'vuex'

const route = useRoute()
const router = useRouter()
const store = useStore()

// 路由操作
const goToUser = (userId) => {
  router.push(`/user/${userId}`)
}

// 状态管理
const user = computed(() => store.state.user)

const updateUser = () => {
  store.dispatch('user/updateUser', { name: '新名字' })
}

// 监听路由变化
watch(() => route.params.id, (newId) => {
  if (newId) {
    loadUserData(newId)
  }
})
</script>

14. TypeScript 支持

<script setup lang="ts">
import { ref, computed } from 'vue'

// 类型定义
interface User {
  id: number
  name: string
  email: string
}

// 带类型的 ref
const user = ref<User>({
  id: 1,
  name: '张三',
  email: '[email protected]'
})

// 带类型的 props
interface Props {
  title: string
  count?: number
  items: User[]
}

const props = withDefaults(defineProps<Props>(), {
  count: 0
})

// 带类型的 emits
const emit = defineEmits<{
  (e: 'update:title', value: string): void
  (e: 'submit', data: User): void
}>()

// 带类型的计算属性
const userName = computed<string>(() => user.value.name.toUpperCase())
</script>

15. 性能优化技巧

<script setup>
import { shallowRef, markRaw } from 'vue'

// 1. 浅层响应式(不深度监听)
const largeObject = shallowRef({ /* 大数据对象 */ })

// 2. 标记非响应式对象
const heavyInstance = markRaw(new HeavyClass())

// 3. 懒加载组件
const HeavyComponent = defineAsyncComponent(() => 
  import('./HeavyComponent.vue')
)

// 4. 计算属性缓存
const expensiveValue = computed(() => {
  return heavyCalculation(props.data)
})

// 5. 防抖操作
import { debounce } from 'lodash-es'
const search = debounce((query) => {
  // 搜索逻辑
}, 300)
</script>

16. 错误边界和调试

<template>
  <ErrorBoundary>
    <Suspense>
      <ComponentThatMightError />
    </Suspense>
  </ErrorBoundary>
</template>

<script setup>
// 错误处理
const handleError = (error) => {
  console.error('组件错误:', error)
  // 上报错误到监控系统
}

// 开发环境调试
const debug = (message, ...args) => {
  if (import.meta.env.DEV) {
    console.log(`[DEBUG] ${message}`, ...args)
  }
}

// 使用调试函数
debug('组件状态:', { count, user })
</script>

17. 第三方库集成

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

// 图表库集成
const chartRef = ref(null)
let chartInstance = null

onMounted(() => {
  chartInstance = echarts.init(chartRef.value)
  chartInstance.setOption({
    // 图表配置
  })
})

onUnmounted(() => {
  chartInstance?.dispose()
})

// UI 库集成
import { ElMessage } from 'element-plus'

const showMessage = () => {
  ElMessage.success('操作成功!')
}

// 工具库集成
import { cloneDeep, throttle } from 'lodash-es'

const throttledSearch = throttle((query) => {
  // 搜索逻辑
}, 300)
</script>

18. 项目结构和最佳实践

src/
├── components/          # 公共组件
│   ├── base/           # 基础组件
│   └── business/       # 业务组件
├── views/              # 页面组件
├── composables/        # 组合式函数
├── stores/             # 状态管理
├── utils/              # 工具函数
├── types/              # 类型定义
└── assets/             # 静态资源

最佳实践总结:

  1. 使用 <script setup> 语法
  2. 合理选择 ref 和 reactive
  3. 逻辑复用使用自定义 Hooks
  4. 类型安全使用 TypeScript
  5. 性能优化使用懒加载和浅层响应
  6. 错误处理使用错误边界
  7. 保持组件单一职责

掌握这 18 个技巧,你的 Vue 3 开发水平将大幅提升!🎯