vue3,移动端项目使用 vant 框架
UI 想要的效果是 van-tabs 组件的下划线比内容略长,可组件本身只能通过 line-width 设置
静态内容还好说,可如图例,标题的宽度是随着内容动态变化的,因此我二次封装了 LocalTabs 组件,使用的时候只需要将 van-tabs 替换为 LocalTabs 就可以了
<script setup>
let tabsRef = ref()
const dealLineBar = () => {
const tabDomList = tabsRef.value.$el.querySelectorAll('.van-tab')
const activeTabWidth = tabDomList[tabsRef.value.active].querySelector('span').clientWidth
const lineDom = tabsRef.value.$el.querySelector('.van-tabs__line')
lineDom.style.width = activeTabWidth + 4 + 'px'
}
const onChange = () => {
setTimeout(() => {
dealLineBar()
}, 20)
}
onMounted(() => {
setTimeout(() => {
dealLineBar()
}, 100)
tabsRef.value.$el.querySelectorAll('.van-tab span').forEach((dom, index) => {
const observer = new MutationObserver(() => {
if (tabsRef.value.active == index) {
setTimeout(() => {
dealLineBar()
}, 0)
}
})
observer.observe(dom, {
characterData: true,
subtree: true,
})
})
})
</script>
<template>
<div :class="['local-tabs-wrap']">
<van-tabs ref="tabsRef" v-bind="$attrs" @change="onChange">
<slot></slot>
<template #nav-right>
<slot name="nav-right"> </slot>
</template>
</van-tabs>
</div>
</template>
<style lang="scss" scoped>
.local-tabs-wrap {
}
</style>
vue3 中使用 v-bind="$attrs"就可以绑定外面的属性和方法,这样就和使用 van-tabs 的传参一致了,onChange 是点击的时候自动计算,onMounted 处理已经选中 tab,但是数据发生变化导致标题长度发生变化的情况
observer.observe(dom, {
characterData: true,
subtree: true,
})
这段代码我搞了好久,本质上文字是文本节点,也属于后代节点,characterData 表示监听文本变化,subtree 表示将监听扩展到子节点,因此二者必须同时具备才能有效果。