Vue 3 单文件组件与模板语法
一、单文件组件是什么
Vue 项目通常使用.vue单文件组件。一个组件可以把结构、逻辑和样式放在同一个文件中:
<template> <div class="page"> <h1>{{ title }}</h1> </div> </template> <script setup lang="ts"> const title = 'Vue 3 页面'; </script> <style scoped> .page { padding: 24px; } </style>三个部分分别承担:
<template> 页面结构 <script setup> 状态、函数和业务逻辑 <style scoped> 当前组件样式二、<script setup>的作用
Vue 3 中常见写法是:
<script setup lang="ts">其中:
setup表示使用组合式 API 的简化语法;lang="ts"表示脚本使用 TypeScript;- 在脚本中声明的变量和函数,可以直接在模板中使用。
例如:
<template> <button @click="increase">{{ count }}</button> </template> <script setup lang="ts"> import { ref } from 'vue'; const count = ref(0); function increase() { count.value += 1; } </script>不需要再手动编写return { count, increase }。
三、插值表达式{{ }}
模板中使用双大括号显示数据:
<h1>{{ title }}</h1> <p>当前炉次:{{ heatId }}</p>插值中可以使用简单表达式:
<p>{{ heatId || '--' }}</p> <p>{{ isRealtime ? '实时模式' : '回放模式' }}</p> <p>{{ warnings.length }}</p>也可以调用格式化函数:
<strong>{{ formatTemperature(temperature) }}</strong>但不建议在模板中编写过于复杂的逻辑。复杂结果应优先放到computed中。
四、可选链与默认值
接口数据在页面首次渲染时可能还没有返回。下面的写法可以避免读取空对象时报错:
<strong>{{ heatMeta?.converter_no || '--' }}</strong>其中:
?.表示对象存在时才继续读取;|| '--'表示前面的值为空时显示占位内容。
需要注意,||会把0、空字符串和false也视为假值。只想处理null和undefined时,可以使用??:
{{ value ?? '--' }}五、条件渲染
1.v-if
<div v-if="loading">正在加载...</div>条件为真时才创建该 DOM。
2.v-else
<div v-if="loading">正在加载...</div> <div v-else>加载完成</div>v-else必须紧跟在v-if或v-else-if后面。
3.v-else-if
<el-tag v-if="risk === 'critical'" type="danger">严重</el-tag> <el-tag v-else-if="risk === 'warning'" type="warning">预警</el-tag> <el-tag v-else type="success">正常</el-tag>4.v-show
<div v-show="visible">控制面板</div>v-show不会销毁 DOM,只会改变 CSS 的display。
选择建议:
切换频繁 → v-show 条件很少变化或内容较重 → v-if六、列表渲染v-for
<div v-for="signal in signalCards" :key="signal.code"> <span>{{ signal.label }}</span> <strong>{{ signal.value }}</strong> </div>这里的含义是:
遍历 signalCards ↓ 每次取出一个 signal ↓ 为它生成一组 DOM也可以同时得到索引:
<li v-for="(item, index) in list" :key="item.id"> {{ index + 1 }}. {{ item.name }} </li>为什么需要:key
Vue 在更新列表时,需要判断哪些节点被增加、删除或移动。稳定的 Key 可以帮助 Vue 正确复用 DOM。
推荐:
:key="item.id"谨慎使用:
:key="index"当列表发生排序、插入或删除时,索引并不稳定。
七、属性绑定v-bind
普通 HTML 属性是固定字符串:
<button disabled="true">按钮</button>需要绑定 JavaScript 数据时使用v-bind:
<el-button v-bind:loading="loading">刷新</el-button>简写为:
<el-button :loading="loading">刷新</el-button>动态类名
对象写法:
<div :class="{ active: isActive, disabled: isDisabled }"></div>数组写法:
<div :class="[riskClass, { lightweight: lowPerformance }]"></div>动态样式
<div :style="{ left: positionX + 'px', top: positionY + 'px' }"></div>也可以绑定完整对象:
<div :style="labelStyles[item.key]"></div>八、事件绑定v-on
<el-button @click="loadData">刷新</el-button>@click是v-on:click的简写。
常见事件包括:
<input @input="handleInput" /> <select @change="handleChange"></select> <div @dblclick="handleDoubleClick"></div> <div @wheel="handleWheel"></div>获取事件对象
<button @click="handleClick($event)">点击</button>functionhandleClick(event:MouseEvent){console.log(event.clientX,event.clientY);}传递自定义参数
<button @click="setMode('realtime')">实时模式</button>九、双向绑定v-model
v-model常用于表单和交互组件:
<input v-model="keyword" /> <el-switch v-model="autoRotate" /> <el-slider v-model="opacity" /> <el-select v-model="playbackSpeed" />例如:
<template> <input v-model="name" /> <p>输入内容:{{ name }}</p> </template> <script setup lang="ts"> import { ref } from 'vue'; const name = ref(''); </script>用户修改输入框时,name自动变化;代码修改name时,输入框也自动变化。
十、模板引用ref="..."
模板引用用于获得真实 DOM 或组件实例:
<div ref="containerRef" class="canvas-container"></div>constcontainerRef=ref<HTMLElement>();组件挂载后可以访问:
onMounted(()=>{console.log(containerRef.value);});这在初始化 Three.js、图表库或视频播放器时非常常见。
需要区分两种ref:
ref() 创建响应式状态 ref="containerRef" 在模板中记录 DOM 或组件引用它们相关,但不是同一个使用场景。
十一、Element Plus 组件也是 Vue 组件
下面的标签不是浏览器原生 HTML:
<el-button /> <el-tag /> <el-alert /> <el-switch /> <el-slider />它们由 Element Plus 提供,本质上仍然遵循 Vue 的属性绑定、事件和v-model规则:
<el-button type="primary" :loading="loading" @click="loadData" > 刷新 </el-button>十二、一个完整的小案例
<template> <section class="monitor-panel"> <h2>{{ title }}</h2> <el-alert v-if="errorMessage" :title="errorMessage" type="warning" /> <div v-if="loading">数据加载中...</div> <div v-else> <div v-for="signal in signals" :key="signal.code" :class="['signal-card', { danger: signal.value > signal.limit }]" > <span>{{ signal.label }}</span> <strong>{{ signal.value }} {{ signal.unit }}</strong> </div> </div> <el-switch v-model="autoRefresh" active-text="自动刷新" /> <el-button :loading="loading" @click="loadSignals">刷新</el-button> </section> </template> <script setup lang="ts"> import { ref } from 'vue'; const title = '过程信号'; const loading = ref(false); const errorMessage = ref(''); const autoRefresh = ref(false); const signals = ref([ { code: 'TEMP', label: '温度', value: 1650, limit: 1700, unit: '°C' }, { code: 'FLOW', label: '氧流量', value: 18000, limit: 22000, unit: 'Nm³/h' } ]); function loadSignals() { loading.value = true; setTimeout(() => { loading.value = false; }, 500); } </script>十三、常见误区
1. 在模板中编写大量业务逻辑
模板应以展示为主。复杂条件应放到computed或函数中。
2.v-for忘记稳定的 Key
列表更新时可能出现状态错位。
3. 把v-if和v-for放在同一个元素上
优先在计算属性中先过滤数据,或使用外层<template>分开处理。
4. 在组件尚未挂载时访问模板 DOM
模板引用通常需要在onMounted()后使用。
十四、本章小结
Vue 模板的核心思想是:
状态决定页面结构和属性 用户事件修改状态 状态变化后页面自动更新下一章将进入 Vue 的核心:ref、reactive和computed响应式数据。