🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
> 大家好,我给大家分享一下豆瓣app的制作。当然了,我们不是用原生去实现,而是用前端框架vuejs来实现豆瓣app。 **为什么我们选择豆瓣app 来做这样一个教程?** > 是因为我很早就接触豆瓣这个网站,我比较喜欢看豆瓣里面电影和文章的点评。并且豆瓣提供了非常丰富的一个api接口供我们使用。也就是说我们可以不通过后端,直接通过前端ajax来获取电影和图书的数据,来组装我们app。 我们可以看一下豆瓣app首页是一个什么样子 ![](https://box.kancloud.cn/5ad22257000785bdbac717515a2d9672_272x486.png) 以上就是豆瓣app的一个截图。 **我们先来分析一下** > 首页分为四个部分。第一个就是顶部的搜索框。搜索框下面就是一个banner图切换。在下面就是一些热点的文章列表。最底部就是一个tab切换。在这篇教程中,我们通过vue的组件来实现这样一个首页的布局。 * * * * * **创建豆瓣项目** 我们可以通过官方vue-cli初始化项目,这里我们采用webpack示例 ~~~ vue init webpack ~~~ 初始化后,通过npm install安装依赖 ~~~ npm install ~~~ 运行项目,可以看到基于官方vue-cli的模版就创建好了 ~~~ npm run dev ~~~ ![](https://box.kancloud.cn/78831cbd1fe41d58c5a62eb5e2ae8ba7_639x660.png) 将所需要用的资源,拷贝到项目中,这里我通过解压豆瓣app获得他的一些图片素材,拷入到src/assets/images目录里。 css这里我用到了normaliz.css 在src下,新建了一个pages目录,存放每一个页面组件,可以看一下我们的目录 ![](https://box.kancloud.cn/27794eab5b4ce2d40521e90f3d56976b_399x646.png) 由于我们的首页更改了位置,所以在router里面的index.js需要更改为 ~~~ import Vue from 'vue' import Router from 'vue-router' import Index from '../pages/Index' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'Index', component: Index } ] }) ~~~ **第一个组件 tabbar** 如何创建自定义组件tabbar,也就是豆瓣app底部的工具栏。这里的结构我们参考了[mint-ui](https://github.com/ElemeFE/mint-ui) 这是我们将要实现的效果图。 ![](https://box.kancloud.cn/d529ae2149d0c65ab98f28df1e9f9f58_324x57.png) 我们先来分析一下这个组件的结构。 这个组件分为两部分:第一个是组件的外层容器,第二个是组件的子容器item,子组件里面又分为图片和文字组合。子组件有2个状态,一个默认灰色的状态,一个选中状态,我们来实现一下这个组件的布局。在index.vue里面 template ~~~ <div class="m-tabbar"> <a class="m-tabbar-item is-active"> <span class="m-tabbar-item-icon"> <img src="../assets/images/ic_tab_home_normal.png" alt=""> </span> <span class="m-tabbar-item-text"> 首页 </span> </a> <a class="m-tabbar-item"> <span class="m-tabbar-item-icon"> <img src="../assets/images/ic_tab_subject_normal.png" alt=""> </span> <span class="m-tabbar-item-text"> 书影音 </span> </a> <a class="m-tabbar-item"> <span class="m-tabbar-item-icon"> <img src="../assets/images/ic_tab_status_normal.png" alt=""> </span> <span class="m-tabbar-item-text"> 广播 </span> </a> <a class="m-tabbar-item"> <span class="m-tabbar-item-icon"> <img src="../assets/images/ic_tab_group_normal.png" alt=""> </span> <span class="m-tabbar-item-text"> 小组 </span> </a> <a class="m-tabbar-item"> <span class="m-tabbar-item-icon"> <img src="../assets/images/ic_tab_profile_normal.png" alt=""> </span> <span class="m-tabbar-item-text"> 我的 </span> </a> </div> ~~~ style ~~~ <style lang="less"> .m-tabbar{ display: flex; flex-direction: row; position: fixed; bottom: 0; left: 0; right: 0; width: 100%; overflow: hidden; height: 50px; background: #fff; border-top: 1px solid #e4e4e4; .m-tabbar-item{ flex: 1; text-align: center; .m-tabbar-item-icon{ display: block; padding-top: 2px; img{ width: 28px; height: 28px; } } .m-tabbar-item-text{ display: block; font-size: 10px; color:#949494; } &.is-active{ .m-tabbar-item-text{ color: #42bd56; } } } } </style> ~~~ 布局大功告成~~~~ 前面我们说的是,通过组件的方式来实现这个app。 如果像上面代码这样的话肯定是不行的!既然我们大体布局已经写好了,现在就可以通过组件的方式来调用。当然我们还要改造一下代码。 先在components文件夹下面,新建两个组件,通过这两个组件来组合实现我们底部的tab组件: **一个是tabbar-item.vue,实现子组件的item项,** **tabbar-item.vue** ~~~ <template> <a class="m-tabbar-item" > <span class="m-tabbar-item-icon"><slot name="icon-normal"></slot></span> <span class="m-tabbar-item-text"><slot></slot></span> </a> </template> <style lang="less"> .m-tabbar-item{ flex: 1; text-align: center; .m-tabbar-item-icon{ display: block; padding-top: 2px; img{ width: 28px; height: 28px; } } .m-tabbar-item-text{ display: block; font-size: 10px; color:#949494; } &.is-active{ .m-tabbar-item-text{ color: #42bd56; } } } </style> ~~~ **一个是tabbar.vue,实现tab的外层容器,** **tabbar.vue** ~~~ <template> <div class="m-tabbar"> <slot></slot> </div> </template> <style lang="less"> .m-tabbar{ display: flex; flex-direction: row; position: fixed; bottom: 0; left: 0; right: 0; width: 100%; overflow: hidden; height: 50px; background: #fff; border-top: 1px solid #e4e4e4; } </style> ~~~ 在Index.vue中组合这两个组件,实现tab组件效果 ~~~ <template> <div> <m-tabbar> <m-tabbar-item id='tab1'> <img src="../assets/images/ic_tab_home_normal.png" alt="" slot="icon-normal"> 首页 </m-tabbar-item> <m-tabbar-item id='tab2'> <img src="../assets/images/ic_tab_subject_normal.png" alt="" slot="icon-normal"> 书影音 </m-tabbar-item> <m-tabbar-item id='tab3'> <img src="../assets/images/ic_tab_status_normal.png" alt="" slot="icon-normal"> 广播 </m-tabbar-item> <m-tabbar-item id='tab4'> <img src="../assets/images/ic_tab_group_normal.png" alt="" slot="icon-normal"> 小组 </m-tabbar-item> <m-tabbar-item id='tab5'> <img src="../assets/images/ic_tab_profile_normal.png" alt="" slot="icon-normal"> 我的 </m-tabbar-item> </m-tabbar> </div> </template> <script> import mTabbar from '../components/tabbar' import mTabbarItem from '../components/tabbar-item' export default { name: 'index', components: { mTabbar, mTabbarItem } } </script> ~~~ 实现的效果。 ![](https://box.kancloud.cn/1013e16c3f2ff8261f67020ed8a001d7_323x175.png) * * * * * **光有一个死的界面,没有点击切换的效果怎么能行?** 以下我们通过vue使用自定义事件的表单输入组件来实现点击切换的效果。 * * * * * 先给Index.vue里面的tab组件加上v-model 来进行数据双向绑定,通过select来达到选择item,在item里面再添加一个选中的active图片 ~~~ <template> <div> 测试 <m-tabbar v-model="select"> <m-tabbar-item id='tab1'> <img src="../assets/images/ic_tab_home_normal.png" alt="" slot="icon-normal"> <img src="../assets/images/ic_tab_home_active.png" alt="" slot="icon-active"> 首页 </m-tabbar-item> <m-tabbar-item id='tab2'> <img src="../assets/images/ic_tab_subject_normal.png" alt="" slot="icon-normal"> <img src="../assets/images/ic_tab_subject_active.png" alt="" slot="icon-active"> 书影音 </m-tabbar-item> <m-tabbar-item id='tab3'> <img src="../assets/images/ic_tab_status_normal.png" alt="" slot="icon-normal"> <img src="../assets/images/ic_tab_status_active.png" alt="" slot="icon-active"> 广播 </m-tabbar-item> <m-tabbar-item id='tab4'> <img src="../assets/images/ic_tab_group_normal.png" alt="" slot="icon-normal"> <img src="../assets/images/ic_tab_group_active.png" alt="" slot="icon-active"> 小组 </m-tabbar-item> <m-tabbar-item id='tab5'> <img src="../assets/images/ic_tab_profile_normal.png" alt="" slot="icon-normal"> <img src="../assets/images/ic_tab_profile_active.png" alt="" slot="icon-active"> 我的 </m-tabbar-item> </m-tabbar> </div> </template> <script> import mTabbar from '../components/tabbar' import mTabbarItem from '../components/tabbar-item' export default { name: 'index', components: { mTabbar, mTabbarItem }, data() { return { select:"tab1" } } } </script> ~~~ tabbar.vue里面通过props来传递数据vaule ~~~ <template> <div class="m-tabbar"> <slot></slot> </div> </template> <script> import mTabbarItem from './tabbar-item'; export default { props: ['value'] } </script> <style lang="less"> .m-tabbar{ display: flex; flex-direction: row; position: fixed; bottom: 0; left: 0; right: 0; width: 100%; overflow: hidden; height: 50px; background: #fff; border-top: 1px solid #e4e4e4; } </style> ~~~ tabbar-item.vue组件:根据父组件的value和当前组件的id判断是否为选中状态,通过 $parent.$emit('input',id) - 触发父组件的自定义事件,添加选中的图片,根据isActive来显示隐藏 ~~~ <template> <a class="m-tabbar-item" :class="{'is-active':isActive}" @click="$parent.$emit('input',id)"> <span class="m-tabbar-item-icon" v-show="!isActive"><slot name="icon-normal"></slot></span> <span class="m-tabbar-item-icon" v-show="isActive"><slot name="icon-active"></slot></span> <span class="m-tabbar-item-text"><slot></slot></span> </a> </template> <script> export default{ props: ['id'], computed: { isActive(){ if(this.$parent.value===this.id){ return true; } } } } </script> <style lang="less"> .m-tabbar-item{ flex: 1; text-align: center; .m-tabbar-item-icon{ display: block; padding-top: 2px; img{ width: 28px; height: 28px; } } .m-tabbar-item-text{ display: block; font-size: 10px; color:#949494; } &.is-active{ .m-tabbar-item-text{ color: #42bd56; } } } </style> ~~~ 大功告成,tabbar组件就完成了~~~~~ ![](https://box.kancloud.cn/40f95edcdfd25a5004514490d1471f8d_650x820.gif) 感谢饿了么团队给我们带来了这么好的ui组件! 源码下载 链接:http://pan.baidu.com/s/1qYlR8g0 密码:9yph 下载安装 ~~~ npm install npm run dev ~~~