1

前言

本章只介绍添加 tabbar 页面。

新建 tab 页

在 views 目录下创建几个 tab 页:

image.png

添加路由

tabbar

在 router 目录创建 tabbar.js:

image.png

内容如下:

const Home = () => import(/* webpackChunkName: "index" */ "@/views/index/index.vue");
const Category = () => import(/* webpackChunkName: "index" */ "@/views/category/category.vue");
const Cart = () => import(/* webpackChunkName: "index" */ "@/views/cart/cart.vue");
const User = () => import(/* webpackChunkName: "index" */ "@/views/user/user.vue");

const tabbar = [
    {
        path: "/",
        name: "home",
        component: Home,
        meta: {
            title: "精选",
            tabbar: true,
            icon: "home-o",
            tabName: "精选",
            index: 1
        }
    },
    {
        name: "category",
        path: "/category",
        component: Category,
        meta: {
            title: "分类",
            tabbar: true,
            icon: "bag-o",
            tabName: "分类",
            index: 2
        }
    },
    {
        name: "cart",
        path: "/cart",
        component: Cart,
        meta: {
            title: "购物车",
            tabbar: true,
            icon: "cart-o",
            tabName: "购物车",
            index: 3
        }
    },
    {
        name: "user",
        path: "/user",
        component: User,
        meta: {
            title: "个人中心",
            tabbar: true,
            icon: "user-o",
            tabName: "我",
            index: 4
        }
    }
];


export default tabbar;

其中:

webpackChunkName:会将名字相同的打包到一起
tabbar:路由数组,其中

1、name:路由的名字
2、path:路由的 history 路径,即 url
3、component:组件,使用的模板页面
4、meta:自定义数据,这里 title 后续用来修改浏览器标题,tabbar,用来区分是否是 tabbar 页,icon 和 tabName 和 index 用来辅助展示 tabbar

index

修改 router 下的 index.js,为路由的入口配置,引入 tabbar.js,详细内容如下:

import Vue from "vue";
import VueRouter from "vue-router";
import Tabbar from "./tabbar.js";

Vue.use(VueRouter);

const routes = [
  ...Tabbar
];

/**如果路径不存在,就按照/ + name 驼峰转斜杠,来填充 */
routes.forEach(route => {
  route.path = route.path || "/" + (route.name.replace(/([A-Z])/g, "/$1").toLowerCase() || "");
});

const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes
});

/**路由进入前回调 */
router.beforeEach((to, from, next) => {
  // 修改浏览器页面标题
  const title = to.meta && to.meta.title;
  if (title) {
    document.title = title;
  }
  next();
});

export default router;

修改容器页面

修改 public 目录下的 index.html,修改 viewport,如果没有就添加:

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no,viewport-fit=cover">
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no,viewport-fit=cover">
    <link rel="icon" href="<%= BASE_URL %>favicon.png">
    <title>微商城</title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but wxshop doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

其中 width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no 是初始化页面缩放大小,并禁止用户对页面缩放,后面那个 viewport-fit=cover 是辅助 ios 安全距离使用的:

image.png

修改入口 App

这才是真正的添加 tabbar,修改 App.vue:

<template>
  <div id="app">
    <!-- 内容 -->
    <transition :name="transitionName" mode="out-in" >
      <!-- keep-alive 标签会缓存页面,缓存所有模板名称以KeepAlive结尾的页面或组件 -->
      <keep-alive :include="/KeepAlive$/">
        <router-view />
      </keep-alive>
    </transition>
    <!-- tabbar -->
    <van-tabbar route safe-area-inset-bottom>
      <van-tabbar-item
        v-for="(tab, index) in tabbar"
        :key="index"
        :to="tab.path"
        :icon="tab.meta.icon"
      >{{tab.meta.tabName}}</van-tabbar-item>
    </van-tabbar>
  </div>
</template>

<script>
import { Tabbar, TabbarItem } from "vant";
import TabbarRoute from "@/router/tabbar.js";
export default {
  name: "App",
  components: {
    [Tabbar.name]: Tabbar,
    [TabbarItem.name]: TabbarItem
  },
  data() {
    return {
      transitionName: "fade",
      tabbar: TabbarRoute
    };
  },

  watch: {
    $route(to, from) {
      if (from.name) {
        /**不是初始页面 */
        if (to.meta.tabbar) {
          const toDepth = to.meta.index;
          const fromDepth = from.meta.index;
          this.transitionName =
            toDepth < fromDepth ? "slide-right" : "slide-left";
        } else {
          const toDepth = to.path.split("/").length;
          const fromDepth = from.path.split("/").length;
          this.transitionName =
            toDepth < fromDepth ? "slide-right" : "slide-left";
        }
      } else {
        /**初始页面,from.name一定没有值 */
        this.transitionName = "fade";
      }
    }
  }
};
</script>

<style lang="scss">
html,
body,
#app {
  background: #ededed;
  height: 100%;
  margin: 0;
  padding: 0;
}
html,
body,
div,
p,
ul,
li,
img,
h1,
h2,
h3,
h4,
h5,
h6 {
  box-sizing: border-box;
}
img {
  max-width: 100%;
  display: inline-block;
}
</style>

<style>
/* 切换动画 */
.slide-right-leave-active,
.slide-left-leave-active,
.slide-right-enter-active,
.slide-left-enter-active {
  transition: all 0.3s;
}

.slide-right-enter {
  opacity: 1;
  transform: translateX(-100%);
}
.slide-right-leave-to {
  opacity: 0;
  transform: translateX(100%);
}
.slide-left-enter {
  opacity: 1;
  transform: translateX(100%);
}
.slide-left-leave-to {
  opacity: 0;
  transform: translateX(-100%);
}

