我认为这是我的辅助函数内部的范围问题,我已经在我的组件中成功实现了相同的逻辑,并且它工作得很好
我已经使用可组合函数将 ref() 与我想要返回的变量一起使用
=> 这是我组件内的工作代码
// component.vue
setup() {
const subnavDiv = ref(false) // this is the variable that the bellow code update
onMounted(() => {
const routEl = document.querySelector('.main')
const fixedNav = document.querySelector('.main-nav')
const el = document.querySelector('.search')
let prevYPosition = 0
let directionVar = 'up'
const options = {
root: routEl,
rootMargin: `${fixedNav.offsetHeight * -1}px`,
threshold: 0.7
}
const setScrollDirection = () => {
if (routEl.scrollTop > prevYPosition) {
directionVar = 'down'
} else {
directionVar = 'up'
}
prevYPosition = routEl.scrollTop
}
const onIntersection = ([e]) => {
setScrollDirection()
if ((directionVar === 'down' && e.boundingClientRect.top > 0) || !e.isIntersecting) {
console.log('down')
subnavDiv.value = true
} else {
subnavDiv.value = false
}
if (
e.isIntersecting &&
(e.boundingClientRect.top < fixedNav.offsetHeight ||
e.boundingClientRect.top > fixedNav.offsetHeight)
) {
subnavDiv.value = false
}
}
const observer = new IntersectionObserver(onIntersection, options)
observer.observe(el)
})
return {
subnavDiv
}
}
}
}
=> 将相同的代码移出组件后
// component.vue
setup() {
const subnavDiv = ref(false)
onMounted(() => {
const rootEl = document.querySelector('.main')
const fixedNav = document.querySelector('.main-nav')
const el = document.querySelector('.search')
subnavDiv.value = onIntersect(rootEl, 0.7, fixedNav, el) // the helper function call: this line is supposed to update the value of subnavDiv
})
})
/////////////////////// $$$$$$$$$$$$$$$$$$ ///////////////
onIntersect.js // the helper function
const onIntersect = (rootElement, thresholdValue, elementToChange, elementToWatch) => {
let prevYPosition = 0
let directionVar = 'up'
const options = {
root: rootElement,
rootMargin: `${elementToChange.offsetHeight * -1}px`,
threshold: thresholdValue
}
let bool = false // this is the variable that i am trying to return from this function
const setScrollDirection = () => {
if (rootElement.scrollTop > prevYPosition) {
directionVar = 'down'
} else {
directionVar = 'up'
}
prevYPosition = rootElement.scrollTop
}
const onIntersection = ([e]) => {
setScrollDirection()
if ((directionVar === 'down' && e.boundingClientRect.top > 0) || !e.isIntersecting) {
console.log('down')
bool = true
} else {
bool = false
}
if (
e.isIntersecting &&
(e.boundingClientRect.top < elementToChange.offsetHeight ||
e.boundingClientRect.top > elementToChange.offsetHeight)
) {
bool = false
}
}
const observer = new IntersectionObserver(onIntersection, options)
observer.observe(elementToWatch)
return bool // the return (not returning any thing)
}
export default onIntersect
重要的部分是 ref,ref 模式的目的是在作用域之间通过引用传递值。
onIntersect
应该是可组合的,并遵循它们的常规约定。它应该在 setup
而不是生命周期钩子中调用。
在 Vue 中直接 DOM 访问是不可取的。如果在同一组件中创建
main
等,则它们应该是模板引用,否则可以将它们作为引用提供给可组合项,以便它可以利用反应性。在可组合项中使用 onMounted
会使其变得不灵活并且容易出现竞争条件。考虑到需要先访问elementToWatch
,可能是:
const useIntersect = (rootElRef, threshold, changedElRef, watchedElRef) => {
const bool = ref(false);
let observer;
watchEffect(() => {
const rootEl = unref(rootElRef);
const changedEl = unref(changedElRef)
const watchedEl = unref(watchedElRef);
if (rootEl && watchedEl) {
...
observer = new IntersectionObserver(onIntersection, options)
observer.observe(watchedEl)
}
return () => {
// clean up
observer?.disconnect();
}
});
return bool // the return (not returning any thing)
};
如有必要,仍然可以直接访问 DOM 元素,但无论如何它们都是引用:
...
const rootElRef = ref(null);
const isSubnav = useIntersect(rootElRef, ...);
onMounted(() => {
rootElRef.value = document.querySelector('.main');
...
作为副作用,它可以对参数的变化做出反应,例如如果需要更改选项,可以强制重新创建观察者。