干货来袭!掌握这 10 个 Vue3 编程技巧,开发效率飙升

在前端开发项目中,Vue 3 凭借其卓越的性能和丰富的特性,成为众多前端开发者的首选框架。它不仅在响应式原理上进行了深度优化,还引入了诸多实用新特性,如 setup 语法糖、Teleport 等,极大地提升了开发效率与代码可维护性。同时,Vue 3 对 TypeScript 的完美支持,让类型安全在开发过程中得到有力保障。本文将深入剖析 Vue3 的十大核心技巧,涵盖响应式控制、组件通信、性能优化等多个方面,帮助开发者全面掌握 Vue3 开发技巧,在实际项目中编写出更高效、更健壮的代码。
一、巧用 ref 与 reactive,精准控制响应式
Vue 3 提供了ref和reactive两种响应式方案,避免滥用能减少性能损耗。基础类型(字符串、数字等)优先用ref,通过.value访问 / 修改,配合unref简化取值(unref(val)等价于val.value ?? val);复杂对象 / 数组用reactive,无需手动解包,但注意避免直接替换整个对象(会丢失响应式),可通过Object.assign或解构更新属性。
<template>
<div>
<p>ref 基础类型:{{ count }}</p>
<p>reactive 复杂对象:{{ user.name }} - {{ user.age }}</p>
<button @click="updateData">更新数据</button>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, unref } from 'vue'
// 基础类型用 ref
const count = ref<number>(0)
// 复杂对象用 reactive
const user = reactive<{ name: string; age: number; hobbies: string[] }>({
name: '张三',
age: 20,
hobbies: ['篮球', '游戏']
})
const updateData = () => {
// ref 需.value 修改,unref 简化取值(count.value → unref(count))
count.value = unref(count) + 10
// reactive 直接修改属性,不丢失响应式
user.age += 1
// 禁止直接替换 reactive 整个对象!如需更新用 Object.assign
// user = { ...user }
Object.assign(user, { name: '朱大明' })
}
</script>
二、用 toRefs 保持解构响应式
解构reactive对象时,直接解构会丢失响应式,此时 toRefs 能派上用场。例如:
<template>
<div>
<p>姓名:{{ name }}</p>
<p>年龄:{{ age }}</p>
<button @click="updateName">修改姓名</button>
</div>
</template>
<script setup lang="ts">
import { reactive, toRefs, toRef } from 'vue'
const user = reactive<{ name: string; age: number; gender?: string }>({
name: '李四',
age: 25
})
// toRefs 解构 reactive,所有属性转为响应式 ref
const { name, age } = toRefs(user)
// toRef 处理可选属性(避免解构 undefined)
const gender = toRef(user, 'gender')
const updateName = () => {
// 解构后仍需.value 修改(因为是 ref)
name.value = '李小四'
gender.value = '男' // 给可选属性赋值
}
</script>
适合在组件中拆分复杂响应式对象,让代码更清晰。
三、watch 与 watchEffect 按需选择
watch:精准监听指定数据源(ref、reactive属性或getter函数),支持深度监听(deep: true)和立即执行(immediate: true),适合需要明确依赖的场景。watchEffect:自动追踪依赖,初始化时立即执行,无需手动指定监听目标,适合 “副作用与依赖紧密关联” 的场景(如数据变化后请求接口),代码更简洁。
<template>
<div>
<input v-model="keyword" placeholder="输入搜索关键词" />
<p>watch 监听结果:{{ watchResult }}</p>
<p>watchEffect 请求状态:{{ reqStatus }}</p>
</div>
</template>
<script setup lang="ts">
import { ref, watch, watchEffect } from 'vue'
const keyword = ref<string>('')
const watchResult = ref<string>('')
const reqStatus = ref<string>('未请求')
// watch:精准监听,可获取新旧值,支持配置项
watch(
keyword, // 监听的源(ref/reactive/getter)
(newVal, oldVal) => {
if (newVal) watchResult.value = `从${oldVal}改为${newVal}`
else watchResult.value = ''
},
{ immediate: false, deep: false } // 立即执行/深度监听
)
// watchEffect:自动追踪依赖,初始化立即执行,适合接口请求等副作用
const stopWatch = watchEffect(() => {
if (keyword.value) {
reqStatus.value = '请求中...'
// 模拟接口请求
setTimeout(() => {
reqStatus.value = '请求成功'
}, 500)
} else {
reqStatus.value = '未请求'
}})
// 组件卸载前停止监听(可选,watchEffect 会自动停止,手动停止适合特殊场景)
// import { onUnmounted } from 'vue'
// onUnmounted(() => stopWatch())
</script>
四、setup 语法糖简化代码
Vue3.2+ 支持语法糖,无需手动导出组件、声明props和emits,大幅减少模板代码:
<template>
<div>
<p>setup 语法糖:{{ msg }}</p>
<button @click="sayHello">点击触发</button>
</div>
</template>
<!-- setup 语法糖核心:无需配置 setup 函数,无需 return -->
<script setup lang="ts">
// 直接定义变量,模板可直接访问
const msg = ref<string>('Vue3 setup 语法糖超简洁!')
// 直接定义方法,模板可直接调用
const sayHello = () => {
alert('setup 语法糖 yyds')
}
// 后续技巧会结合 defineProps/defineEmits,此处体现核心简化点
import { ref } from 'vue'
</script>
配合defineProps、defineEmits等宏,开发效率翻倍。
五、组件通信首选 defineProps/defineEmits
父子组件通信时,摒弃 Vue 2 的props/$emit写法,用中的defineProps声明接收参数,defineEmits声明触发事件,类型校验更清晰:
子组件 Child.vue
<template>
<div class="child">
<p>父组件传值:{{ name }}</p>
<button @click="sendMsgToParent">向父组件传值</button>
</div>
</template>
<script setup lang="ts">
// 声明接收父组件的 props,TS 泛型做类型约束
const props = defineProps<{
name: string; // 必传属性
age?: number; // 可选属性
}>()
// 声明可触发的事件,TS 泛型约束事件参数类型
const emit = defineEmits<{
(e: 'update:age', value: number): void; // 自定义更新事件
(e: 'message', msg: string): void; // 普通消息事件
}>()
// 触发事件,向父组件传值
const sendMsgToParent = () => {
emit('update:age', 18)
emit('message', '我是子组件的消息')
}
</script>
父组件 Parent.vue
<template>
<div class="parent">
<h3>父组件</h3>
<!-- 向子组件传值 + 监听子组件事件 -->
<Child
name="张三"
@update:age="handleAgeUpdate"
@message="handleMessage"
/> <p>子组件传回的年龄:{{ childAge }}</p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Child from './Child.vue'
const childAge = ref<number>(0)// 处理子组件的年龄更新事件
const handleAgeUpdate = (val: number) => {
childAge.value = val
}
// 处理子组件的消息事件
const handleMessage = (msg: string) => {
console.log('接收子组件消息:', msg)
}
</script>
六、computed 缓存复杂计算逻辑
对于需要重复计算的值,用 computed 包裹,Vue 会缓存计算结果,仅当依赖变化时重新计算,避免无效渲染:
<template>
<div>
<div v-for="item in goodsList" :key="item.id">
<p>{{ item.name }}:{{ item.price }}元 × {{ item.num }}件</p>
</div>
<h3>合计:{{ totalPrice }}元</h3>
<button @click="addNum">增加第一件商品数量</button>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
// 模拟商品列表
type Goods = { id: number; name: string; price: number; num: number }
const goodsList = ref<Goods[]>([
{ id: 1, name: 'Vue 实战', price: 59, num: 1 },
{ id: 2, name: 'TS 入门', price: 39, num: 2 },
{ id: 3, name: 'React 基础', price: 49, num: 1}
])
// computed 缓存计算结果,仅当 goodsList 中 price/num 变化时重新计算
const totalPrice = computed<number>(() => {
console.log('computed 重新计算了') // 测试:仅依赖变化时打印
return goodsList.value.reduce((sum, item) => {
return sum + item.price * item.num
}, 0)
})
// 修改依赖,触发 computed 重新计算
const addNum = () => {
goodsList.value[0].num++
}
</script>
七、onMounted 等生命周期钩子按需使用
Vue3 生命周期钩子需从 vue 导入,且在setup或setup>中使用,无需像 Vue2 那样挂载到this上:
<template>
<div>
<p>定时器计数:{{ timerCount }}</p>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, onUpdated } from 'vue'
const timerCount = ref<number>(0)
let timer: NodeJS.Timeout | null = null
// 组件挂载后执行:开启定时器(替代 Vue2 的 mounted)
onMounted(() => {
console.log('组件挂载完成')
timer = setInterval(() => {
timerCount.value++ },
1000)
})
// 组件更新后执行(替代 Vue2 的 updated)
onUpdated(() => {
console.log('组件更新了:', timerCount.value)})
// 组件卸载前执行:清理定时器(关键!避免内存泄漏)
onUnmounted(() => {
console.log('组件即将卸载')
if (timer) clearInterval(timer)
})
</script>
八、v-memo 优化列表渲染性能
对于长列表或复杂组件,用 v-memo 缓存组件渲染结果,仅当依赖数据变化时才重新渲染,减少 DOM 操作:
<template>
<div>
<!-- 长列表渲染:v-memo 缓存,仅 id/status 变化时重新渲染 -->
<div
v-for="item in longList"
:key="item.id"
v-memo="[item.id, item.status]" <!-- 依赖数组:核心优化点 -->
>
<p>编号:{{ item.id }} - 名称:{{ item.name }}</p>
<p>状态:{{ item.status === 'active' ? '已激活' : '未激活' }}</p>
<p>无关内容:{{ item.content }}</p> <!-- 此属性变化不会触发渲染 -->
</div>
<button @click="changeContent">修改无关内容(不渲染)</button>
<button @click="changeStatus">修改状态(触发渲染)</button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
// 模拟 1000 条长列表数据
type ListItem = { id: number; name: string; status: 'active' | 'inactive'; content: string }
const longList = ref<ListItem[]>(
Array.from({ length: 1000 }, (_, i) => ({
id: i + 1,
name: `列表项${i + 1}`,
status: i % 2 === 0 ? 'active' : 'inactive',
content: `初始内容${i + 1}`
}))
)
// 修改 v-memo 依赖外的属性:不会触发组件重新渲染
const changeContent = () => {
longList.value.forEach(item => {
item.content = `修改后的内容${item.id}`
})
}
// 修改 v-memo 依赖内的属性:仅对应项重新渲染
const changeStatus = () => {
longList.value[0].status = longList.value[0].status === 'active' ? 'inactive' : 'active'
}
</script>
v-memo接收数组参数,仅当数组内数据变化时,组件才会重新渲染。
九、Teleport 实现组件 “跨 DOM” 渲染
需要将组件渲染到指定 DOM 节点(如,用 Teleport` 包裹,无需手动操作 DOM,适配弹窗、通知等场景:
<template>
<div>
<button @click="showModal = true">打开弹窗</button>
<!-- Teleport:将内容渲染到 body 节点下 -->
<Teleport to="body">
<div class="modal-mask" v-if="showModal" @click="showModal = false">
<div class="modal-content" @click.stop>
<h3>跨 DOM 弹窗</h3>
<p>我被渲染到 body 下,脱离父组件 DOM 结构</p>
<button @click="showModal = false">关闭</button>
</div>
</div>
</Teleport>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const showModal = ref<boolean>(false)
</script>
<style scoped>
/* 弹窗样式:避免父组件样式隔离影响 */
.modal-mask {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.modal-content {
width: 400px;
padding: 20px;
background: #fff;
border-radius: 8px;
}
</style>
十、用 TypeScript 增强类型校验
Vue3 对 TypeScript 支持更完善,ts可直接编写 TS 代码,通过泛型为 ref、reactive、props 等添加类型约束,提前规避类型错误:
<template>
<div>
<p>TS 约束 ref:{{ count }}(仅能为数字)</p>
<p>TS 约束 reactive:{{ user.name }} - {{ user.age }}(类型严格校验)</p>
<button @click="testType">测试类型约束</button>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue'
// 1. ref 添加 TS 类型约束(基础类型)
const count = ref<number>(0)
// 2. 接口约束复杂对象(更易复用)
interface User {
name: string;
age: number;
hobbies?: string[]; // 可选数组
getInfo: () => string; // 方法类型
}
// reactive 添加 TS 类型约束(复杂对象)
const user = reactive<User>({
name: '王小明',
age: 22,
hobbies: ['跑步', '阅读'],
getInfo: () => {
return `${user.name},${user.age}岁`
}
})
// 3. 泛型约束联合类型
const status = ref<'active' | 'inactive' | 'pending'>('active')
// 4. 类型断言(确定值的类型时使用)
const num = ref<number | string>(123)
const numOnly = num.value as number
// 测试类型约束
const testType = () => {
count.value += 1
user.age += 1
console.log(user.getInfo())
status.value = 'pending'
}
</script>
结语
掌握 Vue 3 的这些核心技巧,能让大家在前端开发项目过程中更加得心应手。从精准控制响应式数据,到巧妙运用组件通信方式;从利用计算属性缓存复杂逻辑,到借助生命周期钩子管理组件状态;再到通过v-memo优化性能、用Teleport实现特殊渲染,以及利用 TypeScript 增强类型校验,每一个技巧都蕴含着提升开发质量与效率的智慧。希望开发者们积极实践这些技巧,不断探索 Vue 3 的更多可能性,打造出更优质的前端应用。
以上关于干货来袭!掌握这 10 个 Vue3 编程技巧,开发效率飙升的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » 干货来袭!掌握这 10 个 Vue3 编程技巧,开发效率飙升
微信
支付宝