欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 社会 > 重学vue3(三):vue3基本语法及使用

重学vue3(三):vue3基本语法及使用

2025/3/31 1:24:12 来源:https://blog.csdn.net/prdslf001001/article/details/146264619  浏览:    关键词:重学vue3(三):vue3基本语法及使用

组合式 API是vue3 的核心特性,替代 Vue2 的选项式 API,强调逻辑复用和代码组织。基本语法如下:

<script setup>
import { ref, reactive, computed, onMounted } from 'vue';// 1. 响应式数据
const count = ref(0);            // 基本类型用 ref
const user = reactive({          // 对象类型用 reactivename: 'Alice',age: 25
});// 2. 计算属性
const isAdult = computed(() => user.age >= 18);// 3. 方法
const increment = () => {count.value++;
};// 4. 生命周期钩子
onMounted(() => {console.log('组件已挂载');
});
</script><template><div><p>{{ count }}</p><button @click="increment">+1</button><p>{{ user.name }} 是否成年:{{ isAdult ? '是' : '否' }}</p></div>
</template>

一、响应式系统

1、ref 和 reactive

(1) 数据类型

ref

  • 适用于基本类型(如 numberstringboolean)。

  • 也可以用于对象或数组(内部会调用 reactive 处理)。

  • 通过 .value 访问数据。

reactive

  • 仅适用于对象或数组

  • 直接访问属性,无需 .value

(2) 使用场景

  • ref

    • 需要处理基本类型

    • 需要将数据传递到其他函数或组件时(因 ref 是对象,可以保持引用)。

  • reactive

    • 需要处理复杂对象或数组

    • 需要直接操作嵌套属性(如 state.user.name)。

2、 toRef 和 toRefs

  • toRef

    • 功能:将响应式对象(reactive 生成)的某个属性转换为一个 ref,并保持响应式连接。

    • 特点

      • 生成的 ref 与原对象的属性同步更新(修改 ref 会影响原对象,反之亦然)。

      • 如果原对象属性是非响应式的,toRef 不会使其变为响应式。

import { reactive, toRef } from 'vue';const state = reactive({ count: 0 });
const countRef = toRef(state, 'count');// 修改 ref 会更新原对象
countRef.value++;
console.log(state.count); // 1// 修改原对象也会更新 ref
state.count++;
console.log(countRef.value); // 2

toRef 的典型场景

①解构响应式对象时保持响应性

直接解构 reactive 对象会丢失响应性,但用 toRef 可以解决:

const state = reactive({ count: 0 });// ❌ 错误:解构会丢失响应性
const { count } = state;// ✅ 正确:使用 toRef 保持响应性
const countRef = toRef(state, 'count');

②将 props 的某个属性转为 ref

在组合式函数中,直接解构 props 会失去响应性,此时可以用 toRef

<script setup>
const props = defineProps(['userId']);
const userIdRef = toRef(props, 'userId'); // 保持响应性
</script>
  • toRefs
  • toRef:仅转换对象的一个属性为 ref

  • toRefs:将整个响应式对象的所有属性转换为普通对象,每个属性都是 ref

const state = reactive({ count: 0, name: 'A' });
const stateRefs = toRefs(state);
// { count: Ref<0>, name: Ref<'A'> }
console.log(stateRefs.count.value); // 0

3、 shallowRef

shallowRef只会在对象的第一层进行响应式处理,不会递归地对对象内部的属性进行响应式处理。

import { shallowRef } from 'vue';const user = shallowRef({ name: 'John', address: { city: 'New York' } });
console.log(user.value); // 输出: { name: 'John', address: { city: 'New York' } }
user.value.address.city = 'San Francisco'; // 修改深层属性
console.log(user.value); // 输出: { name: 'John', address: { city: 'San Francisco' } }
// 注意,修改深层属性不会触发视图更新

4、computed

computed 用于创建计算属性,它基于一个或多个响应式数据(如 ref 或 reactive)计算出一个新的值。计算属性是惰性的,只有在被访问时才会重新计算,并且会缓存结果以提高性能。

特点
  • 依赖追踪:自动追踪依赖的响应式数据,仅在依赖变化时重新计算。
  • 缓存机制:计算结果会被缓存,直到依赖数据变化才重新计算。
  • 返回值:必须返回一个值,通常用于派生数据。
使用场景
  • 需要根据响应式数据计算派生值。
  • 在模板中多次使用某个计算结果,利用缓存提升性能。
  • 将复杂逻辑封装成一个属性。
<script setup>
import { ref, computed } from 'vue';const firstName = ref('John');
const lastName = ref('Doe');const fullName = computed(() => `${firstName.value} ${lastName.value}`);
</script><template><p>{{ fullName }}</p> <!-- 输出 'John Doe',依赖变化时自动更新 -->
</template>

5、watch

