Vue3学习(watchEffect,标签的ref属性,计数器,defineExpose)
一,前言
今天继续学习。
二,watchEffect
atchEffect
是 Vue 3 组合式 API 中用于响应式地执行副作用的一个函数。它与 watch
不同,不需要明确指定要监听的数据源,而是自动收集依赖。
基本用法
watchEffect
接收一个回调函数,每当回调函数中使用的任何响应式数据发生变化时,这个回调函数都会重新执行。
<template><div class="person">
<h2>当前水温:{{temp}}度</h2>
<h2>当前水位:{{sum}}cm</h2>
<button @click="changeTemp">水温加十</button>
<button @click="changesum">水位加十</button></div>
</template>
<script lang="ts" setup name="Person">
import { ref, watch,watchEffect } from 'vue'
let sum = ref(10)
let temp = ref(10)function changeTemp(){
temp.value+=10
}
function changesum(){
sum.value+=10
}
// watch实现
// watch([temp,sum],(value)=>{
// let [newTemp,newSum] = value
// console.log(newTemp,newSum)
// if(newTemp > 30||newSum > 100){
// console.log('温度过高或水位过高')
// }
// })
// watchEffect实现
watchEffect(()=>{if(temp.value > 30||sum.value > 100){console.log('温度过高或水位过高')}
})
</script>
<style>
.person{width: 400px;height: 400px;color: red;
}
</style>
<template><div><p>Count: {{ count }}</p><button @click="increment">增加</button><p>Message: {{ message }}</p><input v-model="message" placeholder="输入消息"><p>Computed: {{ computedValue }}</p></div>
</template><script setup>
import { ref, computed, watchEffect } from 'vue';// 创建响应式数据
const count = ref(0);
const message = ref('');// 创建计算属性
const computedValue = computed(() => count.value * 2);// 使用 watchEffect 监听变化
watchEffect(() => {console.log('count 变化了:', count.value);
});// 监听多个响应式数据
watchEffect(() => {console.log('message 或 computedValue 变化了:', message.value, computedValue.value);
});// 执行副作用并获取停止函数
const stop = watchEffect(() => {console.log('这个副作用可以手动停止');
});// 在某个条件下停止监听
if (count.value > 10) {stop(); // 停止这个 watchEffect
}// 执行副作用,并且可以在副作用重新执行前进行清理
watchEffect((onCleanup) => {// 创建一个定时器const timer = setTimeout(() => {console.log('定时器执行,count 值为:', count.value);}, 1000);// 注册清理函数onCleanup(() => {// 清除定时器clearTimeout(timer);console.log('副作用被清理了');});
});// 增加计数器
const increment = () => {count.value++;
};
</script>
watchEffect 的特点
- 自动追踪依赖:无需显式指定要监听的数据,回调函数中使用的任何响应式数据都会被自动追踪。
- 立即执行:
watchEffect
会在创建时立即执行一次回调函数,然后才开始监听依赖的变化。 - 清理副作用:可以通过传入的
onCleanup
函数注册清理逻辑,这些逻辑会在副作用重新执行前或组件卸载前运行。 - 返回停止函数:
watchEffect
返回一个停止函数,调用它可以手动停止监听。
与 watch 的区别
watch
需要明确指定要监听的数据源watch
默认不会立即执行,需要设置immediate: true
watch
可以获取到变化前后的值watch
更适合需要对比新旧值的场景
常见应用场景
- 数据变化时发送网络请求
- 监听路由变化执行某些操作
- 定时任务的创建与清理
- DOM 操作等副作用
watchEffect
是 Vue 3 中处理响应式副作用的强大工具,特别是在不需要明确追踪特定数据,而是希望对任何依赖变化做出反应的场景下非常有用。
三,标签的ref属性
Vue 3 中的 ref 属性和 ref API
在 Vue 3 中,ref
有两种不同但相关的概念:
- 模板 ref - 类似于 React 的 ref,用于访问 DOM 元素或组件实例
- 响应式引用 (ref API) - Vue 3 组合式 API 中的一个核心概念,用于创建响应式数据
模板 ref (Template Refs)
Vue 3 中的模板 ref 与 Vue 2 类似,但在组合式 API 中有不同的使用方式:
<template><div><input ref="inputRef" type="text" placeholder="输入内容"><button @click="focusInput">聚焦输入框</button></div>
</template><script setup>
import { ref, onMounted } from 'vue';// 创建一个模板 ref
const inputRef = ref(null);// 聚焦输入框的方法
const focusInput = () => {inputRef.value.focus();
};// 在组件挂载后访问 ref
onMounted(() => {console.log('输入框元素:', inputRef.value);
});
</script>
响应式引用 (ref API)
Vue 3 组合式 API 中的 ref()
函数用于创建响应式引用:
<template><div><p>计数器: {{ count }}</p><button @click="increment">增加</button></div>
</template><script setup>
import { ref } from 'vue';// 创建响应式引用
const count = ref(0);// 修改 ref 的值需要使用 .value
const increment = () => {count.value++;
};// 在模板中使用时不需要 .value
// 但在 JavaScript 中访问时需要
console.log(count.value); // 0
</script>
组件引用
在 Vue 3 中引用子组件实例:
<template><div><ChildComponent ref="childRef" /><button @click="callChildMethod">调用子组件方法</button></div>
</template><script setup>
import { ref, onMounted } from 'vue';
import ChildComponent from './ChildComponent.vue';// 引用子组件
const childRef = ref(null);// 调用子组件方法
const callChildMethod = () => {childRef.value?.someMethod();
};onMounted(() => {console.log('子组件实例:', childRef.value);
});
</script>
关键点总结
- 模板 ref:
- 使用
ref="someRef"
在模板中标记元素或组件 - 在脚本中使用
const someRef = ref(null)
创建对应的引用 - 访问时使用
someRef.value
获取实际的 DOM 元素或组件实例
- 使用
- 响应式引用 (ref API):
- 使用
ref()
创建响应式数据 - 在 JavaScript 中访问时需要使用
.value
- 在模板中使用时自动展开,无需
.value
- 使用
- 组合式 API 中的 ref:
- 与
reactive
不同,ref
可以用于任何类型的值,包括基本类型 - 当值是对象时,
ref
会自动转换为reactive
- 与
Vue 3 中的 ref 系统是组合式 API 的核心部分,正确使用它可以帮助你更好地组织代码并利用 Vue 的响应式系统。
四,计数器
计数器的基本概念
在计算机科学和编程中,计数器是一种基本的变量或组件,用于跟踪某个事件发生的次数。它本质上是一个存储数值的容器,通常会随着特定操作的执行而递增或递减。
计数器的用途
计数器在编程中有广泛的应用场景:
- 循环控制:在
for
循环中作为索引变量 - 事件计数:统计用户点击次数、页面访问量等
- 状态管理:跟踪应用中的特定状态变化
- 计时功能:结合定时器实现倒计时或正计时
- 游戏开发:计分系统、生命值等
Vue3计数器示例
<template><div><p>当前计数: {{ count }}</p><button @click="increment">增加</button><button @click="reset">重置</button></div>
</template><script setup>
import { ref } from 'vue';const count = ref(0);const increment = () => {count.value++;
};const reset = () => {count.value = 0;
};
</script>
五,defineExpose
基本概念
在 Vue 2 中,子组件的所有属性和方法默认都是对外暴露的,父组件可以通过 $refs
直接访问。但在 Vue 3 的 <script setup>
中,组件实例的属性和方法默认是私有的,父组件无法直接访问。这是为了更好地遵循封装原则。
defineExpose
允许我们选择性地暴露特定的属性和方法,使它们可以被父组件访问。
示例:
<template><div><ChildComponent ref="childRef" /><button @click="callChildMethod">调用子组件方法</button><p v-if="childCount">子组件计数: {{ childCount }}</p></div>
</template><script setup>
import { ref, onMounted } from 'vue';
import ChildComponent from './ChildComponent.vue';// 创建子组件引用
const childRef = ref(null);// 获取子组件暴露的计数
const childCount = computed(() => childRef.value?.count);// 调用子组件暴露的方法
const callChildMethod = () => {childRef.value?.increment();
};onMounted(() => {console.log('子组件实例:', childRef.value);
});
</script>
<template><div><p>子组件计数器: {{ count }}</p><button @click="increment">增加</button></div>
</template><script setup>
import { ref } from 'vue';// 定义内部状态
const count = ref(0);
const privateValue = ref('这是私有值');// 定义内部方法
const increment = () => {count.value++;
};const reset = () => {count.value = 0;
};// 定义私有方法
const privateMethod = () => {console.log('这是私有方法');
};// 暴露属性和方法给父组件
defineExpose({count, // 暴露计数器值increment // 暴露增加方法// 注意:reset 和 privateValue 没有被暴露,父组件无法访问
});
</script>
关键点说明
- 仅在
<script setup>
中使用:defineExpose
是<script setup>
语法特有的宏,普通的<script>
中不需要它。 - 暴露的内容:
- 可以暴露响应式状态(如
ref
或reactive
创建的) - 可以暴露方法
- 暴露的是原始引用,父组件可以直接修改
- 可以暴露响应式状态(如
- 访问方式:
- 父组件通过
ref
引用子组件 - 使用
childRef.value.property
或childRef.value.method()
访问
- 父组件通过
- 与 Vue 2 的区别:
- Vue 2 中所有属性和方法默认暴露
- Vue 3 中需要显式使用
defineExpose
暴露
实际应用场景
- 组件库开发:只暴露组件的公共 API,隐藏内部实现细节
- 复杂交互:父组件需要控制子组件的特定行为
- 过渡动画控制:父组件需要触发子组件的动画