Vue2和Vue3模板语法对比
Vue.js作为一款渐进式JavaScript框架,以其简洁优雅的模板语法著称。从Vue 2到Vue 3的演进过程中,模板语法经历了重要的改进和优化。Vue 3在保持核心语法兼容性的同时,引入了更强大的功能、更好的性能和更灵活的组织方式。通过详细对比Vue 2和Vue 3在模板语法上的主要差异,可以更好地理解和迁移。
核心模板语法对比表(示例分列展示)
|
对比项 |
Vue 2语法 |
Vue 2示例 |
Vue 3语法 |
Vue 3示例 |
|
**多根节点** |
必须单根节点 |
`<div><header/><main/></div>` |
支持多根节点 |
`<header/><main/>` |
|
**v-model绑定** |
单一v-model |
`<Comp v-model="val">` |
多v-model+参数 |
`<Comp v-model:title="t" v-model:content="c">` |
|
**v-model修饰符** |
仅内置修饰符 |
`<input v-model.lazy="txt">` |
支持自定义修饰符 |
`<Comp v-model:title.capitalize="t">` |
|
**插槽语法** |
slot属性 |
`<template slot="header">` |
v-slot/#缩写 |
`<template #header>` |
|
**作用域插槽** |
slot-scope |
`<template slot-scope="{u}">` |
v-slot参数 |
`<template #default="{u}">` |
|
**动态插槽名** |
不支持 |
无 |
支持#[expr] |
`<template #[slotName]>` |
|
**v-if与v-for** |
v-for优先(可混用) |
`<li v-for="u in list" v-if="u.show">` |
v-if优先(不可混用) |
用计算属性: `<li v-for="u in filtered">` |
|
**动态指令参数** |
不支持 |
无 |
支持:[expr] |
`<a :[attrName]="url">` |
|
**事件多处理器** |
不支持 |
`<button @click="h">` |
逗号分隔 |
`<button @click="h1(), h2()">` |
|
**v-memo指令** |
不存在 |
无 |
记忆化渲染 |
`<div v-memo="[val]">内容</div>` |
|
**Teleport组件** |
不存在 |
无 |
跨DOM渲染 |
`<Teleport to="body"><Modal/></Teleport>` |
|
**Suspense组件** |
不存在 |
无 |
异步加载状态 |
`<Suspense><Async/><template #fallback>Loading</template></Suspense>` |
|
**事件修饰符** |
`.stop/.prevent` |
`@click.stop="h"` |
相同+优化 |
`@click.stop.prevent="h"` |
|
**按键修饰符** |
`.enter/.esc` |
`@keyup.enter="h"` |
推荐-keyCode |
`@keyup.enter="h"` 或 `@keyup.13="h"` |
|
**v-bind.sync** |
`.sync修饰符` |
`<Comp :title.sync="t">` |
废弃,用v-model |
`<Comp v-model:title="t">` |
核心差异详解
1. Fragment多根节点
对比示例:
<!-- Vue 2 -->
<template>
<div>
<header>Header</header>
<main>Content</main>
</div>
</template>
<!-- Vue 3 -->
<template>
<header>Header</header>
<main>Content</main>
</template>
关键点:
• Vue 2:必须单根节点包裹,否则报错
• Vue 3:支持多根节点,减少DOM层级
- 迁移:去除不必要的包裹div
2. v-model大幅增强
对比示例:
<!-- Vue 2 -->
<CustomInput v-model="value"/>
<!-- 组件:props:['value'], emit('input', newValue) -->
<!-- Vue 3 -->
<UserForm
v-model:firstName="first"
v-model:lastName="last"
v-model:email.capitalize="email"
/>
<!-- 组件:props:{firstName,lastName,email}, emit('update:firstName',newValue) -->
关键点:
• Vue 2:单一v-model,默认`value`+`input`事件
• Vue 3:多v-model,自定义参数名+`update:xxx`事件
• Vue 3:支持自定义修饰符(如capitalize)
- 迁移:组件v-model需改用modelValue参数
3. 插槽语法标准化
对比示例:
<!-- Vue 2 -->
<template slot="header">
<h1>Title</h1>
</template>
<template slot-scope="{ user }">
{{ user.name }}
</template>
<!-- Vue 3 -->
<template #header>
<h1>Title</h1>
</template>
<template #default="{ user }">
{{ user.name }}
</template>
关键点:
• Vue 2:slot属性 + slot-scope语法
• Vue 3:v-slot指令 + #缩写
- 迁移:`slot="name"` → `#name`,`slot-scope` → `#default`
4. v-if与v-for优先级(必须重构)
对比示例:
<!-- Vue 2 -->
<li v-for="user in users" v-if="user.isActive">
{{ user.name }}
</li>
<!-- Vue 3 - 报错 -->
<li v-for="user in users" v-if="user.isActive">
{{ user.name }}
</li>
<!-- Vue 3 - 正确 -->
<script setup>
const activeUsers = computed(() => users.value.filter(u => u.isActive))
</script>
<template>
<li v-for="user in activeUsers">
{{ user.name }}
</li>
</template>
关键点:
• Vue 2:v-for优先,可以混用(但性能差)
• Vue 3:v-if优先,无法访问v-for变量(报错)
- 迁移:必须用计算属性重构
5. 动态指令参数
对比示例:
<!-- Vue 2 - 不支持 -->
无动态参数功能
<!-- Vue 3 -->
<script setup>
const attrName = 'href'
const eventName = 'click'
</script>
<template>
<a :[attrName]="url">Link</a>
<button @[eventName]="handler">Button</button>
<template #[slotName]>Dynamic Slot</template>
</template>
关键点:
• Vue 3新增:属性名、事件名、插槽名可动态化
• 语法:`:[expression]`、`@[expression]`、`#[expression]`
- 适用:属性名动态变化的场景
6. 事件处理增强
对比示例:
<!-- Vue 2 -->
<button @click="handleClick">Button</button>
<!-- Vue 3 -->
<button @click="handler1(), handler2()">
Multiple Handlers
</button>
<button @click="count++, logEvent($event)">
Inline + Method
</button>
关键点:
• Vue 2:单一处理器
• Vue 3:逗号分隔多处理器,支持混合内联和方法
- 适用:简化多操作触发场景
7. v-memo性能优化
对比示例:
<!-- Vue 2 - 不存在 -->
无记忆化功能
<!-- Vue 3 -->
<script setup>
const list = ref([...]) // 1000条数据
const selectedId = ref(1)
</script>
<template>
<div
v-for="item in list"
:key="item.id"
v-memo="[item.id, item.id === selectedId]"
>
{{ item.name }}
</div>
</template>
关键点:
• Vue 3新增:记忆化渲染,跳过虚拟DOM diff
• 依赖数组:仅当依赖变化才更新
- 适用:大列表性能优化(最高10倍提升)
8. Teleport组件
对比示例:
<!-- Vue 2 - 不存在 -->
需要手动处理Modal定位问题
<!-- Vue 3 -->
<script setup>
const showModal = ref(false)
</script>
<template>
<button @click="showModal = true">Open</button>
<Teleport to="body">
<div v-if="showModal" class="modal">
<h2>Modal Title</h2>
<button @click="showModal = false">Close</button>
</div>
</Teleport>
</template>
关键点:
• Vue 3新增:跨DOM树渲染组件
• 解决:z-index冲突、overflow遮挡问题
- 适用:Modal、Dropdown、Notification
9. Suspense异步加载
对比示例:
<!-- Vue 2 - 不存在 -->
<div v-if="loading">Loading...</div>
<AsyncComp v-else/>
<!-- Vue 3 -->
<script setup>
const AsyncComp = defineAsyncComponent(() =>
import('./HeavyComponent.vue')
)
</script>
<template>
<Suspense>
<AsyncComp/>
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
</template>
关键点:
• Vue 3新增:异步组件加载状态管理
• 替代:手动v-if判断loading状态
- 适用:异步组件、数据加载
10. v-bind.sync废弃
对比示例:
<!-- Vue 2 -->
<CustomComponent :title.sync="pageTitle"/>
<!-- 组件:emit('update:title', newValue) -->
<!-- Vue 3 -->
<CustomComponent v-model:title="pageTitle"/>
<!-- 组件:emit('update:title', newValue) -->
关键点:
• Vue 2:`.sync`修饰符实现双向绑定
• Vue 3:废弃`.sync`,统一用v-model
- 迁移:`:prop.sync` → `v-model:prop`
11. 事件修饰符
对比示例:
<!-- Vue 2 -->
<form @submit.prevent="handleSubmit">
<input @keyup.enter="handleEnter">
</form>
<!-- Vue 3 -->
<form @submit.prevent="handleSubmit">
<input @keyup.enter="handleEnter">
<!-- 支持链式修饰符 -->
<button @click.stop.prevent="handleClick">
Stop + Prevent
</button>
</form>
关键点:
• Vue 2和Vue 3:基础修饰符相同(`.stop`、`.prevent`、`.capture`等)
• Vue 3:优化修饰符顺序和链式调用
- 无变化:日常使用保持一致
12. 按键修饰符
对比示例:
<!-- Vue 2 -->
<input @keyup.enter="handleEnter"/>
<input @keyup.13="handleEnter"/> <!-- keyCode -->
<!-- Vue 3 -->
<input @keyup.enter="handleEnter"/>
<input @keyup.a="handleA"/> <!-- 按键名推荐 -->
<!-- keyCode已废弃,推荐使用按键名 -->
关键点:
• Vue 2:支持keyCode数字和按键名
• Vue 3:推荐使用按键名,keyCode已废弃
- 迁移:`.13` → `.enter`(推荐)
13. 作用域插槽对比
对比示例:
<!-- Vue 2 -->
<template slot="default" slot-scope="{ user, index }">
<div>{{ index }}: {{ user.name }}</div>
</template>
<!-- Vue 3 -->
<template #default="{ user, index }">
<div>{{ index }}: {{ user.name }}</div>
</template>
关键点:
• Vue 2:`slot-scope`属性接收作用域数据
• Vue 3:`v-slot`参数接收,语法更简洁
- 迁移:`slot-scope="{data}"` → `#default="{data}"`
14. 动态插槽名
对比示例:
<!-- Vue 2 - 不支持 -->
插槽名必须固定字符串
<!-- Vue 3 -->
<script setup>
const dynamicSlot = computed(() => isMobile ? 'mobile' : 'desktop')
</script>
<template>
<template #[dynamicSlot]>
Dynamic Content
</template>
</template>
关键点:
• Vue 3新增:插槽名可动态化
• 语法:`#[expression]`
- 适用:响应式布局、条件插槽
15. class/style绑定(无变化)
对比示例:
<!-- Vue 2和Vue 3完全相同 -->
<template>
<!-- class对象语法 -->
<div :class="{ active: isActive }">Active</div>
<!-- class数组语法 -->
<div :class="[baseClass, { error: hasError }]">Array</div>
<!-- style对象语法 -->
<div :style="{ color: textColor, fontSize: '14px' }">Styled</div>
<!-- style数组语法 -->
<div :style="[baseStyles, overrideStyles]">Array Style</div>
</template>
关键点:
• Vue 2和Vue 3:语法完全相同
• 支持:对象语法、数组语法、混合语法
- 无迁移:直接使用即可
无变化语法快速参考
|
语法 |
示例 |
说明 |
|
**数据绑定** |
`{{ message }}` |
Mustache语法,无变化 |
|
**v-text** |
`<span v-text="msg">` |
替换文本内容 |
|
**v-html** |
`<div v-html="content">` |
渲染HTML内容 |
|
**v-show** |
`<div v-show="visible">` |
显示/隐藏切换 |
|
**v-once** |
`<div v-once>{{msg}}` |
只渲染一次 |
|
**v-pre** |
`<span v-pre>{{原始文本}}` |
跳过编译 |
|
**v-for** |
`<li v-for="item in list" :key="item.id">` |
循环渲染 |
|
**v-if/v-else** |
`<div v-if="show">A</div><div v-else>B</div>` |
条件渲染 |
|
**v-else-if** |
`<div v-if="a">A</div><div v-else-if="b">B</div>` |
多条件分支 |
|
**ref引用** |
`<input ref="inputRef">` |
模板引用(访问方式不同) |
|
**key属性** |
`<div :key="id">` |
唯一标识 |
|
**class绑定** |
`:class="{ active: isActive }"` |
对象/数组语法 |
|
**style绑定** |
`:style="{ color: textColor }"` |
对象/数组语法 |
必须修改清单
|
Vue 2写法 |
Vue 3修改 |
原因 |
|
`<li v-for="u in users" v-if="u.active">` |
用计算属性过滤 |
v-if/v-for优先级变更,无法访问变量 |
|
`<template slot="header">` |
`<template #header>` |
插槽语法标准化 |
|
`<template slot-scope="{data}">` |
`<template #default="{data}">` |
作用域插槽语法更新 |
|
`<Comp :title.sync="t">` |
`<Comp v-model:title="t">` |
.sync修饰符废弃 |
|
`<div><header/><main/></div>` |
`<header/><main/>` |
Fragment支持,消除冗余包裹 |
|
`<Comp v-model="val">` |
`<Comp v-model:modelValue="val">` |
v-model参数化 |
可选优化清单
|
新特性 |
Vue 3示例 |
适用场景 |
|
**多v-model** |
`<Form v-model:name="n" v-model:email="e">` |
表单多字段双向绑定 |
|
**v-model自定义修饰符** |
`<Input v-model:text.capitalize="t">` |
自定义数据处理 |
|
**动态参数** |
`<a :[attrName]="url">` |
属性名/事件名动态变化 |
|
**v-memo** |
`<div v-memo="[deps]">` |
大列表性能优化 |
|
**Teleport** |
`<Teleport to="body"><Modal/></Teleport>` |
Modal、Dropdown定位 |
|
**Suspense** |
`<Suspense><AsyncComp/></Suspense>` |
异步组件加载状态 |
|
**多事件处理器** |
`@click="h1(), h2()"` |
简化多操作触发 |
|
**动态插槽名** |
`<template #[slotName]>` |
条件插槽、响应式布局 |
总结
Vue 3模板语法的15个核心差异:
必须掌握(5个)
1. Fragment - 多根节点,减少DOM嵌套
2. v-model增强 - 多绑定+自定义参数+修饰符
3. 插槽标准化 - v-slot/#缩写统一语法
4. v-if/v-for优先级 - 必须重构混用场景
5. .sync废弃 - 统一用v-model替代
可选优化(10个)
6. 动态指令参数 - 属性名/事件名动态化
7. 事件多处理器 - 逗号分隔简化操作
8. 动态插槽名 - 插槽名称动态化
9. v-memo - 记忆化渲染优化
10. Teleport - 跨DOM树渲染
11. Suspense - 异步加载状态管理
12. 按键修饰符优化 - 推荐按键名
13. 事件修饰符优化 - 链式调用
14. v-model修饰符 - 自定义修饰符
15. class/style绑定 - 无变化,快速参考
迁移优先级
• 必须修改:v-if+v-for混用、插槽旧语法、.sync语法
• 建议采用:Fragment多根节点、v-model参数化、动态参数
• 可选优化:v-memo、Teleport、Suspense、多处理器
掌握这15个核心差异点,即可快速完成Vue 2到Vue 3模板语法迁移,编写更简洁、性能更优的代码。
浙公网安备 33010602011771号