watch 用于监听指定的响应式数据(如 ref、reactive 或计算属性)的变化,并在数据变化时执行回调函数。

特点
  • 手动指定依赖:需要明确指定监听的数据源。
  • 支持深度监听:通过 { deep: true } 选项可监听对象或数组的深层变化。
  • 新旧值:回调函数可接收新值和旧值,便于比较。
  • 多数据监听:可通过数组或对象同时监听多个数据源。
使用场景
  • 数据变化时需要执行副作用,如发送请求、更新 DOM。
  • 需要监听深层对象或数组变化。
  • 需要执行异步操作或依赖新旧值比较。

6、watchEffect

watchEffect 是一个简化的监听工具,它会自动追踪回调函数中使用的响应式依赖,并在依赖变化时重新运行回调,无需手动指定监听目标。

特点
  • 自动依赖追踪:Vue 自动检测回调中使用的响应式数据。
  • 立即执行:创建时立即运行一次以收集依赖。
  • 不支持新旧值:回调函数不接收新旧值参数。
  • 副作用优先:适合执行不需要返回值的操作。
使用场景
  • 多个响应式数据变化时执行相同副作用。
  • 依赖关系动态变化,不想手动管理。
  • 简单的副作用逻辑,如日志记录或 DOM 操作。

二、Class 与 Style 绑定

1. Class 绑定

Class 绑定让你可以动态切换 CSS 类。Vue 3 提供了多种语法来实现这一点:

2.1.1. 对象语法

可以用一个对象来绑定 Class,对象的键是 CSS 类名,值是布尔值,表示是否应用该类。

<template><div :class="{ active: isActive, 'text-danger': hasError }"></div>
</template><script setup>
import { ref } from 'vue';const isActive = ref(true);
const hasError = ref(false);
</script>

解释

  • :class 是 v-bind:class 的缩写,用于动态绑定。
  • 如果 isActive 为 true,<div> 会有 active 类。
  • 如果 hasError 为 true,<div> 会有 text-danger 类。

2.1.2. 数组语法

也可以用数组列出要应用的类名

<template><div :class="[activeClass, errorClass, 'static-class']"></div>
</template><script setup>
import { ref } from 'vue';const activeClass = ref('active'); // 动态类名
const errorClass = ref('');        // 空类名不渲染
</script>

渲染结果:

<div class="active static-class"></div>

如果你也想在数组中有条件地渲染某个 class,你可以使用三元表达式:

<div :class="[isActive ? activeClass : '', errorClass]"></div>

解释

  • errorClass 会一直存在,但 activeClass 只会在 isActive 为真时才存在。

2.1.3. 混合对象和数组

<template><div :class="[isActive ? 'active' : '', { 'text-danger': hasError }]"></div>
</template><script setup>
import { ref } from 'vue';const isActive = ref(true);
const hasError = ref(false);
</script>

解释

  • isActive ? 'active' : '':如果 isActive 为 true,应用 active 类,否则应用空字符串。
  • { 'text-danger': hasError }:如果 hasError 为 true,应用 text-danger 类。

2.1.4. 绑定组件根元素的 Class

在自定义组件上绑定 Class 时,类会应用到组件的根元素。

子组件 Child.vue
<template><div class="child-root"><!-- 父组件传递的 class 会合并到根元素 --></div>
</template>
父组件使用:
<template><Child class="parent-class" />
</template>
渲染结果:
<div class="child-root parent-class"></div>

2.style 绑定

Style 绑定用于动态设置内联样式,同样支持对象和数组语法。

2.2.1. 对象语法

用对象绑定样式,键是 CSS 属性名,值是属性值。

<template><div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
</template><script setup>
import { ref } from 'vue';const activeColor = ref('red');
const fontSize = ref(16);
</script>

解释

  • :style 是 v-bind:style 的缩写。
  • color 被设置为 red。
  • fontSize 被设置为 16px(注意单位需要手动拼接)。
  • 属性名可以用驼峰式(如 fontSize)或短横线分隔(如 font-size),推荐驼峰式。

2.2.2. 数组语法

用数组应用多个样式对象。

<template><div :style="[baseStyles, overridingStyles]"></div>
</template><script setup>
import { reactive } from 'vue';const baseStyles = reactive({color: 'blue',fontSize: '14px'
});const overridingStyles = reactive({fontSize: '18px'
});
</script>

解释

  • baseStyles 设置 color: blue 和 fontSize: 14px。
  • overridingStyles 设置 fontSize: 18px,会覆盖前面的 fontSize。
  • 最终结果:color: blue; font-size: 18px。

2.2.3. 自动前缀

Vue 3 会自动为某些样式属性添加浏览器前缀。例如:

<template><div :style="{ transform: 'rotate(45deg)' }"></div>
</template>

Vue 会自动生成 -webkit-transform 等前缀,确保兼容性。

