Vue3作为当前主流的前端框架之一,其视图渲染技术是开发者必须掌握的核心内容。Vue3的模板语法基于HTML扩展,允许开发者以声明式的方式将组件实例的数据绑定到DOM上。这种设计理念使得Vue模板既符合标准HTML规范,又能被现代浏览器和HTML解析器正确解析。
在实际开发中,Vue会将模板编译为高度优化的JavaScript代码。结合其响应式系统,当应用状态发生变化时,Vue能够智能地计算出需要重新渲染的最小组件集合,并执行最少的DOM操作。这种机制大幅提升了应用性能,特别是在处理复杂界面时效果尤为明显。
插值表达式是Vue中最基础的数据绑定形式,采用"Mustache"语法(双大括号{{}})实现。它的核心特点包括:
javascript复制<script setup type='module'>
let msg = "hello vue3"
let getMsg = () => {
return 'hello vue3 message'
}
let age = 19
let bee = '蜜 蜂'
const carts = [
{name:'可乐', price:3, number:10},
{name:'薯片', price:6, number:8}
];
function compute(){
let count = 0;
for(let index in carts){
count += carts[index].price * carts[index].number;
}
return count;
}
</script>
在模板中使用时:
html复制<template>
<div>
<h1>{{msg}}</h1>
msg的值为: {{ msg }} <br>
getMsg返回的值为: {{ getMsg() }} <br>
是否成年: {{ age>=18?'true':'false' }} <br>
反转: {{ bee.split(' ').reverse().join('-') }} <br>
购物车总金额: {{ compute() }} <br/>
</div>
</template>
Vue提供了专门的指令来处理文本渲染:
v-text:将数据渲染为双标签中间的文本,不识别HTML结构v-html:将数据渲染为双标签中间的文本,识别HTML结构javascript复制<script setup type='module'>
let msg = 'hello vue3'
let redMsg = '<font color="red">msg</font>'
let greenMsg = `<font color="green">${msg}</font>`
</script>
<template>
<div>
<span v-text='msg'></span> <br>
<span v-text='redMsg'></span> <br>
<span v-html='msg'></span> <br>
<span v-html='redMsg'></span> <br>
<span v-html='greenMsg'></span> <br>
</div>
</template>
注意:使用v-html时要特别注意XSS攻击风险,永远不要将用户提供的内容作为v-html的值直接渲染。
插值表达式不能直接用于HTML属性,此时需要使用v-bind指令:
html复制<img v-bind:src="imageSrc">
<!-- 简写形式 -->
<img :src="imageSrc">
属性绑定支持动态属性名:
html复制<button :[dynamicAttr]="value">...</button>
Vue使用v-on指令监听DOM事件:
html复制<button v-on:click="handler">点击</button>
<!-- 简写形式 -->
<button @click="handler">点击</button>
事件处理器可以是方法名或内联JavaScript语句:
javascript复制<script setup>
import {ref} from 'vue'
let count = ref(0)
function increment() {
count.value++
}
</script>
<template>
<div>
<button @click="count++">内联事件</button>
<button @click="increment">方法事件</button>
</div>
</template>
Vue提供了一系列事件修饰符来处理常见的DOM事件细节:
.stop:阻止事件冒泡.prevent:阻止默认行为.capture:使用捕获模式.self:仅当事件是从侦听器绑定的元素本身触发时才触发回调.once:只触发一次.passive:提升滚动性能html复制<!-- 阻止单击事件继续传播 -->
<a @click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<form @submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a @click.stop.prevent="doThat"></a>
<!-- 点击事件将只会触发一次 -->
<button @click.once="doThis"></button>
Vue3提供了两种创建响应式数据的主要方式:
ref用于包装基本类型数据,使其变为响应式:
javascript复制import {ref} from 'vue'
const count = ref(0)
function increment() {
count.value++ // 注意需要通过.value访问
}
reactive用于包装对象,使其所有属性都变为响应式:
javascript复制import {reactive} from 'vue'
const state = reactive({
count: 0,
name: 'Vue'
})
function increment() {
state.count++ // 直接访问属性
}
这两个API用于在保持响应式的情况下解构reactive对象:
javascript复制import {reactive, toRef, toRefs} from 'vue'
const state = reactive({
count: 0,
name: 'Vue'
})
// 单个属性转换
const countRef = toRef(state, 'count')
// 整个对象转换
const {count, name} = toRefs(state)
html复制<div v-if="type === 'A'">
A
</div>
<div v-else-if="type === 'B'">
B
</div>
<div v-else>
Not A/B
</div>
html复制<h1 v-show="isVisible">Hello!</h1>
区别:v-if是真正的条件渲染,元素会被销毁和重建;v-show只是切换CSS的display属性,元素始终存在DOM中。
使用v-for指令渲染数组:
javascript复制<script setup>
const items = ref([
{id: 1, name: 'Item 1'},
{id: 2, name: 'Item 2'}
])
</script>
<template>
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</ul>
</template>
重要提示:使用v-for时必须提供唯一的key属性,这有助于Vue识别节点身份,提高渲染效率。
javascript复制const carts = reactive([
{name: '可乐', price: 3, number: 10},
{name: '薯片', price: 6, number: 8}
])
javascript复制function computeTotal() {
return carts.reduce((total, item) => {
return total + item.price * item.number
}, 0)
}
javascript复制function removeItem(index) {
carts.splice(index, 1)
}
html复制<table>
<thead>
<tr>
<th>序号</th>
<th>商品名</th>
<th>价格</th>
<th>数量</th>
<th>小计</th>
<th>操作</th>
</tr>
</thead>
<tbody v-if="carts.length > 0">
<tr v-for="(item, index) in carts" :key="index">
<td>{{ index + 1 }}</td>
<td>{{ item.name }}</td>
<td>{{ item.price }}元</td>
<td>{{ item.number }}</td>
<td>{{ item.price * item.number }}元</td>
<td><button @click="removeItem(index)">删除</button></td>
</tr>
</tbody>
<tbody v-else>
<tr>
<td colspan="6">购物车为空</td>
</tr>
</tbody>
</table>
<div>总金额: {{ computeTotal() }}元</div>
问题:直接修改数组或对象属性时视图不更新
解决:
问题:出现"Template compilation error"
解决:
问题:页面渲染卡顿
解决:
html复制<component :is="currentComponent" />
javascript复制app.directive('focus', {
mounted(el) {
el.focus()
}
})
javascript复制import {h} from 'vue'
export default {
render() {
return h('div', {}, 'Hello World')
}
}
在实际项目开发中,我发现以下几点特别值得注意:
响应式数据设计:合理规划数据结构,避免过度嵌套。对于简单的状态管理,ref和reactive已经足够;复杂场景可以考虑Pinia。
性能优化:列表渲染务必提供key,大数据量场景下考虑虚拟滚动方案。我曾经在一个项目中因为没有正确使用key导致性能下降明显,这个教训很深刻。
代码组织:将复杂逻辑抽取到computed和methods中,保持模板简洁。这样不仅提高可读性,也便于维护和测试。
组件拆分:不要在一个组件中堆积太多功能,按照职责合理拆分。我通常会根据功能模块划分组件,每个组件专注于单一职责。
工具链使用:充分利用Vue Devtools进行调试和性能分析,它能帮助快速定位问题。
最后分享一个小技巧:在开发过程中,可以使用v-once指令标记那些永远不会改变的静态内容,这可以跳过它们的响应式跟踪,提高性能。