2025年 Vue 开发必备的25个超实用技巧!
本文来分享 25 个 Vue 开发必备的实用技巧!
文章目录
- 使用 defineModel() 实现双向数据绑定
- 避免滥用 ref()
- 使用 v-bind 同名简写
- 使用 shallowRef 优化性能
- 类型化组件 emits
- 在 <style> 中使用 v-bind
- 使用 Tanstack Query 简化数据获取
- 使用 :global 伪类应用全局样式
- 使用 withDefaults 设置默认值
- 自定义指令
- 使用 :deep() 伪类影响子组件样式
- 使用 :slotted 伪类针对插槽内容
- 使用 <KeepAlive> 缓存组件状态
- 传递多个具名插槽
- 使用 Suspense 处理异步依赖
- 使用 Teleport 传送模板部分
- 启用性能追踪
- 动态渲染组件
- 布尔类型 prop 的简写
- 使用 v-model 修饰符
- 自动类型转换
- 自动修剪空格
- 使用 defineExpose 暴露属性
- 合并类和样式
- 启用自定义格式化器
使用 defineModel()
实现双向数据绑定
在 Vue 3.4 中,推荐使用 defineModel()
宏来实现双向数据绑定,这大大简化了代码。
<!-- 使用 defineModel 之前 -->
<script setup>
const props = defineProps(["modelValue"]);
const emit = defineEmits(["update:modelValue"]);
</script>
<template>
<input :value="props.modelValue" @input="emit('update:modelValue', $event.target.value)" />
</template>
<!-- 使用 defineModel 之后 -->
<script setup>
const model = defineModel();
</script>
<template>
<input v-model="model" />
</template>
避免滥用 ref()
在 Vue 中,不需要为每个变量都使用 ref()
,只有在需要响应性时才使用。
<script setup>
// 不需要响应性,不需要使用 ref()
const links = [
{
name: "about",
href: "/about"
},
{
name: "terms of service",
href: "/tos"
},
{
name: "contact us",
href: "/contact"
}
];
// 需要响应性,使用 ref()
const tabs = ref([
{
name: "Privacy",
url: "/privacy",
isActive: true
},
{
name: "Permissions",
url: "/permissions",
isActive: false
}
]);
</script>
使用 v-bind
同名简写
Vue 3.4 引入了 v-bind
同名简写,进一步简化了代码。
<template>
<!-- 之前 -->
<img :id="id" :src="src" :alt="alt" />
<!-- 现在 -->
<img :id :src :alt />
</template>
使用 shallowRef
优化性能
对于不需要深度响应性的大型数据结构,可以使用 shallowRef
来优化性能。
const state = shallowRef({ count: 1 })
// 不会触发更新
state.value.count = 2
// 会触发更新
state.value = { count: 2 }
类型化组件 emits
通过类型化 emits
,可以获得更好的错误处理和编辑器支持。
const emit = defineEmits<{
change: [id: number]
update: [value: string]
}>()
在 <style>
中使用 v-bind
Vue 允许在 <style>
中使用 v-bind
来绑定动态值,并且它是完全响应式的。
<style scoped>
button {
background-color: v-bind(backgroundColor);
}
</style>
使用 Tanstack Query 简化数据获取
Tanstack Query(Vue Query)可以大大减少数据获取的样板代码,并提供自动缓存、自动重新获取等强大功能。
// 之前的数据获取模式
const posts = ref([]);
const isLoading = ref(false);
const isError = ref(false);
async function fetchPosts() {
isLoading.value = true;
isError.value = false;
try {
const response = await fetch('someurl');
posts.value = await response.json();
} catch (error) {
isError.value = true;
} finally {
isLoading.value = false;
}
}
onMounted(() => {
fetchPosts();
})
// 使用 Tanstack Query 简化
const {
data: posts,
isLoading,
isError
} = useQuery({
queryKey: ['posts'],
queryFn: fetchPosts
})
async function fetchPosts() {
const response = await fetch('someurl');
const data = await response.json();
return data;
}
使用 :global
伪类应用全局样式
在 Vue 中,使用 :global
伪类可以在局部样式中应用全局样式。
<style scoped>
:global(.red) {
color: red;
}
</style>
使用 withDefaults
设置默认值
即使在使用类型声明的 defineProps
时,也可以使用 withDefaults
宏来设置默认值。
<script setup lang="ts">
export interface Props {
variant?: "primary" | "secondary";
disabled?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
variant: "primary",
disabled: false
});
</script>
自定义指令
在 Vue 中,可以通过创建一个包含生命周期钩子的对象来注册自定义指令
<script setup>
// 在模板中启用 v-focus
const vFocus = {
mounted: (el) => el.focus()
};
</script>
<template>
<input v-focus />
</template>
使用 :deep()
伪类影响子组件样式
在局部样式中,使用 :deep()
伪类可以影响子组件的样式。
<style scoped>
.a :deep(.b) {
/* ... */
}
</style>
使用 :slotted
伪类针对插槽内容
默认情况下,局部样式不会影响通过 <slot/>
渲染的内容。使用 :slotted
伪类可以显式地针对插槽内容。
<style scoped>
:slotted(div) {
color: red;
}
</style>
使用 <KeepAlive>
缓存组件状态
在 Vue.js 中,默认情况下,切换组件时会卸载当前组件。使用 <KeepAlive>
可以缓存组件状态。
<template>
<KeepAlive>
<component :is="activeComponent" />
</KeepAlive>
</template>
传递多个具名插槽
在 Vue.js 中,可以向子组件传递多个具名插槽。
<!-- 子组件 / Input.vue -->
<template>
<div class="input-wrapper">
<label>
<slot name="label" />
</label>
<input />
<div class="input-icon">
<slot name="icon" />
</div>
</div>
</template>
<!-- 父组件 -->
<template>
<Input>
<template #label>Email</template>
<template #icon>
<EmailIcon />
</template>
</Input>
</template>
使用 Suspense
处理异步依赖
通过实验性的 Suspense
组件,可以在组件树中协调异步依赖,并在等待多个嵌套异步依赖解析时渲染加载状态。
<template>
<Suspense>
<!-- 包含嵌套异步依赖的组件 -->
<Dashboard />
<!-- 通过 #fallback 插槽显示加载状态 -->
<template #fallback>Loading...</template>
</Suspense>
</template>
使用 Teleport
传送模板部分
在 Vue 中,可以使用内置的 Teleport
组件将模板的一部分传送到组件 DOM 层次结构之外的 DOM 节点。
<template>
<Teleport to="body">
<div v-if="open" class="modal">
<p>Hello from the modal!</p>
<button @click="open = false">Close</button>
</div>
</Teleport>
</template>
启用性能追踪
在 Vue 中,可以在浏览器开发者工具的性能/时间线面板中启用性能追踪。这仅在开发模式下有效。
const app = createApp(App);
app.config.performance = true;
app.mount('#app');
动态渲染组件
在 Vue 中,可以使用内置的 <Component>
组件动态渲染组件。
<script setup>
import UserSettings from "./Foo.vue";
import UserNotifications from "./Bar.vue";
const activeComponent = ref(UserSettings);
</script>
<template>
<component :is="activeComponent" />
</template>
布尔类型 prop 的简写
在 Vue 中,当传递布尔类型的 prop 且值为 true
时,可以使用简写形式。
<template>
<!-- 可以使用这种形式 -->
<BlogPost is-published />
<!-- 而不是这种形式 -->
<BlogPost :is-published="true" />
</template>
使用 v-model
修饰符
默认情况下,v-model
在每次 input
事件后同步数据。可以使用 lazy
修饰符改为在 change
事件后同步。
<!-- 在 "change" 事件后同步 -->
<input v-model.lazy="msg" />
自动类型转换
如果希望用户输入自动转换为数字,可以在 v-model
上添加 number
修饰符。
<input v-model.number="age" />
自动修剪空格
如果希望自动修剪用户输入的空格,可以在 v-model
上添加 trim
修饰符。
<input v-model.trim="msg" />
使用 defineExpose
暴露属性
在 <script setup>
组件中,默认是关闭的。使用 defineExpose
编译器宏可以显式暴露属性。
<script setup>
import { ref } from "vue";
const a = 1;
const b = ref(2);
defineExpose({
a,
b
});
</script>
合并类和样式
在 Vue 中,当组件模板中有一些类,并且在父组件中也添加了一些类时,这些类会自动合并。
<!-- 父组件 -->
<template>
<Table class="py-2"></Table>
</template>
<!-- 子组件 Table.vue -->
<template>
<table class="border-solid border-2 border-sky-500">
<!-- ... -->
</table>
</template>
<!-- 合并后的类 -->
<template>
<table class="border-solid border-2 border-sky-500 py-2">
<!-- ... -->
</table>
</template>
启用自定义格式化器
在 Vue 中,启用自定义格式化器可以在控制台中更好地查看响应式数据。可以在 Chrome 浏览器的开发者工具中启用自定义格式化器,选择 Console -> custom formatters。
