🔗 Vue3 开发10个超实用技巧
🔗 shallowRef与shallowReactive——响应式的“轻量卫士”
处理大型数据结构时,有没有被页面卡顿折磨到崩溃?明明只是改了一个小数据,整个组件却像疯了一样疯狂重新渲染,浏览器直接“卡死”。这是因为Vue3默认的深度响应式,在数据量大的时候,就像一头横冲直撞的“蛮牛”,不管三七二十一,把所有数据都检查一遍,性能全浪费在这无用功上了!
别慌!shallowRef和shallowReactive就像两位“轻量卫士”,专门来救场。shallowRef是个“一根筋”,只专注基本数据类型,只有在你直接赋值的时候,它才“警觉”起来;shallowReactive则是“表面派”,只监听对象的顶层属性变化。
举个例子,去年我们团队做一个企业资源管理系统,里面有个表格展示上万条员工数据。一开始用常规响应式,每次筛选数据,页面都要卡好几秒。
后来用shallowReactive处理数据,页面流畅度直接提升30%!这效果,谁用谁知道!
// 引入shallowRef和shallowReactive函数
import { shallowRef, shallowReactive } from 'vue';
// 创建一个shallowRef,存储一个数字(仅对直接赋值响应)
const simpleCount = shallowRef(0);
// 修改simpleCount的值,触发响应
simpleCount.value = 1;
// 创建一个复杂对象
const user = {
name: 'Alice',
address: {
city: 'Beijing',
street: 'Xinhua Road'
}
};
// 使用shallowReactive将对象变成浅层响应式
const shallowUser = shallowReactive(user);
// 修改顶层属性,触发响应
shallowUser.name = 'Bob';
// 修改嵌套属性,不会触发响应(需手动处理)
// shallowUser.address.city = 'Shanghai';
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
为啥它们这么厉害?关键就在于减少了不必要的深层监听。这可是“Vue3性能优化”的必备大招,以后遇到大数据场景,记得把这两位“卫士”请出来!
🔗 toRef与toRefs——数据解包的“智能快递员”
从响应式对象里取数据,是不是感觉像在干苦力活?写一堆 const name = state.name
,又枯燥又容易出错,稍不注意,响应式还可能“失灵”。别愁!toRef
和 toRefs
就是我们的“智能快递员”,不仅能帮我们轻松解包数据,还能牢牢守住响应式的“阵地”!toRef
是个“独行侠快递员”,一次只专注提取一个属性;toRefs
则是“团队作战高手”,多个属性打包一起送。有了它们,数据提取变得超轻松!
// 引入reactive、toRef和toRefs函数
import { reactive, toRef, toRefs } from 'vue';
// 创建一个响应式对象
const product = reactive({
title: 'Vue3实战指南',
price: 99,
stock: 100
});
// 使用toRef提取单个属性,保持响应式
const productTitle = toRef(product, 'title');
productTitle.value = 'Vue3进阶秘籍'; // 修改title,product对象中的title也会改变
// 使用toRefs提取多个属性
const { price, stock } = toRefs(product);
price.value = 89; // 修改price,product对象中的price同步更新
stock.value = 80; // 修改stock,product对象中的stock同步更新
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
说实话,自从用了这俩“快递员”,在“Vue3数据处理”这块,代码量少了一大截,同事看了都夸我代码写得又简洁又专业!
🔗 watchPostEffect——数据监听的“后知后觉者”
用 watch
监听数据变化时,有没有遇到过这样的尴尬?回调函数跑得比数据准备好的速度还快,导致各种“玄学”问题出现,debug都不知道从哪下手。别担心!watchPostEffect
就像一个“后知后觉者”,专门等组件更新完成、DOM渲染得明明白白后,再慢悠悠地执行回调,把这些尴尬统统解决!
// 引入ref和watchPostEffect函数
import { ref, watchPostEffect } from 'vue';
const count = ref(0);
// 使用watchPostEffect监听count变化
watchPostEffect(() => {
// 这里的代码会在组件更新完成后执行
console.log(`页面已更新,count的值为: ${count.value}`);
});
count.value++; // 触发count变化
2
3
4
5
6
7
8
9
10
11
在处理依赖DOM状态的操作时,比如动态计算元素位置、初始化第三方插件,watchPostEffect简直就是“救星”。它能精准把握时机,确保代码在最合适的时候执行。这可是“Vue3响应式编程”里的进阶技巧,学会它,你就是团队里的技术大牛!
🔗 v-for的key——列表渲染的“身份标识官”
用 v-for
渲染列表,要是不设置key,那麻烦可就大了!数据错乱、动画卡顿,页面效果就像“车祸现场”。key就像列表项的“身份标识官”,有了它,Vue就能快速分辨每个列表项,渲染效率直线上升!
<template>
<ul>
<!-- 为每个列表项设置唯一的key -->
<li v-for="item in userList" :key="item.id">
{{ item.name }} - {{ item.age }}
</li>
</ul>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
// 定义用户列表数据
const userList = ref([
{ id: 1, name: 'Tom', age: 25 },
{ id: 2, name: 'Jerry', age: 23 }
]);
return {
userList
};
}
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
当列表数据更新时,Vue会拿着key去比对,快速判断哪些项是新增的、哪些修改了、哪些该删除。要是没有key,Vue就只能像个“笨学生”,一项一项慢慢对比,效率低得不行。所以说,在“Vue3列表渲染”中,给 v-for
设置key,这是经过无数项目验证的最佳实践,千万不能忘!
🔗 自定义Hooks——代码复用的“魔法工厂”
项目里重复的逻辑到处都是,表单验证写了一遍又一遍,数据请求代码复制粘贴无数次,既浪费时间又容易出错,每次写都感觉在“坐牢”。别怕!自定义Hooks就像一个“魔法工厂”,把这些重复逻辑统统加工成可复用的“魔法道具”,下次要用直接拿出来就行!
拿表单输入和验证来说,我们来打造一个专属的自定义Hooks:
// 引入ref和watch函数
import { ref, watch } from 'vue';
// 自定义表单处理Hooks
function useFormInput() {
// 存储输入框的值
const inputValue = ref('');
// 存储验证错误信息
const errorMessage = ref('');
// 验证函数
const validate = () => {
if (inputValue.value.trim() === '') {
errorMessage.value = '输入内容不能为空';
return false;
}
errorMessage.value = '';
return true;
};
// 监听输入框值的变化,实时验证
watch(inputValue, () => {
validate();
});
// 返回相关数据和方法
return {
inputValue,
errorMessage,
validate
};
}
export default useFormInput;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
在组件里用这个Hooks,方便得很:
<template>
<div>
<input v-model="form.inputValue" placeholder="请输入内容">
<p v-if="form.errorMessage" class="error">{{ form.errorMessage }}</p>
<button @click="submitForm" :disabled="!form.validate()">提交</button>
</div>
</template>
<script>
import useFormInput from './useFormInput';
export default {
setup() {
// 使用自定义Hooks
const form = useFormInput();
const submitForm = () => {
if (form.validate()) {
console.log('表单验证通过,提交数据');
}
};
return {
form,
submitForm
};
}
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
有了自定义Hooks,“Vue3代码复用”不再是梦,开发效率直接起飞!不过要特别警惕,封装时一定要保证逻辑通用,别把Hooks搞得太复杂,不然反而会给自己挖坑。
🔗 v-cloak——页面闪烁的“隐形斗篷”
项目刚加载那一瞬间,有没有被 插值闪烁吓到?用户还没看到内容,先瞅见一堆大括号在屏幕上“跳舞”,体验感直接“归零”。别慌!v - cloak就是那件能拯救局面的“隐形斗篷”!
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>v-cloak示例</title>
<style>
/* 隐藏带有v-cloak属性的元素 */
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cloak>
{{ message }}
</div>
<script type="module">
import { createApp } from 'vue';
const app = createApp({
data() {
return {
message: '欢迎使用Vue3!'
};
}
});
app.mount('#app');
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
在Vue实例挂载完成之前,带着 v-cloak
的元素就像被施了隐身咒,藏得严严实实。等数据渲染好,v-cloak
自动“消失”,内容优雅登场。这操作,是不是超神奇?这可是“Vue3项目优化”里提升用户体验的小宝藏技巧,赶紧码住!
🔗 v-slot ——组件插槽的“内容调配师”
在组件开发过程中,想灵活传递和展示内容?v - slot就像一位经验丰富的“内容调配师”,不管是具名插槽还是作用域插槽,它都能轻松拿捏!
先看看具名插槽:
<template>
<MyComponent>
<!-- 使用具名插槽,定义头部内容 -->
<template v-slot:header>
<h2>这是组件头部</h2>
</template>
<!-- 使用具名插槽,定义默认内容 -->
<template v-slot:default>
<p>这是组件主体内容</p>
</template>
</MyComponent>
</template>
<script>
import MyComponent from './MyComponent.vue';
import { defineComponent } from 'vue';
export default defineComponent({
components: {
MyComponent
}
});
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- MyComponent.vue -->
<template>
<div>
<!-- 定义头部插槽 -->
<slot name="header"></slot>
<!-- 定义默认插槽 -->
<slot></slot>
</div>
</template>
<script>
import { defineComponent } from 'vue';
export default defineComponent({});
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
再瞧瞧作用域插槽:
<template>
<MyList>
<!-- 作用域插槽,获取子组件传递的数据 -->
<template v-slot:item="scope">
<div>{{ scope.item.name }}</div>
</template>
</MyList>
</template>
<script>
import MyList from './MyList.vue';
import { defineComponent } from 'vue';
export default defineComponent({
components: {
MyList
}
});
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- MyList.vue -->
<template>
<ul>
<li v-for="item in list" :key="item.id">
<!-- 向作用域插槽传递数据 -->
<slot name="item" :item="item"></slot>
</li>
</ul>
</template>
<script>
import { defineComponent, ref } from 'vue';
export default defineComponent({
setup() {
const list = ref([
{ id: 1, name: '苹果' },
{ id: 2, name: '香蕉' }
]);
return {
list
};
}
});
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
有了 v-slot
,组件间内容调配变得随心所欲,这可是“Vue3组件开发”的核心技能,学会它,组件开发对你来说就是“小菜一碟”!
🔗 v-model的修饰符——表单输入的“贴心助手”
处理表单输入时,是不是经常被一些小需求搞得焦头烂额?想把输入转成数字、去掉首尾空格,或者延迟更新数据,自己写代码又麻烦又容易出错。别担心!v-model
的修饰符就是我们的“贴心助手”,分分钟搞定这些需求!
<template>
<div>
<!-- 使用.number修饰符,将输入值转为数字 -->
<input v-model.number="age" type="text">
<!-- 使用.trim修饰符,自动去除输入值首尾空格 -->
<input v-model.trim="username" type="text">
<!-- 使用.lazy修饰符,在失去焦点或回车时更新数据 -->
<input v-model.lazy="message" type="text">
</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const age = ref(0);
const username = ref('');
const message = ref('');
return {
age,
username,
message
};
}
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
这些修饰符让 v-model
在“Vue3表单开发”中如虎添翼,不管什么奇葩需求,都能轻松应对,妥妥的表单开发必备神器!
🔗 keep-alive——组件缓存的“记忆仓库”
有些组件频繁切换时,重新渲染就像在“烧性能”,页面卡得让人抓狂。比如tabs页面、导航菜单里的组件,每次切换都要重新加载,用户体验差到极点。别着急!keep-alive
就像一个“记忆仓库”,帮我们把组件实例缓存起来,告别重复渲染!
<template>
<div>
<button @click="activeTab = 'tab1'">标签1</button>
<button @click="activeTab = 'tab2'">标签2</button>
<!-- 使用keep-alive缓存组件 -->
<keep-alive>
<component :is="activeTab"></component>
</keep-alive>
</div>
</template>
<script>
import Tab1 from './Tab1.vue';
import Tab2 from './Tab2.vue';
import { ref } from 'vue';
export default {
components: {
Tab1,
Tab2
},
setup() {
const activeTab = ref('tab1');
return {
activeTab
};
}
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
被 keep-alive
照顾的组件,切换时不会被销毁,而是把当前状态好好存进“仓库”。等再次切换回来,直接“取货”展示,性能优化效果显著,“Vue3性能优化”就靠它撑场子了!
🔗 Web Workers——多线程处理的“幕后帮手”
碰上复杂计算任务,比如大数据排序、加密操作,是不是担心阻塞主线程,让页面直接“卡死”?Web Workers就像一位“幕后帮手”,在后台默默处理这些苦差事,不影响主线程正常工作,保证页面流畅!
在Vue3项目里用上Web Workers:
// worker.js
self.onmessage = function (e) {
console.log('接收到主线程的消息:', e.data);
// 模拟一个耗时任务(比如计算1到1000000的和)
let result = 0;
for (let i = 1; i <= 1000000; i++) {
result += i;
}
// 向主线程发送处理结果
self.postMessage(`任务处理完成,结果是: ${result}`);
};
2
3
4
5
6
7
8
9
10
11
通过使用 Web Workers,能让我们能实现 “Vue3 多线程处理”,让复杂任务在后台运行,保证页面的流畅性,是解决性能问题的终极杀器!
🔗 watchEffect:数据依赖的“省心小助手”
写代码时最烦的就是手动盯着数据变化,稍不留神就漏了依赖项,debug到怀疑人生。这时候watchEffect就像个贴心小助手,不用你动手指定依赖,自己就能把数据变化盯得死死的。
举个做购物车总价的例子,单价和数量一变就得重新计算总价,用watchEffect一行代码搞定:
// 引入响应式工具和watchEffect
import { ref, watchEffect } from 'vue';
// 定义商品价格和数量
const price = ref(10);
const quantity = ref(2);
// 自动监听价格或数量变化,实时计算总价
watchEffect(() => {
console.log('总价更新为:', price.value * quantity.value);
});
// 模拟价格变化,自动触发计算
price.value = 15; // 控制台输出:总价更新为:30
2
3
4
5
6
7
8
9
10
11
12
13
14
它为啥这么智能?关键在于能自动收集回调里用到的数据,只要price或quantity变了,马上重新执行。去年做电商项目时靠它省了一半代码量,现在写数据联动再也不用手忙脚乱了!想知道怎么用它处理更复杂的场景吗?接着往下看。
🔗 Teleport:组件布局的“空间传送门”
做弹窗时最头疼样式层级问题,不管怎么调z-index,弹窗永远被其他组件盖住,像个受气包。Teleport就像个传送门,直接把组件“扔”到指定位置,比如body底下,彻底摆脱层级限制。
<template>
<button @click="showModal = true">打开弹窗</button>
<!-- 把弹窗传送到body层级,再也不怕被遮挡 -->
<Teleport to="body">
<div v-if="showModal" class="modal">
<p>这是顶层弹窗</p>
<button @click="showModal = false">关闭</button>
</div>
</Teleport>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
// 控制弹窗显示的响应式变量
const showModal = ref(false);
return { showModal };
}
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
神奇的是,组件传送后响应式完全不受影响,生命周期也正常执行。注意传送目标得是存在的DOM节点,不然会报错哦。现在做后台管理系统的全局通知,我都用这招,再也不用和样式层级较劲了。
🔗 Pinia:状态管理的“轻量管家”
项目一复杂,组件间传数据就像接力赛,用Vuex又觉得太重?试试Pinia,轻量又好用,就像个精明的管家,把用户登录状态、全局配置这些数据管得明明白白。
// 定义用户状态仓库
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
state: () => ({
isLogin: false, // 登录状态
userInfo: null // 用户信息
}),
actions: {
// 登录方法
login(user) {
this.isLogin = true;
this.userInfo = user;
}
}
});
// 组件里使用
const userStore = useUserStore();
userStore.login({ name: '小明' });
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
它支持插件扩展和时间旅行调试,比Vuex简单太多。现在团队新项目统一用Pinia,状态逻辑清晰多了,新人也能快速上手。你更喜欢用Pinia还是Vuex?评论区聊聊你的体验。
🔗 v-memo:列表渲染的“偷懒高手”
渲染几千条数据时,页面卡得像蜗牛,每次更新都全量渲染,性能全浪费了。v-memo就像个偷懒高手,帮你记住哪些列表项不用重新渲染,只更新有变化的部分。
<template>
<ul>
<!-- 只有id或title变化时才重新渲染,其他情况直接复用 -->
<li v-for="item in list" :key="item.id" v-memo="[item.id, item.title]">
<h3>{{ item.title }}</h3>
<p>{{ item.content }}</p>
</li>
</ul>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const list = ref([
{ id: 1, title: '文章1', content: '内容1' }
]);
return { list };
}
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
用的时候注意依赖项别选太多,不然反而影响性能。之前做表格组件时,用v-memo把渲染时间缩短了40%,用户滑动再也不卡顿了。想知道怎么搭配key优化吗?后面还有干货。
🔗 自定义指令:重复逻辑的“万能模板”
按钮防抖、输入框自动聚焦这些功能,每次都要写重复代码,浪费时间还容易出错。自定义指令就像个万能模板,把这些逻辑封装起来,一劳永逸。
// 注册防抖指令,300ms内重复点击只执行一次
app.directive('debounce', {
mounted(el, binding) {
let timer;
el.addEventListener('click', () => {
clearTimeout(timer);
timer = setTimeout(() => binding.value(), 300);
});
}
});
// 使用时只需一行代码
<button v-debounce @click="fetchData">搜索</button>
2
3
4
5
6
7
8
9
10
11
12
13
现在写表单提交、搜索框防抖,我都直接用自定义指令,团队代码统一了,维护也方便。你平时喜欢封装哪些自定义指令?评论区分享你的经验。
🔗 provide/inject:跨层级传值的“隐形传送带”
组件嵌套三四层时,用props传值像爬楼梯,层层传递麻烦死了。provide/inject就像隐形传送带,直接把数据从顶层传到深层组件,不用中间组件接力。
// 顶层组件提供数据
provide('theme', ref('light'));
// 深层组件注入数据
const theme = inject('theme');
2
3
4
5
注意别滥用,不然数据流向会乱。之前做多语言切换功能,用这招轻松实现全局主题共享,再也不用在中间组件写一堆props了。
🔗 Suspense:异步加载的“优雅缓冲带”
加载异步组件时页面突然白屏,用户体验极差。Suspense就像个缓冲带,加载时显示loading,加载完再切换内容,过渡自然不突兀。
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<div class="loading">努力加载中...</div>
</template>
</Suspense>
2
3
4
5
6
7
8
现在做图片画廊、动态组件加载,我都会用Suspense,用户再也不会看到白屏了。
🔗 readonly:数据安全的“电子围栏”
团队协作时最怕手滑改坏全局配置,readonly就像电子围栏,把重要数据保护起来,只能看不能改。
const config = readonly(reactive({
apiUrl: 'https://api.com'
}));
config.apiUrl = 'https://new.com'; // 报错!不能修改
2
3
4
🔗 useAsyncData:异步请求的“全自动助手”
处理异步数据时,加载状态、错误处理写得人麻了。useAsyncData全自动处理,一行代码搞定数据请求和状态管理。
const { data, error, isLoading } = useAsyncData('fetchNews', () =>
fetch('https://newsapi.com').then(res => res.json())
);
2
3
🔗 自定义Hooks:代码复用的“乐高积木”
表单验证、文件上传这些逻辑,每次都重写?自定义Hooks像乐高积木,把通用逻辑封装,哪里需要拼哪里。
function useForm() {
const value = ref('');
const error = ref('');
const validate = () => {
if (!value.value) error.value = '不能为空';
else error.value = '';
};
return { value, error, validate };
}
2
3
4
5
6
7
8
9