一、Vue 绑定属性的核心问题
(一)核心问题
绑定属性时,即使绑定的值是常量 false,也需要使用 : 前缀。关键认知:: 的作用是区分字符串 vs JavaScript 值,而非 "固定值 vs 变量"。
(二)关键对比
1. 不用 :(HTML 属性)
ini
<el-form hide-required-asterisk="false">
- 浏览器解析结果:
props.hideRequiredAsterisk = "false"(字符串类型) - JavaScript 中逻辑:非空字符串均为 truthy,
if ("false")会执行,最终等效于true
2. 用 :(JavaScript 表达式)
ruby
<el-form :hide-required-asterisk="false">
- Vue 解析结果:
props.hideRequiredAsterisk = false(布尔类型) - JavaScript 中逻辑:
if (false)不会执行,符合预期效果
(三)实际测试验证
| 代码写法 | 解析结果 | 是否正确 |
|---|---|---|
<el-form hide-required-asterisk="false"> |
等效于 :hide-required-asterisk="'false'" |
❌ |
<el-form :hide-required-asterisk="'false'"> |
等效于 :hide-required-asterisk="true" |
❌ |
<el-form :hide-required-asterisk="false"> |
解析为布尔值 false |
✅ |
(四): 的真正作用
告诉 Vue:"把引号里的内容当作 JavaScript 代码执行,而非字符串"。
(五)完整使用示例
场景 1:传递字符串(无需 :)
ini
<el-input placeholder="请输入">
- 解析结果:
placeholder = "请输入"(字符串类型)
场景 2:传递数字(需要 :)
ini
<el-input :maxlength="100">
- 解析结果:
maxlength = 100(数字类型,非字符串 "100")
场景 3:传递布尔值(需要 :)
ruby
<el-form :hide-required-asterisk="false">
- 解析结果:
hideRequiredAsterisk = false(布尔类型)
场景 4:传递对象(需要 :)
ini
<el-form :model="{ name: 'test' }">
- 解析结果:
model = { name: 'test' }(对象类型)
场景 5:传递变量(需要 :)
ruby
<el-form :hide-required-asterisk="isReadOnly">
- 解析结果:
hideRequiredAsterisk = isReadOnly(变量对应的值)
(六)代码结论
ruby
<el-form :hide-required-asterisk="false">
false是固定值,此表述正确- 必须使用
:,原因是要传递布尔类型(JavaScript 值),而非字符串 - 使用
:的核心依据是值的类型,而非 "是否会变化"
(七)总结
: 的核心作用是区分 "字符串" 与 "JavaScript 代码执行结果",而非 "固定值 vs 变量"。本质:: 表示 "这是 JavaScript 代码,请执行它"。
二、computed/useMemo 触发差异(Vue3/React 响应式本质区别)
(一)核心问题
Vue3 中 computed 函数是否会在 "依赖更新" 和 "视图渲染" 时多次触发?为何与 React useMemo 行为不同?这一差异的本质是框架响应式设计理念的区别吗?
(二)核心结论
Vue3 computed 函数仅在依赖数据变化时重新执行,视图渲染时若依赖未变则复用缓存,不会额外触发;其与 React useMemo 的行为差异,核心是 Vue "响应式 getter 驱动" 与 React "声明式依赖数组驱动" 的设计理念不同,而非 "两次触发" 的差异。
(三)框架响应式本质差异(Vue3 vs React)
1. Vue3 Computed 工作原理
-
核心定位:computed 是 "响应式的 getter",与响应式数据(ref/reactive)深度绑定。
-
触发逻辑:
- 当依赖的响应式数据(如
snapshotData.value)发生变化时,computed 函数会自动重新执行。 - 模板中读取
{{ formatAmount }}时,仅当依赖已变化,才会触发重新计算;依赖未变则直接复用缓存结果。
- 当依赖的响应式数据(如
-
代码示例:
javascript
const formatAmount = computed(() => {
console.log("snapshotData.value", snapshotData.value); // 仅依赖变化时打印
return snapshotData.value ? `+${snapshotData.value.amount}` : "+0";
});
2. React useMemo 工作原理
-
核心定位:useMemo 是 "声明式的缓存计算工具",依赖显式配置的依赖数组。
-
触发逻辑:
- 仅在 "依赖数组中的值变化" 或 "组件首次渲染" 时,执行计算函数。
- JSX 中使用
{formatAmount}仅为读取缓存值,无论视图如何渲染,只要依赖未变,都不会重新执行计算函数。
-
代码示例:
javascript
const formatAmount = useMemo(() => {
console.log("snapshotData", snapshotData); // 仅依赖数组变化时打印
return snapshotData ? `+${snapshotData.amount}` : "+0";
}, [snapshotData]); // 显式依赖数组
(四)关键特性对比表
| 特性 | Vue3 Computed | React useMemo |
|---|---|---|
| 核心定位 | 响应式 getter | 声明式缓存计算工具 |
| 触发时机 | 依赖响应式数据变化时 | 依赖数组变化 / 组件首次渲染时 |
| 视图使用时 | 依赖已变则重新计算,否则复用缓存 | 仅读取缓存值,不触发重新计算 |
| 依赖配置 | 自动追踪响应式依赖,无需手动配置 | 需显式声明依赖数组 |
| 内部打印行为 | 依赖变化时打印(与计算执行同步) | 依赖数组变化时打印(仅执行一次) |
(五)"打印多次" 的真实原因
并非 "依赖更新 + 视图渲染" 两次触发,而是:
- 初始渲染时,依赖数据(如
snapshotData.value)为初始值(如null),computed 执行一次并打印。 - 业务操作后(如提交表单),依赖数据更新(如变为
{ amount: 100 }),computed 再次执行并打印。 - 两次打印均是 "依赖变化" 导致的计算执行,与视图渲染本身无直接关联。
(六)优化建议(避免不必要的打印 / 计算)
- 调试场景:使用 Vue3 的
watchEffect专门监听数据变化,替代 computed 内打印。
javascript
watchEffect(() => {
console.log("snapshotData 变化了:", snapshotData.value); // 仅监听变化打印
});
- 业务场景:在数据更新的源头(如提交函数、赋值逻辑)打印,明确数据变更时机。
ini
const handleSubmit = () => {
snapshotData.value = { amount: formData.amount };
console.log("快照数据更新:", snapshotData.value); // 源头打印更精准
};
- 生产环境:移除 computed 内的调试打印,避免冗余执行。
(七)总结
- 差异本质:Vue3 是 "响应式驱动",computed 自动追踪依赖、依赖变则计算;React 是 "声明式驱动",useMemo 仅按依赖数组变化执行计算,二者是框架设计理念的差异,无优劣之分。
- 关键认知:Vue3 computed 不会因 "单纯视图渲染" 额外触发,多次执行均源于依赖变化;这一特性是 Vue 响应式系统的体现,而非设计缺陷。
三、Vue3 reactive
(一)核心定义
reactive 是 Vue 3 Composition API 中用来创建响应式对象的核心函数,作用是让数据具备 "变化自动触发视图更新" 的特性。
(二)基本概念
1. 什么是响应式?
响应式意味着:当数据变化时,使用这些数据的地方(如模板、计算属性等)会自动更新。
php
import { reactive } from "vue";
// 创建响应式对象
const state = reactive({
count: 0,
message: "Hello"
});
// 数据修改后,依赖该数据的视图自动更新
state.count++;
2. 实际代码应用(金额调整组件)
javascript
const formData = reactive({
adjustType: 1, // 调整类型(+/-)
amount: undefined, // 金额
remark: "" // 说明
});
- 表单绑定
v-model="formData.amount"时,用户输入会自动同步到formData.amount。 - 确认弹窗中
{{ formatAmount }}依赖formData,会随数据变化自动更新。
(三)响应式工作原理
xml
<template>
<!-- 1. 显示数据:依赖 formData.amount -->
<div>金额:{{ formData.amount }}</div>
<!-- 2. 修改数据:v-model 绑定响应式属性 -->
<input v-model="formData.amount" />
</template>
<script setup>
import { reactive } from "vue";
const formData = reactive({
amount: 0
});
// 3. 输入框值改变 → formData.amount 自动更新 → 模板显示同步更新
</script>
(四)reactive vs 普通对象
| 类型 | 代码示例 | 响应式效果 |
|---|---|---|
| 普通对象 | const formData = { amount: 0 }; formData.amount = 300; |
❌ 模板不会更新 |
| 响应式对象 | const formData = reactive({ amount: 0 }); formData.amount = 300; |
✅ 模板自动更新 |
(五)reactive 的核心特点
-
仅支持对象类型
- 支持:对象、数组、Map 等复杂类型(
reactive({ count: 0 })、reactive([1,2,3]))。 - 不支持:数字、字符串等基本类型(
reactive(0)无效,需用ref)。
- 支持:对象、数组、Map 等复杂类型(
-
深层响应式嵌套对象的属性同样具备响应式,修改任意层级属性都会触发更新:
php
const state = reactive({
user: {
name: "张三",
address: { city: "上海" }
}
});
state.user.address.city = "北京"; // ✅ 触发响应式更新
- 直接访问属性 无需
.value,直接读写属性即可:
ini
const formData = reactive({ amount: 300 });
console.log(formData.amount); // 300(直接访问)
formData.amount = 500; // 直接赋值修改
(六)reactive vs ref 对比
| 特性 | reactive |
ref |
|---|---|---|
| 适用类型 | 对象、数组等复杂类型 | 任何类型(基本类型、对象) |
| 访问方式 | 直接访问:state.count |
脚本中需 .value:count.value |
| 模板中使用 | {{ state.count }} |
{{ count }}(自动解包,无需 .value) |
| 解构特性 | ❌ 直接解构会失去响应式 | ✅ 配合 toRefs 可保持响应式 |
代码示例对比
ini
import { reactive, ref } from "vue";
// reactive 用法(对象)
const formData = reactive({ amount: 0 });
formData.amount = 300;
// ref 用法(基本类型)
const count = ref(0);
count.value = 300;
(七)实际应用场景
- 表单数据管理(如金额调整组件)
php
const formData = reactive({ adjustType: 1, amount: undefined, remark: "" });
- 组件状态管理
php
const state = reactive({ user: null, isLoading: false, error: null });
state.isLoading = true; // 状态变化触发视图更新
- 列表数据处理
php
const todoList = reactive({ items: [], filter: "all" });
todoList.items.push({ id: 1, text: "学习 Vue 3" }); // 列表更新自动渲染
(八)注意事项(避免失去响应式)
-
禁止直接解构
- 错误:
const { count } = reactive({ count: 0 }); count++(失去响应式)。 - 正确:使用
toRefs保持响应式:
- 错误:
ini
import { toRefs } from "vue";
const state = reactive({ count: 0 });
const { count } = toRefs(state);
count.value++; // ✅ 正常触发更新
-
不能整体替换对象
- 错误:
let state = reactive({ count: 0 }); state = reactive({ count: 100 })(原响应式关联失效)。 - 正确:修改属性或使用
Object.assign:
- 错误:
ini
state.count = 100; // 直接修改属性
Object.assign(state, { count: 100 }); // 合并更新
(九)总结
reactive 的核心价值是为对象类型数据添加 "自动响应式" 能力:
- ✅ 数据变化 → 视图自动同步,无需手动操作 DOM。
- ✅ 适配表单、状态、列表等多种业务场景。
- ✅ 深层响应式设计,嵌套对象无需额外处理。
- ⚠️ 注意避免 "解构" 和 "整体替换",防止失去响应式。
- ⚠️ 基本类型需用
ref,reactive仅适用于复杂对象类型。