一个「钉住」一个「喊话」

移动端页面里有两个高频却常被低估的组件:Sticky 吸顶——让导航条、筛选栏滚动到顶部时钉在屏幕上;NoticeBar 通告栏——用跑马灯循环播放公告。两者经常配合出现在列表页:顶部通告 + 吸顶筛选,信息层次立刻清晰。

Sticky:把任意元素钉在顶部

把需要吸顶的元素包在 van-sticky 里即可,组件会在滚动经过时自动切换为 fixed 定位:

html page.vue
<template>
  <van-nav-bar title="商品列表" />

  <!-- 距离顶部 0px 时吸顶 -->
  <van-sticky>
    <van-dropdown-menu>
      <van-dropdown-item v-model="sort" :options="sortOptions" />
      <van-dropdown-item v-model="brand" :options="brandOptions" />
    </van-dropdown-menu>
  </van-sticky>

  <van-list ...>列表内容...</van-list>
</template>

<script setup>
import { ref } from "vue";

const sort = ref("hot");
const brand = ref("all");
const sortOptions = [
  { text: "默认排序", value: "hot" },
  { text: "价格从低到高", value: "price" },
];
const brandOptions = [{ text: "全部品牌", value: "all" }];
</script>

offset-top:与其他导航共存

页面顶部已有固定导航栏时,吸顶元素会被遮住,用 offset-top 让它停在导航栏下方:

html page.vue
<!-- 固定在顶部的导航 -->
<van-nav-bar title="标题" fixed />

<!-- 吸顶时停在导航栏(46px)下方 -->
<van-sticky offset-top="46px">
  <van-tabs>
    <van-tab title="推荐">内容</van-tab>
    <van-tab title="新品">内容</van-tab>
  </van-tabs>
</van-sticky>

container 模式:指定吸顶范围

默认 Sticky 一旦吸顶会钉到页面底部。若希望「滚出某个容器就放开」,用 container 属性指定参照容器:

html section.vue
<template>
  <div ref="container" class="section">
    <van-sticky :container="container">
      <div class="section-title">第一节标题(只在本节范围内吸顶)</div>
    </van-sticky>
    <div class="section-body">第一节内容...</div>
  </div>
</template>

<script setup>
import { ref } from "vue";
const container = ref(null);
</script>
💡 小技巧:Sticky 吸顶时会自动给元素加上 van-sticky--fixed 类,可以借此给吸顶状态加阴影或背景色,强化"钉住"的视觉反馈。

NoticeBar:循环播放的公告条

文本超宽时 van-notice-bar 默认以跑马灯形式横向循环,也可以设置可滚动开关、关闭按钮等:

html demo.vue
<template>
  <!-- 跑马灯:文本超宽自动滚动 -->
  <van-notice-bar
    left-icon="volume-o"
    text="618 大促进行中,全场满 300 减 50,活动截止 6 月 20 日,速来抢购!"
  />

  <!-- 单行截断 + 可关闭 -->
  <van-notice-bar
    wrapable
    :scrollable="false"
    mode="closeable"
    text="系统将于今晚 23:00 - 24:00 升级维护,期间可能出现短暂卡顿。"
    @close="onClose"
  />
</template>
属性说明
scrollable是否开启滚动条;false 时文本超宽会截断
wrapable是否开启文本换行(与 scrollable 互斥场景)
speed滚动速度,px/s,默认 60
delay动画延迟,秒,默认 1
modecloseable 显示关闭按钮 / link 显示右侧箭头
color / background自定义文字与背景颜色

组合实战:公告 + 吸顶筛选列表页

把两者放进同一个列表页:NoticeBar 常驻顶部,下拉菜单吸顶,滚动长列表时筛选始终可用:

html list-page.vue
<template>
  <van-notice-bar
    left-icon="volume-o"
    :text="announcement"
    background="#ecf9ff"
    color="#1989fa"
  />

  <van-sticky offset-top="46px">
    <van-tabs v-model:active="activeTab" sticky>
      <van-tab v-for="t in tabs" :key="t" :title="t">
        <van-list
          v-model:loading="loading"
          :finished="finished"
          finished-text="没有更多了"
          @load="onLoad"
        >
          <van-cell v-for="i in list" :key="i" :title="`商品 ${i}`" />
        </van-list>
      </van-tab>
    </van-tabs>
  </van-sticky>
</template>

<script setup>
import { ref } from "vue";

const announcement = "新品首发专区已上线,前 100 名下单享 5 折优惠!";
const activeTab = ref(0);
const tabs = ["推荐", "数码", "服饰", "食品"];
const list = ref([]);
const loading = ref(false);
const finished = ref(false);

const onLoad = () => {
  const start = list.value.length;
  for (let i = 1; i <= 20; i++) list.value.push(start + i);
  loading.value = false;
  if (list.value.length >= 60) finished.value = true;
};
</script>

常见问题排查

Sticky 不吸顶?

  • 父容器设置了 overflow: hidden / auto,会破坏 fixed 定位的参照,去掉或改为 visible
  • 吸顶元素自身是 position: fixed 或在 transform 容器里,需要调整 DOM 结构

NoticeBar 文本很短却也在滚动?

组件默认「超宽才滚动」,若短文本也滚,通常是容器宽度异常(如父级 flex 未约束)。显式设置 :scrollable="false" 可强制不滚。

多页签下吸顶位置不对?

每个 van-tab 是独立滚动容器,Sticky 放在 tabs 外层只对第一个页签生效;需要每个页签内各自包一层 van-sticky,或把 offset-top 配合页签高度计算。

总结

Sticky 与 NoticeBar 都是「零样式成本」的体验型组件:Sticky 用 offset-top 对齐固定导航、用 container 限定吸顶范围;NoticeBar 记住 scrollable / wrapable / mode 三个开关。组合进列表页后,长内容的浏览效率会明显提升。

让重要信息留在屏幕上,让次要信息自己动起来。