.fade-leave-active,
.fade-enter-active {
  transition: opacity 0.2s;
}
.fade-enter,.fade-leave-to{
  opacity: 0;
}
</style>

其中:
1、keep-alive 缓存页面作用,一个商品列表,滑动三页,打开一个商品详情,点击返回,能否回到第三页浏览的位置?,如果列表页面加了缓存,会回到原来的位置,否则就会重新加载页面。
3、tabbar 使用 vant 的组件,route 属性表示根据路由改变 tabbar,safe-area-inset-bottom 属性表示开启 ios 的安全距离(需要viewport-fit=cover才起作用)。
4、transition 动画,用来使页面产生过度效果。
5、watch 监听路由变化,用来确定使用的过度动画,这里设计了三种动画,fade、slide-right、slide-left。
6、添加动画样式

测试

在管理后台点击运行启动:

image.png

点击切换是否有特效,我这边是有的,测试成功。

调整 tabbar

将 App.vue 中 tabbar 改为组件,在 components 文件夹下新建 tabbar.vue 组件,内容如下:

image.png

代码:

  <van-tabbar route safe-area-inset-bottom>
      <van-tabbar-item
        v-for="(tab, index) in tabbar"
        :key="index"
        :to="tab.path"
        :icon="tab.meta.icon"
      >{{tab.meta.tabName}}</van-tabbar-item>
    </van-tabbar>
</template>

<script>
import { Tabbar, TabbarItem } from "vant";
import TabbarRoute from "@/router/tabbar.js";
export default {
  name: "tabbar",
  components: {
    [Tabbar.name]: Tabbar,
    [TabbarItem.name]: TabbarItem
  },
  data(){
    return {
      tabbar: TabbarRoute
    }
  }
};
</script>

修改 tabbar 路由:

image.png

代码:

const Home = () => import(/* webpackChunkName: "index" */ "@/views/index/index.vue");
const Category = () => import(/* webpackChunkName: "index" */ "@/views/category/category.vue");
const Cart = () => import(/* webpackChunkName: "index" */ "@/views/cart/cart.vue");
const User = () => import(/* webpackChunkName: "index" */ "@/views/user/user.vue");
const Tabbar = () => import(/* webpackChunkName: "index" */ "@/components/tabbar.vue");

const tabbar = [
    {
        path: "/",
        name: "home",
        components: {
            default: Home,
            tabbar: Tabbar
        },
        meta: {
            title: "精选",
            tabbar: true,
            icon: "home-o",
            tabName: "精选",
            index: 1
        }
    },
    {
        name: "category",
        path: "/category",
        components: {
            default: Category,
            tabbar: Tabbar
        },
        meta: {
            title: "分类",
            tabbar: true,
            icon: "bag-o",
            tabName: "分类",
            index: 2
        }
    },
    {
        name: "cart",
        path: "/cart",
        components: {
            default: Cart,
            tabbar: Tabbar
        },
        meta: {
            title: "购物车",
            tabbar: true,
            icon: "cart-o",
            tabName: "购物车",
            index: 3
        }
    },
    {
        name: "user",
        path: "/user",
        components: {
            default: User,
            tabbar: Tabbar
        },
        meta: {
            title: "个人中心",
            tabbar: true,
            icon: "user-o",
            tabName: "我",
            index: 4
        }
    }
];


export default tabbar;

修改 App.vue:

image.png

代码:

<template>
  <div id="app">
    <!-- 内容 -->
    <transition :name="transitionName" mode="out-in">
      <!-- keep-alive 标签会缓存页面,缓存所有模板名称以KeepAlive结尾的页面或组件 -->
      <keep-alive :include="/KeepAlive$/">
        <router-view />
      </keep-alive>
    </transition>
    <!-- tabbar -->
    <keep-alive>
      <router-view name="tabbar" />
    </keep-alive>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      transitionName: "fade"
    };
  },

  watch: {
    $route(to, from) {
      if (from.name) {
        /**不是初始页面 */
        if (to.meta.tabbar) {
          const toDepth = to.meta.index;
          const fromDepth = from.meta.index;
          this.transitionName =
            toDepth < fromDepth ? "slide-right" : "slide-left";
        } else {
          const toDepth = to.path.split("/").length;
          const fromDepth = from.path.split("/").length;
          this.transitionName =
            toDepth < fromDepth ? "slide-right" : "slide-left";
        }
      } else {
        /**初始页面,from.name一定没有值 */
        this.transitionName = "fade";
      }
    }
  }
};
</script>

<style lang="scss">
html,
body,
#app {
  background: #ededed;
  height: 100%;
  margin: 0;
  padding: 0;
  position: relative;
}
html,
body,
div,
p,
ul,
li,
img,
h1,
h2,
h3,
h4,
h5,
h6 {
  box-sizing: border-box;
}
img {
  max-width: 100%;
  display: inline-block;
}
.line1 {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  word-break: break-all;
}
.line2 {
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.line3 {
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
}
</style>

<style>
/* 切换动画 */
.slide-right-leave-active,
.slide-left-leave-active,
.slide-right-enter-active,
.slide-left-enter-active {
  transition: all 0.3s;
}

.slide-right-enter {
  opacity: 1;
  transform: translateX(-100%);
}
.slide-right-leave-to {
  opacity: 0;
  transform: translateX(100%);
}
.slide-left-enter {
  opacity: 1;
  transform: translateX(100%);
}
.slide-left-leave-to {
  opacity: 0;
  transform: translateX(-100%);
}

.fade-leave-active,
.fade-enter-active {
  transition: opacity 0.2s;
}
.fade-enter,
.fade-leave-to {
  opacity: 0;
}
</style>

router-view 组件指定渲染的路由组件名称,详细说明查看官网文档


贝哥哥
89 声望19 粉丝