模板语法及新特性
🎯 引言
Vue3 在模板语法上带来了不少新变化和优化,特别是在多根节点支持、v-model 的升级、插槽语法调整、指令优先级、属性透传、事件修饰符增强以及全新的 Teleport 和 Suspense 组件上。掌握这些内容,能让你写出更简洁高效的 Vue3 应用,也更好地理解 Vue3 的设计理念和进阶用法。
🌿 多根节点 & Fragment
Vue2 中,一个组件模板必须有且只有一个根节点,这是大家常遇到的限制。而在 Vue3 中,这个限制被取消了,支持多根节点,也就是说你可以直接写多个并列的标签,而不需要额外包一层元素。
<template>
<header>这是头部</header>
<main>这是主体内容</main>
<footer>这是底部</footer>
</template>
多根节点的底层实现是利用了虚拟 DOM 中的 Fragment 概念,Vue3 会自动将多个根节点包裹成一个 Fragment,渲染时不会额外创建 DOM 元素,保持结构清晰。
这个特性让组件结构更灵活,减少了不必要的 wrapper 标签,样式和布局也更方便控制。
🔄 v-model 语法升级
标准 v-model 写法
通过 modelValue 进行接收,update:modelValue 进行发送事件。你可以在 get/set 中加入逻辑,比如格式化、验证等。
<template>
<input v-model="model" />
</template>
<script setup>
import { computed } from 'vue';
const props = defineProps({
modelValue: String,
});
const emit = defineEmits(['update:modelValue']);
const model = computed({
get() {
return props.modelValue;
},
set(val) {
emit('update:modelValue', val);
},
});
</script>
defineModel 语法糖
极简写法:省去了手动定义 props、emit 和 computed。Vue 3.4+ 新增,低版本不支持。
<template>
<input v-model="model" />
</template>
<script setup>
const model = defineModel();
</script>
多参数绑定
Vue3 支持多个 v-model 绑定不同的 prop,例如:
<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />
ChildComponent 需要声明对应的 prop 和事件:
<script setup>
const title = defineModel('title');
const content = defineModel('content');
</script>
🎭 插槽语法变化(具名/作用域)
Vue3 里插槽语法变化不大,但更推崇使用 <template #name> 语法替代 Vue2 的 slot="name",并且作用域插槽使用更简洁的 v-slot 简写法。
具名插槽
Vue2 传统写法:
<template>
<ChildComponent>
<template slot="header">这里是头部</template>
</ChildComponent>
</template>
Vue3 推荐写法:
<template>
<ChildComponent>
<template #header>这里是头部</template>
</ChildComponent>
</template>
作用域插槽
Vue3 作用域插槽语法也更简单:
<template>
<ChildComponent>
<template #default="{ user }">
<p>用户名是:{{ user.name }}</p>
</template>
</ChildComponent>
</template>
# 符号是 v-slot 的语法糖,写起来更简洁,同时也更统一,方便阅读。⚖️ v-if 和 v-for 的优先级变更
在 Vue3 中,v-if 和 v-for 的优先级规则发生了细微调整,主要是 v-if 优先级更高,避免一些复杂情况下的渲染混乱。
Vue2 中写法问题示例
<li v-for="item in list" v-if="item.visible">{{ item.name }}</li>
这里虽然能用,但逻辑上 v-if 会在 v-for 循环内部触发。
Vue3 推荐写法
<template v-for="item in list" v-if="item.visible">
<li>{{ item.name }}</li>
</template>
🧩 属性透传 Attributes
Vue3 组件默认支持属性透传,即父组件传入的未被组件声明的属性,会自动绑定到组件根节点上。
<ChildComponent id="foo" class="bar" />
如果 ChildComponent 没声明 id 和 class,这两个属性依然会透传到组件根标签。
<template>
<div>
<!-- id 和 class 会自动被绑定到这里 -->
</div>
</template>
关闭属性透传
有时候你不希望属性自动绑定,可以使用 inheritAttrs: false。
<script setup>
defineOptions({ inheritAttrs: false });
</script>
然后手动使用 v-bind="$attrs" 绑定到指定元素。
🔔 事件修饰符新特性
Vue3 对事件修饰符做了增强,其中比较实用的是对 .once 和 .passive 修饰符的支持更加全面,同时新增了 .capture 修饰符。
<button @click.once="handleClick">点击一次</button>
<button @scroll.passive="handleScroll">滚动监听</button>
<button @click.capture="handleCapture">捕获阶段事件</button>
.once:事件只触发一次,后续无效。.passive:告诉浏览器不调用 preventDefault,提升性能,通常用于滚动事件。.capture:事件捕获阶段触发,而不是冒泡阶段。
🚀 Teleport 与 Suspense 使用
Teleport
Teleport 是 Vue3 新增的组件,用于把子组件的 DOM 结构传送到 DOM 树的指定位置,常用来实现模态框、弹出层等 UI。
<template>
<Teleport to="body">
<div class="modal">这是一个弹窗内容</div>
</Teleport>
</template>
<style>
.modal {
position: fixed;
top: 30%;
left: 50%;
transform: translate(-50%, -30%);
background: white;
padding: 20px;
border: 1px solid #ccc;
}
</style>
Suspense
Suspense 用于等待异步组件或者异步数据加载完成再渲染,提升用户体验。
<template>
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<div>加载中...</div>
</template>
</Suspense>
</template>
<script setup>
import { defineAsyncComponent } from 'vue';
const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'));
</script>
这里 Suspense 会先显示 fallback 区域内容,异步组件加载完成后再显示默认插槽。
🎨 CSS 中的动态样式绑定:v-bind
Vue 3 提供了一个特别的功能,就是可以在 <style> 标签里直接用 v-bind 绑定组件的响应式变量,自动帮你把变量值注入到样式中。这样,样式和数据状态保持同步,写法也非常简洁。
<template>
<button @click="toggleColor">点击切换按钮颜色</button>
</template>
<script setup>
import { ref } from 'vue';
const isRed = ref(true);
function toggleColor() {
isRed.value = !isRed.value;
}
</script>
<style scoped>
button {
background-color: v-bind(isRed ? 'red': 'blue');
color: white;
border: none;
padding: 10px 20px;
cursor: pointer;
font-size: 16px;
border-radius: 4px;
transition: background-color 0.3s;
}
</style>
<style scoped> 中,不能写在全局样式文件里。🧾 小节总结
- Vue3 支持多根节点和 Fragment,组件写法更灵活,无需包裹无用节点。
- v-model 由原来的 value/input 模式升级为 modelValue/update:modelValue,支持多参数双向绑定。
- 插槽语法使用
<template #name>更简洁,作用域插槽写法也更清晰。 - 事件修饰符支持
.once、.passive、.capture等,提升事件处理性能。 - Teleport 让组件元素传送到任意 DOM 位置,Suspense 让异步组件加载更友好。
❓ 知识问答(Q&A)
Q:Vue3 中为什么支持多根节点了?
A:因为 Vue3 引入了 Fragment 概念,虚拟 DOM 可以处理多个根节点,不再强制组件只有一个根节点,代码结构更灵活。
Q:v-model 在 Vue3 中的事件名和 prop 名称是什么?
A:事件名是 update:modelValue,prop 名称是 modelValue
Q:Teleport 有什么实际应用场景?
A:常见用于模态框、弹出层、通知等需要脱离父组件层级的 UI 元素。
🧪 小练习
练习 1
创建一个多根节点的 Vue3 组件,包含头部、主体和底部区域,使用 Fragment 结构,不添加额外包裹元素。
<template>
<!-- 请在这里添加你的代码 -->
</template>
练习 2
实现一个支持两个 v-model 的自定义输入组件,分别双向绑定 title 和 content。
🎉 恭喜你已经掌握 Vue3 模板语法与功能变化里的多根节点、v-model 升级、插槽写法优化、指令优先级调整、属性透传、事件修饰符增强,以及 Teleport 和 Suspense 使用技巧啦!祝你前端开发之路越走越顺!
