Vue 3 核心教程
从「改 DOM」到「改数据」— 给中际旭创全栈工程师的 Vue 3 入门实战手册
01 · Vue 是什么
想象你在工厂里做一个 设备状态看板。传统做法(jQuery)就像手动写白板:
- 设备 A 停机了 → 你得亲自去擦掉"运行中",写上"停机"
- 设备 B 产量变了 → 你得亲自找到对应位置,改掉数字
- 100 台设备?你就得跑 100 次
而 Vue = 自动白板:你只需要把数据改掉(比如 status = '停机'),白板上的字自动变化。你不用关心「怎么改 DOM」,只管「改数据」。
jQuery = 你是车间工人,每次设备状态变化,你得亲自跑过去改看板上的字。
Vue = 你是 MES 系统,只管更新数据库里的状态,看板会自动刷新。
| 概念 | Excel | Vue |
|---|---|---|
| 数据 | A1 单元格的值 | ref(85) 响应式变量 |
| 视图 | B1 的公式 =A1*2 | 模板中的 {{ value * 2 }} |
| 更新 | 改 A1 → B1 自动变 | 改 ref → 页面自动变 |
| 核心思想 | 数据驱动公式 | 数据驱动视图 |
jQuery vs Vue 代码对比
// 数据变了,你手动找到元素,手动更新文字
var oee = 85;
$('#oee-value').text(oee); // 手动改 DOM
$('#oee-bar').css('width', oee + '%'); // 手动改样式
// 数据又变了?再来一遍!
oee = 72;
$('#oee-value').text(oee);
$('#oee-bar').css('width', oee + '%');
const oee = ref(85)
// 模板里用 {{ oee }},页面自动显示 85
// 数据一改,页面自动更新,不用碰 DOM!
oee.value = 72 // 就这一行,页面自动变了
Vue 的核心思想 = 数据驱动视图。你只管改数据,页面自动更新。就像 Excel 里改了源单元格,所有引用它的公式都会自动重算。
02 · 响应式数据 (ref / reactive)
响应式 是 Vue 的灵魂。简单说就是:数据一变,页面自动更新。
Vue 3 提供两种方式创建响应式数据:
ref — 包裹单个值
import { ref } from 'vue'
// 创建一个响应式变量,初始值 85
const oee = ref(85)
// 读取:用 .value
console.log(oee.value) // 85
// 修改:也用 .value → 页面自动更新
oee.value = 92
OEE 当前值:92(数据从 85 变成 92 后,页面自动更新)
reactive — 包裹整个对象
import { reactive } from 'vue'
// 创建一个响应式对象(像 Excel 的一行数据)
const machine = reactive({
name: 'CNC-A01',
status: '运行中',
oee: 85,
output: 1200
})
// reactive 不需要 .value,直接访问属性
machine.status = '停机' // 页面自动更新
machine.oee = 0
设备:CNC-A01 | 状态:停机 | OEE:0% | 产量:1200
ref vs reactive 对比
| Excel 类比 | ref | reactive | |
|---|---|---|---|
| 适合场景 | 单个单元格 | 单个值(数字、字符串) | 整行数据(对象) |
| 访问方式 | =A1 | oee.value(JS 中要 .value) | machine.oee(直接用) |
| 模板中使用 | 直接引用 | {{ oee }}(自动解包) | {{ machine.oee }} |
| 特性 | ref | reactive |
|---|---|---|
| 数据类型 | 任意(数字、字符串、布尔、对象都行) | 仅对象 / 数组 |
| JS 中读取 | x.value | x.prop |
| 模板中使用 | {{ x }}(自动解包) | {{ x.prop }} |
| 重新赋值 | ✅ x.value = 新值 | ❌ 不能整个替换,只能改属性 |
| 推荐场景 | 基础值、单个状态 | 表单、复杂对象 |
ref 在 JS 中必须用 .value,但在模板中会自动解包,不需要 .value。这是新手最容易忘的!
reactive 不能整个替换对象:machine = { ... } 会丢失响应性,必须逐个属性改。
03 · 模板语法
Vue 的模板就是 增强版 HTML,在普通 HTML 基础上加了几个「魔法指令」。
模板 = 看板的「版式模板」。你定好版式(哪里放设备名、哪里放状态灯),数据填进去就自动显示。不用每次重画整个看板。
3.1 插值 {{ }}
{{ }} = 在 HTML 中显示数据,就像 Excel 的 =A1。
<template>
<div>
<h2>{{ machineName }}</h2>
<p>OEE: {{ oee }}%</p>
<p>状态: {{ status === 'running' ? '运行中' : '停机' }}</p>
</div>
</template>
<script setup>
const machineName = ref('CNC-A01')
const oee = ref(87.5)
const status = ref('running')
</script>
CNC-A01
OEE: 87.5%
状态: 运行中
3.2 v-bind — 绑定属性
v-bind = 把数据绑定到 HTML 属性上,简写为 :。就像 Excel 的「单元格链接」。
<!-- 完整写法 -->
<img v-bind:src="imageUrl" v-bind:alt="machineName" />
<!-- 简写(推荐,日常都用这个) -->
<img :src="imageUrl" :alt="machineName" />
<!-- 实际案例:动态进度条 -->
<div class="progress-bar"
:style="{ width: oee + '%' }">
{{ oee }}%
</div>
<!-- 动态 class -->
<span :class="{ 'text-green': status === 'running', 'text-red': status !== 'running' }">
{{ status }}
</span>
动态进度条:
● 运行中3.3 v-on — 绑定事件
v-on = 监听用户操作(点击、输入等),简写为 @。
| Excel | Vue |
|---|---|
| 按钮 → 运行宏 | @click → 调用函数 |
| 数据验证 → 触发提示 | @change → 响应变化 |
<template>
<!-- 完整写法 -->
<button v-on:click="refreshData">刷新数据</button>
<!-- 简写(推荐) -->
<button @click="refreshData">刷新数据</button>
<!-- 带参数 -->
<button @click="selectMachine('CNC-A01')">选择 A01</button>
<!-- 实时计数器 -->
<p>已刷新 {{ count }} 次</p>
<button @click="count++">+1</button>
</template>
<script setup>
const count = ref(0)
const refreshData = () => {
count.value++
console.log('数据已刷新')
}
const selectMachine = (id) => {
console.log('选中:', id)
}
</script>
3.4 v-model — 双向绑定
v-model = 数据 ↔ 视图双向同步。你改输入框,数据跟着变;你改数据,输入框也变。
想象一个「设备筛选搜索框」:你在搜索框输入"CNC",筛选结果实时跟着变。这就是 v-model — 输入框和数据之间有根看不见的线,两头同步。
<template>
<input v-model="searchText" placeholder="搜索设备..." />
<p>搜索关键词: {{ searchText }}</p>
<!-- 也可用于 select -->
<select v-model="selectedLine">
<option value="line1">产线 1</option>
<option value="line2">产线 2</option>
</select>
</template>
<script setup>
const searchText = ref('')
const selectedLine = ref('line1')
</script>
搜索关键词: CNC
3.5 v-if / v-show — 条件渲染
| v-if | v-show | |
|---|---|---|
| 原理 | 真正销毁/创建 DOM 元素 | 只是 CSS display: none 切换 |
| 切换开销 | 高(每次重建 DOM) | 低(只改 CSS) |
| 初始开销 | 低(条件为 false 不渲染) | 高(始终渲染) |
| 适用场景 | 不频繁切换(弹窗、权限) | 频繁切换(Tab、折叠面板) |
<template>
<!-- v-if: 条件为 false 时,DOM 直接不存在 -->
<div v-if="status === 'running'">🟢 运行中</div>
<div v-else-if="status === 'warning'">🟡 警告</div>
<div v-else>🔴 停机</div>
<!-- v-show: 只是隐藏,DOM 一直在 -->
<div v-show="showDetail">详细信息面板</div>
</template>
<script setup>
const status = ref('running') // 显示 🟢 运行中
const showDetail = ref(true) // 显示详细信息
</script>
🟢 运行中
详细信息面板
3.6 v-for — 列表渲染
v-for = 循环渲染,相当于 Excel 的「往下填充」。最适合渲染列表数据。
你有 5 台设备的状态数据(像 Excel 里 5 行),需要生成 5 个状态卡片。v-for 自动帮你循环生成。
<template>
<table>
<tr v-for="machine in machines" :key="machine.id">
<td>{{ machine.id }}</td>
<td>{{ machine.name }}</td>
<td>
<span :class="machine.status">{{ machine.status }}</span>
</td>
<td>{{ machine.oee }}%</td>
</tr>
</table>
</template>
<script setup>
const machines = ref([
{ id: 1, name: 'CNC-A01', status: '运行中', oee: 87.5 },
{ id: 2, name: 'CNC-A02', status: '运行中', oee: 92.1 },
{ id: 3, name: 'CNC-B01', status: '停机', oee: 0 },
{ id: 4, name: '注塑-C01', status: '警告', oee: 65.3 },
])
</script>
| 编号 | 设备名 | 状态 | OEE |
|---|---|---|---|
| 1 | CNC-A01 | 运行中 | 87.5% |
| 2 | CNC-A02 | 运行中 | 92.1% |
| 3 | CNC-B01 | 停机 | 0% |
| 4 | 注塑-C01 | 警告 | 65.3% |
:key 类似数据库的主键,帮 Vue 高效追踪每个列表项。没有 key,列表更新时可能出现渲染错误。用唯一 ID(如设备编号),不要用 index。
模板语法速查表
| 指令 | 作用 | 简写 | Excel 类比 |
|---|---|---|---|
{{ }} | 显示数据 | — | =A1 |
v-bind | 绑定属性 | : | 单元格链接 |
v-on | 绑定事件 | @ | 按钮 → 运行宏 |
v-model | 双向绑定 | — | 输入框直连单元格 |
v-if | 条件渲染(销毁/创建) | — | IF 条件显示行 |
v-show | 条件显示(显隐切换) | — | 隐藏行(行还在) |
v-for | 循环渲染列表 | — | 往下拖拽填充 |
04 · 组件化
组件 = 可复用的独立功能块。就像工厂里的标准化模块,拼装成完整产线。
看板大屏 = 一个大页面,上面有 10 个「设备卡片」。每个卡片就是一个组件。你写一次卡片组件,传不同数据就能生成 10 张卡片。
就像工厂的标准化工位:每个工位结构一样,只是加工的产品不同。你不需要为每个产品单独设计一条产线。
组件的数据流:Props 进,Emit 出
| 组件概念 | Excel 类比 | 说明 |
|---|---|---|
| Props | 函数的输入参数 | 父组件传给子组件的数据(单向,只读) |
| Emit | 函数的返回值 | 子组件通知父组件「发生了什么事」 |
| 组件 | 自定义函数 | 封装好的、可复用的功能块 |
<!-- 一个 OEE 仪表盘卡片组件 -->
<template>
<div class="oee-card">
<h3>{{ title }}</h3>
<!-- props 是父组件传进来的数据 -->
<div class="gauge">{{ value }}%</div>
<div class="bar" :style="{ width: value + '%' }"></div>
<!-- 点击按钮,通知父组件 -->
<button @click="handleClick">查看详情</button>
</div>
</template>
<script setup>
// 1. Props = 输入参数(父组件传来的数据)
const props = defineProps({
title: { type: String, required: true },
value: { type: Number, default: 0 }
})
// 2. Emit = 输出事件(通知父组件)
const emit = defineEmits(['detail'])
const handleClick = () => {
emit('detail', props.title) // 把设备名传回给父组件
}
</script>
<template>
<!-- 像调用函数一样使用组件,:value 传入数据 -->
<OeeGauge title="可用率" :value="92" @detail="onDetail" />
<OeeGauge title="表现率" :value="88" @detail="onDetail" />
<OeeGauge title="质量率" :value="95" @detail="onDetail" />
</template>
<script setup>
import OeeGauge from './OeeGauge.vue'
const onDetail = (name) => {
console.log('查看详情:', name)
}
</script>
可用率
表现率
质量率
组件通信方式总结
| 方式 | 方向 | 场景 | Excel 类比 |
|---|---|---|---|
| Props | 父 → 子 | 传递数据(设备名、OEE 值) | 函数入参 |
| Emit | 子 → 父 | 通知事件(点击、选择) | 函数返回值 |
| Provide / Inject | 祖先 → 后代 | 跨层级传数据(主题、全局配置) | 全局变量 |
| Pinia(状态管理) | 任意组件 | 全局共享数据(用户信息、设备列表) | 共享的工作表 |
05 · 组合式 API (Composition API)
Vue 3 的组合式 API 是目前推荐的写法。相比旧的 Options API,它更灵活、代码组织更好。
Options API = 按工种分组:所有焊接工在一个车间、所有装配工在另一个车间。简单但找人不方便。
Composition API = 按产线分组:每条产线有自己的焊接、装配、质检,一条龙。方便管理和复用。
Options API vs Composition API
export default {
data() {
return { oee: 85 }
},
computed: {
oeeLevel() { return this.oee >= 85 ? '优秀' : '需改善' }
},
methods: {
refresh() { this.oee = 90 }
},
mounted() { this.refresh() }
}
<script setup>
import { ref, computed, onMounted } from 'vue'
// 数据
const oee = ref(85)
// 计算属性
const oeeLevel = computed(() => oee.value >= 85 ? '优秀' : '需改善')
// 方法
const refresh = () => { oee.value = 90 }
// 生命周期
onMounted(() => refresh())
</script>
核心 API 一览
computed — 计算属性
像 Excel 公式:=IF(A1>=85, "优秀", "需改善")。依赖的数据变了,自动重算。
const oee = ref(87)
const quality = ref(95)
// computed = Excel 的公式,依赖变化自动重算
const totalOee = computed(() => {
return ((oee.value * quality.value) / 100).toFixed(1)
})
// totalOee = 82.7(oee 或 quality 变了,自动更新)
表现率: 87% × 质量率: 95% = 综合 OEE: 82.7%
watch — 侦听器
监视数据变化,触发副作用(调接口、打日志等)。像 Excel 的「数据变化时运行宏」。
import { ref, watch } from 'vue'
const oee = ref(85)
// 当 oee 变化时,触发回调
watch(oee, (newVal, oldVal) => {
console.log(`OEE 从 ${oldVal} 变为 ${newVal}`)
// 实际场景:OEE 低于阈值 → 发送告警
if (newVal < 60) {
sendAlert(`OEE 过低: ${newVal}%`)
}
})
computed vs watch vs methods
| computed | watch | methods | |
|---|---|---|---|
| 用途 | 根据已有数据算出新值 | 数据变化时执行副作用 | 封装操作逻辑 |
| 缓存 | ✅ 有缓存(依赖不变不重算) | ❌ 每次都执行 | ❌ 每次都执行 |
| 返回值 | 有(像公式) | 无(像宏) | 可有可无 |
| Excel 类比 | =A1*B1 | VBA Change 事件 | 自定义函数 |
| 场景 | 格式化、过滤、计算 | 调 API、发告警、打日志 | 按钮点击、业务逻辑 |
06 · 生命周期
每个 Vue 组件从创建 → 挂载到页面 → 更新 → 销毁,经历一系列「生命阶段」。你可以在特定阶段插入自己的代码。
组件的生命周期 = 一台设备的开机关机流程:
- onMounted = 设备开机自检完成,开始采集数据
- onUpdated = 设备参数调整后,重新校准
- onUnmounted = 设备关机前,保存数据、断开连接
三大生命周期钩子
<script setup>
import { ref, onMounted, onUpdated, onUnmounted } from 'vue'
let timer = null
const oee = ref(0)
// ① onMounted:组件挂载后 → 开始获取数据
// 就像设备开机后,连上 MES 开始读数据
onMounted(() => {
fetchOeeData() // 首次加载 OEE 数据
// 每 5 秒刷新一次(实时看板的核心!)
timer = setInterval(() => {
fetchOeeData()
}, 5000)
})
// ② onUpdated:数据变化导致 DOM 更新后
onUpdated(() => {
console.log('看板已更新,OEE:', oee.value)
})
// ③ onUnmounted:组件销毁前 → 清理定时器!
// 就像设备关机前要断开连接,否则会内存泄漏
onUnmounted(() => {
clearInterval(timer) // ⚠️ 必须清理!否则定时器永远在跑
console.log('看板已关闭,定时器已清理')
})
const fetchOeeData = async () => {
// 模拟从后端 API 获取数据
oee.value = await getOeeFromApi()
}
</script>
在 onUnmounted 中必须 clearInterval、clearTimeout、取消 ECharts 实例。否则:
- 定时器继续跑 → 无意义的网络请求 → 内存泄漏
- 切换页面后,后台还在每 5 秒请求一次 API
- ECharts 实例不销毁 → DOM 节点泄漏
生命周期速查
| 钩子 | 时机 | 常见用途 | 工厂类比 |
|---|---|---|---|
onMounted | 组件挂载到页面后 | 获取数据、启动定时器、初始化图表 | 开机自检,开始采集 |
onUpdated | 数据变化→DOM 更新后 | 操作更新后的 DOM | 参数调整后重新校准 |
onUnmounted | 组件销毁时 | 清理定时器、取消订阅、销毁图表 | 关机保存数据 |
07 · 实战:OEE 看板组件
综合前面的知识,我们来写一个完整的 OEE 看板卡片组件,使用 Vue 3 + ECharts。
OEE(Overall Equipment Effectiveness,设备综合效率)= 可用率 × 表现率 × 质量率。
<template>
<div class="oee-dashboard">
<h2>{{ machineName }} — OEE 看板</h2>
<!-- 三个指标卡片 -->
<div class="metrics">
<MetricCard title="可用率" :value="availability" color="#7ee787" />
<MetricCard title="表现率" :value="performance" color="#89dceb" />
<MetricCard title="质量率" :value="quality" color="#d2a8ff" />
</div>
<!-- 综合得分 -->
<div class="total-oee">
<h3>综合 OEE: {{ totalOee }}%</h3>
<span :class="oeeLevel === '优秀' ? 'green' : 'yellow'">
{{ oeeLevel }}
</span>
</div>
<!-- ECharts 仪表盘图表 -->
<div ref="chartRef" class="chart" style="width: 100%; height: 300px;"></div>
<!-- 手动刷新按钮 -->
<button @click="refreshData">刷新数据</button>
</div>
</template>
<script setup>
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts'
// ============ Props ============
const props = defineProps({
machineName: { type: String, default: 'CNC-A01' }
})
// ============ 响应式数据 ============
const availability = ref(92) // 可用率
const performance = ref(88) // 表现率
const quality = ref(95) // 质量率
const chartRef = ref(null) // DOM 引用
let chartInstance = null
let timer = null
// ============ computed 计算属性 ============
const totalOee = computed(() => {
return ((availability.value * performance.value * quality.value) / 10000).toFixed(1)
})
const oeeLevel = computed(() => {
return totalOee.value >= 85 ? '优秀' : '需改善'
})
// ============ watch 侦听器 ============
// 数据变化 → 自动更新 ECharts 图表
watch(totalOee, () => {
updateChart()
})
// ============ 方法 ============
const initChart = () => {
chartInstance = echarts.init(chartRef.value)
updateChart()
}
const updateChart = () => {
if (!chartInstance) return
chartInstance.setOption({
series: [{
type: 'gauge',
data: [{ value: totalOee.value, name: 'OEE' }],
detail: { formatter: '{value}%' }
}]
})
}
const refreshData = async () => {
// 从后端 API 获取最新数据
const data = await fetch(`/api/oee/${props.machineName}`)
availability.value = data.availability
performance.value = data.performance
quality.value = data.quality
// 数据变了 → computed 自动更新 → watch 自动更新图表 ✅
}
// ============ 生命周期 ============
onMounted(() => {
initChart()
refreshData()
// 每 10 秒自动刷新(实时看板)
timer = setInterval(refreshData, 10000)
})
onUnmounted(() => {
clearInterval(timer) // 清理定时器
chartInstance.dispose() // 销毁 ECharts 实例
})
</script>
CNC-A01 — OEE 看板
- ref:三个响应式指标(availability, performance, quality)
- computed:totalOee(自动计算)、oeeLevel(自动判断等级)
- watch:totalOee 变化 → 自动更新 ECharts 图表
- onMounted:初始化图表 + 首次加载数据 + 启动定时刷新
- onUnmounted:清理定时器 + 销毁 ECharts 实例
- props:接收设备名称参数
08 · Vue vs React 速查
岗位要求「至少熟悉 Vue 或 React 其中一个」。如果你学完 Vue 后想了解 React,这里是最核心的概念对照表。
Vue = 自动手动挡(自动帮你处理 DOM 更新,模板语法更直觉)
React = 纯手动挡(JSX 全靠函数调用,更灵活但代码量稍多)
两者都能到达同一个目的地(构建 Web 应用),只是开车方式不同。
| 概念 | Vue 3 | React |
|---|---|---|
| 创建组件 | <script setup> + <template> |
function Comp() { return JSX } |
| 响应式数据 | ref(0) / reactive({}) |
useState(0) |
| 计算属性 | computed(() => ...) |
useMemo(() => ..., [deps]) |
| 侦听变化 | watch(val, fn) |
useEffect(() => {}, [deps]) |
| 生命周期 | onMounted / onUnmounted |
useEffect(() => {}, []) |
| 条件渲染 | v-if="flag" |
{flag && <Comp />} |
| 列表渲染 | v-for="item in list" :key="item.id" |
{list.map(item => <Comp key={item.id} />)} |
| 事件绑定 | @click="handleClick" |
onClick={handleClick} |
| 双向绑定 | v-model="value" |
value + onChange(手动实现) |
| 父传子 | defineProps |
函数参数 props |
| 子传父 | defineEmits |
回调函数 props.onXxx |
| 模板语法 | HTML + 指令(v-if, v-for) |
JSX(HTML 写在 JS 里) |
| 状态管理 | Pinia | Redux / Zustand |
| 路由 | Vue Router | React Router |
| 开箱体验 | 更多内置功能(指令、过渡动画) | 更轻量,功能靠生态补充 |
// ============ Vue 3 ============
<template>
<input v-model="name" />
<p v-if="name">你好, {{ name }}!</p>
</template>
<script setup>
import { ref } from 'vue'
const name = ref('')
</script>
// ============ React ============
import { useState } from 'react'
function App() {
const [name, setName] = useState('')
return (
<>
<input value={name} onChange={e => setName(e.target.value)} />
{name && <p>你好, {name}!</p>}
</>
)
}
09 · 面试考点 — Top 10
以下为 Vue 高频面试题,每题附简明答案 + 一句话记忆法。
v-if:条件为 false 时,DOM 元素不存在(真正的销毁/创建)。切换开销大,适合不频繁切换的场景(弹窗、权限控制)。
v-show:元素始终存在,只是 CSS display: none 切换。初始渲染开销大,但切换快,适合频繁切换(Tab 标签、折叠面板)。
一句话:v-if = 拔电源(彻底关机),v-show = 盖遮布(还在,只是看不见)。
ref:可以包裹任意类型(数字、字符串、布尔、对象)。JS 中通过 .value 访问,模板中自动解包。支持整个替换。
reactive:只能包裹对象/数组。直接访问属性,不需要 .value。但不能整个替换对象(会丢失响应性)。
一句话:ref 万能但要多写 .value,reactive 好用但只能包对象。
推荐:优先用 ref,对象特别复杂时用 reactive。
computed:计算属性,依赖数据变了自动重算,有缓存。必须有返回值。像 Excel 公式。
watch:侦听器,监听某个数据变化后执行副作用(调 API、打日志、发告警)。不返回值。
methods:普通方法,每次调用都执行。用于事件处理、业务逻辑。
一句话:computed 算值、watch 做事、methods 随叫随到。
key 帮 Vue 识别每个列表节点的身份,高效复用和重新排序现有元素。
没有 key 时,Vue 使用「就地复用」策略,可能导致列表更新时出现渲染错误(状态错位、动画异常)。
类比:key = 数据库主键。没有主键,更新时可能改错行。
最佳实践:用唯一的业务 ID(如设备编号),不要用 index(列表增删时 index 会变)。
| 方式 | 方向 | 适用场景 |
|---|---|---|
| Props | 父 → 子 | 传递数据(最常用) |
| Emit | 子 → 父 | 子组件通知父组件 |
| Provide / Inject | 祖先 → 后代 | 跨多层传数据(主题、配置) |
| Pinia | 任意组件 | 全局状态管理 |
| EventBus | 任意组件 | Vue 3 推荐用 mitt 库 |
一句话:Props 向下传,Emit 向上通知,Provide 跨层级,Pinia 全局共享。
keep-alive 包裹动态组件时,会缓存组件实例而不是销毁它们。
切换回来时,组件状态(表单输入、滚动位置等)还在,不用重新创建和请求数据。
类比:keep-alive = 待机而不是关机。切走时「待机」,切回来「唤醒」,状态全在。
场景:Tab 切换、列表页 → 详情页 → 返回列表页(保留搜索条件和滚动位置)。
Vue 更新 DOM 是异步的。改了数据,DOM 不会立刻变,而是在下一个「tick」统一更新。
nextTick = 等 DOM 更新完成后执行回调。
使用场景:
- 改了数据后需要操作更新后的 DOM(获取高度、聚焦输入框)
- 在
v-if条件变为 true 后操作新出现的元素
一句话:nextTick = 「等 DOM 更新完再做下一步」。
| 特性 | Vue 2 | Vue 3 |
|---|---|---|
| API 风格 | Options API | Composition API(推荐) |
| 响应式原理 | Object.defineProperty | Proxy(更强大) |
| 根实例 | new Vue() | createApp() |
| 多根节点 | ❌ 不支持 | ✅ 支持(Fragment) |
| Teleport | ❌ 无 | ✅ 传送组件到任意 DOM |
| Suspense | ❌ 无 | ✅ 异步组件加载状态 |
| TypeScript | 支持较弱 | 原生支持 |
| 性能 | 基准 | 更快(虚拟 DOM 重写) |
Vue 3 使用 Proxy 实现响应式:
- 当你用
ref()或reactive()创建数据时,Vue 用 Proxy 包裹它 - 组件渲染时「读取」数据 → Proxy 记录「谁在用这个数据」(依赖收集)
- 当你「修改」数据 → Proxy 检测到变化 → 通知所有依赖它的组件更新
类比:就像 MES 系统监控设备状态 — 有人订阅了某设备的状态,当状态变化时,MES 自动通知所有订阅者。
虚拟 DOM (Virtual DOM) = 用 JavaScript 对象来描述真实 DOM 结构的轻量副本。
- 数据变化 → 生成新的虚拟 DOM 树
- 对比新旧虚拟 DOM(Diff 算法)→ 找出差异
- 只把差异部分更新到真实 DOM(最小化 DOM 操作)
类比:就像工厂的变更单 — 不是每次都重建整条产线,而是只改需要改的工位。效率高很多。
为什么需要:直接操作真实 DOM 很慢(重排重绘),虚拟 DOM 通过批量+最小更新来提升性能。