移动端页面的两大骨架

一个典型移动端 App 的页面结构:

<nav>     NavBar 顶部导航
<main>    页面主内容区
<footer>  Tabbar 底部标签栏

Vant 用 van-nav-bar 和 van-tabbar 分别实现。

NavBar 顶部导航

<template>
  <van-nav-bar
    title="文章详情"
    left-text="返回"
    left-arrow
    @click-left="goBack"
  />
  <van-nav-bar title="设置">
    <template #right>
      <van-icon name="search" size="20" @click="onSearch" />
    </template>
  </van-nav-bar>
  <van-nav-bar
    title="标题"
    :border="false"
    fixed
    placeholder
    safe-area-inset-top
  />
</template>

<script setup>
import { useRouter } from "vue-router";
const router = useRouter();
const goBack = () => {
  if (window.history.length > 1) {
    router.back();
  } else {
    router.push("/");
  }
};
</script>
  • left-arrow:显示返回箭头
  • fixed + placeholder:固定在顶部并占位(防止内容被遮挡)
  • safe-area-inset-top:适配 iPhone 刘海屏

Tabbar 底部导航

<template>
  <van-tabbar v-model="active" route safe-area-inset-bottom>
    <van-tabbar-item icon="home-o" to="/">首页</van-tabbar-item>
    <van-tabbar-item icon="orders-o" to="/orders">订单</van-tabbar-item>
    <van-tabbar-item icon="user-o" to="/profile">我的</van-tabbar-item>
  </van-tabbar>
</template>

<script setup>
import { ref } from "vue";
const active = ref(0);
</script>
  • v-model:绑定当前激活的 tab 索引
  • route:启用路由模式,点击自动跳转 Vue Router
  • safe-area-inset-bottom:适配 iPhone 底部安全区

实战:完整的 App 骨架

<template>
  <div class="app-container">
    <van-nav-bar
      v-if="showNav"
      :title="navTitle"
      left-arrow
      @click-left="onBack"
      fixed
      placeholder
      safe-area-inset-top
    >
      <template #right v-if="showSearch">
        <van-icon name="search" size="20" @click="$router.push('/search')" />
      </template>
    </van-nav-bar>

    <main class="page-content">
      <router-view />
    </main>

    <van-tabbar v-model="active" route safe-area-inset-bottom v-if="showTab">
      <van-tabbar-item icon="home-o" to="/">首页</van-tabbar-item>
      <van-tabbar-item icon="apps-o" to="/categories">分类</van-tabbar-item>
      <van-tabbar-item icon="cart-o" to="/cart">购物车
        <template #badge>
          <van-badge :content="cartCount" />
        </template>
      </van-tabbar-item>
      <van-tabbar-item icon="user-o" to="/profile">我的</van-tabbar-item>
    </van-tabbar>
  </div>
</template>

<script setup>
import { ref, computed } from "vue";
import { useRoute } from "vue-router";

const route = useRoute();
const active = ref(0);
const cartCount = ref(3);

const showNav = computed(() => !route.meta.hideNav);
const showTab = computed(() => !route.meta.hideTab);
const showSearch = computed(() => route.meta.showSearch);
const navTitle = computed(() => route.meta.title || "");

const onBack = () => {
  if (history.length > 1) history.back();
  else location.href = "/";
};
</script>

<style>
.app-container {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}
.page-content {
  flex: 1;
  padding-bottom: env(safe-area-inset-bottom);
}
</style>

路由 meta 配置示例

// router/index.js
const routes = [
  {
    path: "/",
    component: Home,
    meta: { title: "首页", showSearch: true },
  },
  {
    path: "/login",
    component: Login,
    meta: { hideNav: true, hideTab: true },
  },
  {
    path: "/article/:id",
    component: ArticleDetail,
    meta: { title: "文章详情" },
  },
];

通过 route.meta 来控制每个页面的 NavBar 和 Tabbar 显示,非常灵活。