vue3实时获取vnode节点距离顶部的top值,onUpdated也可以这样用,实现useElementOffset的hook

357 阅读1分钟

今天,做pc后台新系统进行layout布局的时候,有一个需求,页面布局如图 image.png 其实这种常规布局,侧边栏固定菜单,我们直接fixed定位,右边栏直接使用margin-left就好了,主要的问题是要如何实时计算content内容块的高度,在header内容随时会改变的情况下。

其实,我的想法是实时获取content的距离可视化区域的top值,然后通过100vh - top值 - footer固定高度来算出这块内容的高度。

所以问题来了,如何实时获取这个top值呢?这里碰了几个坑,分享下,我尝试使用watchEffectvueuse插件里的useIntersectionObserver实时获取elementRef的top值的更新,但发现不管怎么操作,elementRef都只更新一次,当变化的时候根本监听不到,这是为啥呢?真的一脸懵逼。懂得帮忙解答下哈。

于是,我转了另一种想法,既然更新时监听,onUpdated岂不是很香,哇塞,终于找到了这个生命周期的应用场景了,于是,我直接封装了一个useElementOffset的hook方法,直接看代码:

import { ref, onMounted, onUpdated, nextTick } from 'vue'

/**
 * @params elementRef 对应的绑定vnode 
 * /
export const useElementOffset = (elementRef) => {
    const offsetTop = ref(0)

    // 获取elementRef的top值
    const updateOffsetTop = () => {
        const element = elementRef.value
        if (element && element?.$el) {
            const top = elementRef.value.$el?.getBoundingClientRect?.()?.top
            offsetTop.value = top ?? element.$el?.offsetTop
        }
    }

    onMounted(() => {
        // 首次渲染期间更新一次
        updateOffsetTop()
    })

    onUpdated(async () => {
        // 防止获取不到top或者值不准确
        await nextTick()
        // 每次更新,就重新获取它的top值
        updateOffsetTop()
    })

    return { offsetTop }
}

其实这个还是有点bug,如页面resize时,根本没实时获取,所以要再监听resize变化时会更好。但因为只是后台系统,所以就没去较真这一个。

该方法使用方式如下(以jsx写法为例):


setup() {
    const myRef = ref();
    const { offsetTop } = useElementOffset(myRef);
    
    return (<div ref={myRef} style={{height: `calc(100vh - ${offsetTop}px)`}}></div>)
}