阅读视图
vue hooks编程
我们来想想这段效果怎么实现,一个鼠标在窗口移动,页面上不断实时更新x与y的值
很简单的一段逻辑,相信各位大佬一看就会,不就用两个响应式数据,如然后监听mousemove吗,各位大佬且别急,听小编慢慢到来
我们来看看最简单传统的写法
<template>
<div>
<p >x:{{ mousePos.x }}</p>
<p>y:{{ mousePos.y }}</p>
</div>
</template>
<script setup>
const mousePos=reactive({x:0,y:0})
function handleMouseMove(e) {
mousePos.x=e.clientX
mousePos.y=e.clientY
console.log("haizai")
}
window.addEventListener('mousemove', handleMouseMove)
</script>
我们先不探讨这段代码存在的问题,我们先来看看这个需求,添加一个按钮,实现上述组件的v-if功能 我们来看看根组件的代码
<template>
<div>
<MousePos v-if="showMouse" />
<button @click="toggle">切换</button>
</div>
</template>
<script setup>
import {
ref
} from 'vue'
import MousePos from './components/MousePos.vue'
const showMouse=ref(true)
const toggle=()=>{
showMouse.value=!showMouse.value
}
</script>
看起来我们好像完成了这个需求,但是上述代码有致命问题,各位大佬先仔细想想,我们先探讨用到vue中的狗钩子函数进行上述需求的完善
我们来看看更新后的代码
<template>
<div>
<p >x:{{ mousePos.x }}</p>
<p>y:{{ mousePos.y }}</p>
</div>
</template>
<script setup>
import {
reactive,
onMounted,
onUnmounted
} from 'vue'
// const x=ref(0)
// const y=ref(0)
const mousePos=reactive({x:0,y:0})
function handleMouseMove(e) {
mousePos.x=e.clientX
mousePos.y=e.clientY
console.log("我还在")
}
onMounted(()=>{
window.addEventListener('mousemove', handleMouseMove)
})
</script>
<style scoped>
</style>
onMounted钩子函数在挂载前设置了一个监听器我们一样实现了这个功能,但是各位大佬应该很敏感的反映到,这里的致命问题--内存泄漏
我们来看看什么是常见的内存泄漏
- 全局变量:未被清理的全局变量会一直占用内存。
- 事件监听器:添加了事件监听器但在不再需要时没有移除它们。
-
定时器:设置的
setInterval
或setTimeout
没有被清除。 - 闭包:闭包可能会意外地保持对大对象的引用,阻止垃圾回收机制回收这些对象。
- Vue 组件中的响应式数据:组件卸载后未正确清理响应式数据或侦听器。
如果我们把代码运行就会发现即使点击了切换组件,即把组件利用v-if销毁,控制台也一直在鼠标移动的时候输出我还在,所有我们发现即使组件销毁了这个定时器依旧没有在内存中删掉,一直浪费性能,所以我们重新来修改修改我们的代码
<template>
<div>
<p >x:{{ mousePos.x }}</p>
<p>y:{{ mousePos.y }}</p>
</div>
</template>
<script setup>
import {
reactive,
onMounted,
onUnmounted
} from 'vue'
// const x=ref(0)
// const y=ref(0)
const mousePos=reactive({x:0,y:0})
function handleMouseMove(e) {
mousePos.x=e.clientX
mousePos.y=e.clientY
console.log("haizai")
}
onMounted(()=>{
window.addEventListener('mousemove', handleMouseMove)
})
onUnmounted(()=>{
window.removeEventListener('mousemove', handleMouseMove)
})
</script>
<style scoped>
</style>
我们上述完成了一个小小的响应式功能,并且利用组建的生命周期钩子函数在组件销毁时自动删除了这个定时器,但是各位大佬再详细我们还能优化吗
我们今天重点说说这个思想-响应式数据+ui=组件
很明显上述的响应式数据与鼠标移动的组件放在一起,如果我们采用这个思想 把ui与数据分离,这样我们的数据业务不仅能更好的维护,而且更加符合es6模块化思想,当我们做大型项目时,协同性也会更优秀
我们先来src创建一个hooks目录。里面封装我们的响应式业务逻辑,我们来看看这段代码
import { ref,onMounted ,onUnmounted} from 'vue'
export function useMouse() {
alert("hello")
}
export function useMousePosition() {
const x=ref(0)
const y=ref(0)
function update(e){
x.value=e.pageX
y.value=e.pageY;
}
onMounted(()=>{
window.addEventListener('mousemove',update)
})
onUnmounted(()=>{
window.removeEventListener('mousemove',update)
})
return {x,y};
}
我们用export导出这个模块函数,并且请记住return这个响应式数据我们来看看鼠标移动这个组件代码
<template>
<div>
<p>x: {{ mousePos.x }}</p>
<p>y: {{ mousePos.y }}</p>
</div>
</template>
<script setup>
import { useMousePosition,useMouse } from '../hooks/useMouse.js';
// 调用 useMouse 并接收整个响应式对象
const mousePos = useMousePosition();
// 如果你需要分别访问 x 和 y,可以继续使用 mousePos.x 和 mousePos.y
// const { x, y } = mousePos; // 这样做仍然会保持响应性,因为 mousePos 是响应式的
</script>
<style scoped>
/* 添加必要的样式 */
</style>
我们把业务逻辑封装在一个模块,并且导出,这个模块还具有其他功能,这样我们只要在想用的组件中利用es6的解构便能轻松实现逻辑的复用与方便的维护