2.2.4. 多值绑定(Vue 3.2+)

可以为单个属性提供多个值,Vue 会应用最后一个支持的值。

<template><div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
</template>

3.使用计算属性

对于复杂逻辑,可以用 computed 生成 Class 或 Style。

2.3.1. Class 绑定示例

<template><div :class="classes"></div>
</template><script setup>
import { computed, ref } from 'vue';const isActive = ref(true);
const hasError = ref(false);const classes = computed(() => ({active: isActive.value,'text-danger': hasError.value
}));
</script>

解释:classes 根据 isActive 和 hasError 的值动态返回类对象。

2.3.2. Style 绑定示例

<template><div :style="styles"></div>
</template><script setup>
import { computed, ref } from 'vue';const color = ref('red');
const fontSize = ref(16);const styles = computed(() => ({color: color.value,fontSize: `${fontSize.value}px`
}));
</script>

解释:styles 根据 color 和 fontSize 的值动态返回样式对象。

4.注意事项

  • 响应式:绑定的 Class 和 Style 会随数据变化自动更新。
  • 与静态共存:静态的 class 和 style 可以与动态绑定共用。
<template><div class="static" :class="{ active: isActive }" style="color: blue" :style="{ fontSize: fontSize + 'px' }"></div>
</template>

结果:<div> 始终有 static 类和 color: blue,同时根据 isActive 和 fontSize 动态应用其他值。

三、watch和watchEffect用法细说:

1、watch:

3.1.1. 核心特点

  • 显式指定监听的数据源(需手动列出依赖)。

  • 惰性执行:默认不会立即执行回调,除非设置 immediate: true

  • 适合场景:需要精确控制监听目标和回调逻辑

3.1.2. 基本语法

import { watch, ref } from 'vue';const data = ref(value);watch(source, // 监听的数据源(ref、reactive、getter 函数或数组)(newVal, oldVal) => { /* 回调逻辑 */ },{ immediate: false, // 是否立即执行回调deep: false       // 是否深度监听对象/数组}
);

3.1.3. 代码示例

①监听单个数据源

<script setup>
import { ref, watch } from 'vue';const count = ref(0);// 监听 count 的变化
watch(count, (newVal, oldVal) => {console.log(`count 从 ${oldVal} 变为 ${newVal}`);
});const increment = () => {count.value++;
};
</script><template><button @click="increment">+1</button>
</template>

②监听多个数据源

<script setup>
import { ref, watch } from 'vue';const x = ref(0);
const y = ref(0);// 监听多个 ref
watch([x, y], ([newX, newY], [oldX, oldY]) => {console.log(`坐标变化:(${oldX},${oldY}) → (${newX},${newY})`);
});
</script>

③深度监听对象

<script setup>
import { reactive, watch } from 'vue';const user = reactive({ name: 'Alice', address: { city: 'Beijing' }
});// 深度监听 user 对象
watch(user,(newVal) => {console.log('用户信息变化:', newVal);},{ deep: true }
);const changeCity = () => {user.address.city = 'Shanghai'; // 触发回调
};
</script>

④监听对象中某个特定属性

通过 getter 函数 明确指定要监听的属性,确保能精准追踪目标属性的变化。

<script setup>
import { reactive, watch } from 'vue';const obj = reactive({ a: 1, b: 2 });// 监听 obj.a 的变化
watch(() => obj.a, // getter 函数返回目标属性(newVal, oldVal) => {console.log(`obj.a 从 ${oldVal} 变为 ${newVal}`);}
);// 修改 obj.a 会触发回调
const changeA = () => {obj.a++;
};
</script><template><button @click="changeA">修改 obj.a</button>
</template>

注意:

  • getter 函数 只是监听对象中某个指明的属性;比如;监听()=>obj.a;只有当obj.a变化了才会触发回调函数(obj.b变化了不触发);
  • 深度监听{deep:true}:是监听整个对象及其所有嵌套属性;比如监听obj对象,对象的任意属性(包括嵌套)变化时均会触发;newVal 和 oldVal 相同(因为对象引用未变)

2、watchEffect

3.2.1. 核心特点

  • 自动收集依赖回调函数中使用的响应式数据会被自动追踪

  • 立即执行:回调函数会立即执行一次。

  • 适合场景:依赖多个数据且不需要旧值的场景。

3.2.2. 基本语法

import { watchEffect } from 'vue';const stop = watchEffect((onCleanup) => {// 自动追踪依赖// 执行副作用逻辑return () => { // 清理逻辑(如取消请求)};
});// 手动停止监听
stop();

3.2.3. 代码示例

①自动跟着依赖

<script setup>
import { ref, watchEffect } from 'vue';const count = ref(0);
const double = ref(0);// 自动追踪 count 和 double
watchEffect(() => {double.value = count.value * 2;console.log(`double 值:${double.value}`);
});const increment = () => {count.value++;
};
</script>

