第一章:基于@vue/cli4.x+vue2.x原生封装的tabbar高可用组件示例

引言

如今前端框架也越来越丰富,学习成本也在逐渐加深,我作为纯后端Java开发人员,也学习了一下目前前端比较火的vue,并将学习的第一阶段成果以一个高可用,可自定义内容的,自己手动撸的一个封装好的一个tabbar组件。本章关于里面用到的知识点的原理不会过多的去讲解,主要作为一次初级入门的实践示例来分享。

封装好的tabbar作用

这是我第一个小实践示例。主要用到 @vue/cli4.x+vue2.x+vue-router(路由)来实现。实现好的效果如下图展示:

示例演示

封装好下面的tabbar,跳转不同的路径显示不同的vue页面,下面的按钮个数可以自定义,点击的效果颜色也可以自定义,以及图标跟文字都是可以实现类似参数传递的功能来高度自定义tabbar的内容。

  • @vue/cli4.x: 这是vue原生的官方脚手架。具体的详细的介绍我不多说了,你可以理解为用它可以创建一个vue的项目的架子,相当于建房子的一个框架,把一些最基本的东西给你生成好了,你只需要去具体实现你自己的业务代码。你也可以查看官网的介绍点这里
  • vue2.x: Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。摘抄自官网,曾今我有很长时间都读错了它。
  • vue-router: 是vue.js官方的路由管理器。它和vue.js的核心深度集成。

应用

开发正式开始之前,需要准备的工作

  • WebStorm 2019.3.2 x64开发工具的安装
  • node.js的安装

1、在webstorm的命令行窗口或者cmd窗口安装官网提供的最新脚手架@vue/cli。我这里直接在webstorm命令行窗口进行安装演示,如图:

安装@vue/cli脚手架

点击Terminal是打开命令行的操作。npm install -g @vue/cli安装命令中-g表示全局安装。其实我之前已经在我电脑上安装过了,这里为了演示。
2、使用脚手架创建tabbar项目。执行vue create tabbar命令后会弹出如下选择界面:
创建项目

default表示它给你推荐的默认配置

  • babel:

babel是一个JavaScript编译器;
babel是一个工具链,主要用于将ECMAScript2015+版本的代码转换为向后兼容的JavaScript语法,以便能够在当前和旧版本的浏览器或其它环境中运行

  • eslint: 可以理解为质量检查工具,让你可以对自己有个相对规范的开发
    如果我们选择 manually select features,那么脚手架会提供更多个性化的配置让我们选择如何初始化项目。


    脚手架提供的更多个性化的配置选择

    上下键选择,空格键确认选择。这里我简单描述下每个配置选项

  • typescript: 使用下一代Javascript的typescript语法,可以理解为更新的脚本语言,如果不熟练的话不推荐使用。
  • Progressive Web App (PWA) Support: 我在知乎上找到这样一段描述

Progressive Web App其实严格说来不是一个产品,而是一种理念,或者叫打包产品,因为他是把众多能让WEB产品APP化的能力的一个集合。
从Google官方网站介绍内容提炼一下,PWA的三大基本能力分别是:1、 类APP交互 2、消息推送 3、离线缓存

我的理解是将我们的应用可以整的类似app的交互体验,即webapp。

  • Router: 是否安装路由
  • Vuex: 是否安装全局状态管理器
  • CSS Pre-processors: 选择CSS 预处理类型
  • Linter / Formatter :一种规范类型
  • Unit Testing:选择Unit测试方式
  • E2E Testing: 端到端的测试,E2E是end to end 的简写

当你选择完这个个性化的配置后,他会提示你是否保存这个默认配置,方便下次使用。我的项目是选择了默认的配置
生成后的项目如图:

项目结构化展示

我们主要是在src目录下进行开发。
3、安装vuerouter路由
安装vue-router

然后我们在vue中安装vuer-outer,配置我们的页面
路由的配置

import Vue from 'vue'
import VueRouter from 'vue-router'
import routes from "./routes"

//1、先在vue中安装router路由
Vue.use(VueRouter);

//2、创建router实例,然后传入‘routers’配置
const router = new VueRouter({
  routes,   //传入写好的路由页面配置
  mode: 'history'  //可以使得页面跳转时不带#号
})

//3、导出路由对象
export default router;

代码中有详细的注释、关于为什么用import、export这种概念性的不做过多描述
其中路由的页面配置

//定义路由
const routes = [
  {
    path: '',
    redirect: '/home'
  },
  {
    path: '/home',
    meta: {
      title: '首页'
    },
    component: ()=>import("views/home/Home")
  },
  {
    path: '/category',
    meta: {
      title: '分类'
    },
    component: ()=>import("views/category/Category")
  },
  {
    path: '/cart',
    meta: {
      title: '购物车',
    },
    component: ()=>import("views/cart/Cart")
  },
  {
    path: '/profile',
    meta: {
      title: '我的'
    },
    component: ()=>import("views/profile/Profile")
  }
]

export default routes;

将配置好的路由放入vue实例中去

vue示例添加路由

views文件夹下创建我们的业务所需页面
创建业务页面

脚手架的文件夹命名是有规范的,页面文件一般建立在views下面,router即路由的文件所在位置
样式和图片所在位置

里面的资源可以在我的gitee代码仓库中进行下载。
4、敲黑板,重点来了,组件的创建
tabbar组件部分

我们将tabbar看作一个整体,封装第一层组件
TabBar.vue

<template>
    <div id = "tab-bar">
        <!--定义一个插槽,里面的内容由外部来定义-->
        <slot></slot>
    </div>
</template>

<script>
  export default {
    name: "TabBar"
  }
</script>

<style scoped>
    #tab-bar {
        display: flex;
        background-color: #f6f6f6;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        box-shadow: 0 -3px 1px rgba(100,100,100,.2);
    }
</style>

TabBar.vue页面中我只定义了一个插槽,这个插槽可以放任何我们业务需要的东西进去,首先将tabbar的大筐筐的样式和布局来定义好

将大筐筐定义好

slot插槽的内容外部来定义。
TabBarItem.vue:针对每个按钮的封装实现

<template>
    <div class="tab-bar-item" @click="tabBarItemClick">
        <!--图片区域-->
        <!--未点击图片显示状态-->
        <div v-if = "!isActive">
            <slot name="item-icon"></slot>
        </div>
        <!--点击图片显示状态-->
        <div v-else>
            <!--创建一个具名插槽-->
            <slot name="item-icon-active"></slot>
        </div>
        <!--文字区域-->
        <div :style = "isActiveColor">
            <slot name="item-text"></slot>
        </div>
    </div>
</template>

<script>
  export default {
    name: "TabBarItem",
    //接收来自父组件的参数
    props: {
      path: String,   //第一种写法   参数:类型
      activeColor: {  //第二种写法,用对象来接收。可以不光定义接收的参数类型,还可以定义默认值
        type: String,
        default: 'red'
      }
    },
    data() {
      return {

      }
    },
    //定义我们需要的计算属性
    computed: {
      isActive() {
        //判断当前点击的是否与路由的路径一致
        return this.$route.path == this.path;
      },
      isActiveColor() {
        return this.isActive ? {color: this.activeColor} : {}
      }
    },
    methods: {
      tabBarItemClick() {
        console.log('path===' + this.path);
        //进行切换路由页面
        this.$router.replace(this.path);
      }
    }
  }
</script>

<style scoped>
    .tab-bar-item {
        flex: 1;
        text-align: center;
        height: 49px;
        font-size: 14px;
    }

    .tab-bar-item img {
        width: 24px;
        height: 24px;
        margin-top: 3px;
        vertical-align: middle;
        margin-bottom: 2px;
    }
</style>

props: props用来接收父级传过来的参数来定义父级传过来的参数,

  • path: 父级定义好的跳转路径

  • activeColor: 定义父级传过来的点击的切换颜色
    此页面定义了三个 具名插槽slot:

  • <slot name="item-icon"></slot> :没有被点击的图片插槽,这里的插槽使得图片的定义由父级来定义

  • <slot name="item-icon-active"></slot>:被点击的图片插槽,这里的插槽使得图片的定义由父级来定义

  • <slot name="item-text"></slot>:文字区域插槽

    image.png

    MainTabBar.vue:父级页面,传入业务需要的图标参数与文字到子页面的插槽中

<template>
    <tab-bar>
        <!--path和activeColor相当于父组件向子组件传递的参数,子组件用props来接收-->
        <tab-bar-item path="/home" activeColor="deepPink">
            <img slot="item-icon" src="~assets/img/tabbar/home.svg" alt="">
            <img slot="item-icon-active" src="~assets/img/tabbar/home_active.svg" alt="">
            <div slot="item-text">首页</div>
        </tab-bar-item>
        <tab-bar-item path="/category" activeColor="deepPink">
            <img slot="item-icon" src="~assets/img/tabbar/category.svg" alt="">
            <img slot="item-icon-active" src="~assets/img/tabbar/category_active.svg" alt="">
            <div slot="item-text">分类</div>
        </tab-bar-item>
        <tab-bar-item path="/cart" activeColor="deepPink">
            <img slot="item-icon" src="~assets/img/tabbar/shopcart.svg" alt="">
            <img slot="item-icon-active" src="~assets/img/tabbar/shopcart_active.svg" alt="">
            <div slot="item-text">购物车</div>
        </tab-bar-item>
        <tab-bar-item path="/profile" activeColor="deepPink">
            <img slot="item-icon" src="~assets/img/tabbar/profile.svg" alt="">
            <img slot="item-icon-active" src="~assets/img/tabbar/profile_active.svg" alt="">
            <div slot="item-text">我的</div>
        </tab-bar-item>
    </tab-bar>
</template>

<script>
    import TabBar from "components/tabbar/TabBar";
    import TabBarItem from "components/tabbar/TabBarItem";
  export default {
    name: "MainTabBar",
    components: {
      TabBar,
      TabBarItem
    }
  }
</script>

<style scoped>

</style>

封装好这个组建后,vue主页面进行引用


image.png
<template>
  <div id="app">
    <router-view/>
    <MainTabBar></MainTabBar>
  </div>
</template>

<script>
import MainTabBar from "components/tabbar/MainTabBar";
export default {
  name: 'App',
  components: {
    MainTabBar
  }
}
</script>

<style>
@import "assets/css/base.css";
</style>

<router-view/>放入我们配置好的路由页面。
最后我们输入 npm run serve

启动应用

我们输入npm run serve命令是由package.json页面配置决定的
image.png

注意:页面中的路径在 vue.config.js进行了别名配置,项目用到的资源和导入页面组件时所以才可以不用写绝对路径。
image.png

module.exports = {
  configureWebpack: {
    resolve: {
      alias: {
        'assets': '@/assets',
        'components': '@/components',
        'views': '@/views'
      }
    }
  }
}

这里的 @表示项目的src目录,vue以及帮我们定义好了,这里定义好目录别名后。项目内部用到图片路径就可以直接用我们配置好的别名路径代替全局路径了

配置好的路径实际使用截图

  • 1、import导入页面的时候可以直接用配置好的别名
  • 2、html标签,vue页面模板用到的时候需要在配置好的别名路径前加~

作者:金哲一(jinzheyi)【笔名】
本文代码地址:https://gitee.com/jinzheyi/vue
本文链接:https://www.jianshu.com/p/b2ae3e903300

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343