②清理副作用

<script setup>
import { watchEffect } from 'vue';watchEffect((onCleanup) => {const timer = setInterval(() => {console.log('定时器运行中...');}, 1000);// 清理定时器(组件卸载或依赖变化时触发)onCleanup(() => {clearInterval(timer);console.log('定时器已清理');});
});
</script>

3.2.4. 注意事项

①避免无限循环

在回调中修改监听的数据可能导致无限循环:

// ❌ 错误:修改 count 会再次触发回调
watchEffect(() => {count.value++;
});

②异步操作处理

在 watchEffect 中处理异步操作时,使用 onCleanup 避免竞态条件:

watchEffect(async (onCleanup) => {let isValid = true;onCleanup(() => (isValid = false)); // 标记请求已过期const data = await fetchData();if (isValid) {// 处理有效数据}
});

③手动停止监听

在组件卸载时手动停止监听(尤其是 watchEffect):

const stop = watchEffect(() => { /* ... */ });// 组件卸载时
onUnmounted(() => stop());

④性能优化

  • 使用 flush: 'post' 确保回调在 DOM 更新后执行:

    watchEffect(() => { /* ... */ }, { flush: 'post' });
  • 避免在 watch 中深度监听大型对象。

3、flush进一步解释:

`flush` 是 Vue 3 中 `watch` 和 `watchEffect` 的一个配置选项,用于控制**副作用函数(回调)的执行时机**。  
简单来说,它决定了当数据变化时,监听的回调函数是立即执行,还是等 DOM 更新后再执行

3.3.1.flush 的三种模式

  • 'pre'(默认值)

    • 触发时机:在组件更新前执行回调。

    • 特点:此时 DOM 尚未更新,因此无法访问最新的 DOM 状态。

    • 适用场景:需要在数据变化后、DOM 更新前执行逻辑(如验证数据或准备某些状态)。

  • 'post'

    • 触发时机:在组件更新后执行回调。

    • 特点:此时 DOM 已更新,可以安全操作 DOM 或访问最新的渲染结果。

    • 适用场景:需要依赖 DOM 更新的操作(如测量元素尺寸、触发第三方库的布局计算)。

  • 'sync'

    • 触发时机同步触发回调,即依赖的数据变化后立即执行。

    • 特点:可能导致回调被频繁触发(如一个操作修改多个依赖值),需注意性能问题。

    • 适用场景:极少用,仅在需要极低延迟响应时使用(如实现复杂的同步逻辑)。

3.3.2.flush 的使用场景例子

①`flush: 'post'`(常用)

在 DOM 更新后执行操作(如聚焦输入框)

  watchEffect(() => {// DOM 已更新,可以安全操作inputRef.value.focus();},{ flush: 'post' } // 确保 DOM 更新后执行);

②flush: 'pre'(默认值)

在组件更新前处理数据(如读取旧 DOM 状态)

watch(() => data.value,(newVal, oldVal) => {// 组件更新前记录旧 DOM 高度const oldHeight = element.clientHeight;},{ flush: 'pre' } // 默认值,可省略
);

③flush: 'sync'(特殊需求)

立即响应数据变化(如调试或实时同步数据)

watch(() => count.value,(newVal) => {console.log('Count 立即变化为:', newVal); // 同步输出},{ flush: 'sync' }
);

4、副作用的解释:

3.4.1:副作用的概述

  • 副作用:在 watchEffect 回调中执行的异步或外部操作(如定时器、事件监听、请求等)。

  • 清理函数:通过 onCleanup 注册的函数,用于在以下时机执行清理逻辑:

    • 依赖变化:当依赖的响应式数据变化,触发新的回调前。

    • 组件卸载:当组件卸载时,自动清理所有未完成的副作用。

//基本语法
watchEffect((onCleanup) => {// 执行副作用(如定时器、请求)const timer = setInterval(() => {console.log('定时器运行中...');}, 1000);// 注册清理函数onCleanup(() => {clearInterval(timer); // 清理定时器console.log('定时器已清理');});
});

3.4.2:代码执行流程

①副作用产生

  • 触发时机
    当组件挂载时,watchEffect 会立即执行回调函数。

  • 操作内容
    在回调中创建了一个定时器 timer,它会每秒执行一次 console.log('每秒执行一次')

②清理副作用

  • 触发清理的时机

    • 依赖变化:如果回调中使用了响应式数据(如 ref 或 reactive),当这些数据变化时,Vue 会重新执行回调函数。在重新执行前,会先调用上一次注册的清理函数。

    • 组件卸载:当组件被销毁时,Vue 会自动触发清理函数。

  • 清理操作
    调用 clearInterval(timer) 清除定时器,停止日志输出,避免内存泄漏。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词