用vue仿某外卖app项目总结

github地址:https://github.com/MissNanLan/vue-takeoutApp.git

先看效果


商品页.jpg

商家页jpg
购物车.jpg
评论页jpg

公告页S80202-230340.jpg

路由配置

1、main.js
记得要引入各个组件

const  router = new VueRouter({
mode:"hash",
linkActiveClass:"myactive",//设置高亮显示。myactive的样式在app.vue当中设置
routes:[
 {path:'/',redirect:'goods'},  //设置默认的页面
 { path:'/goods',
    component:goods
  },
  { path :'/ratings',
    component:ratings
  },
  { path:'/seller',
    component:seller
  }
]
}) 
new Vue({
  el: '#app',
  router,
  template: '<App/>',
  components: { App }
})

2、App.vue

 <div class="tab border-1px">
        <div class="tab-item">
         <router-link to="/goods" activeClass="myactive" >商品</router-link>
        </div>
          <div class="tab-item">
         <router-link to="/ratings">评论</router-link>
          </div>
          <div class="tab-item">
            <router-link to="/seller" >商家</router-link>
          </div>
        </div>
        <!--keep-alive只让页面请求一次-->
        <router-view :seller="seller" keep-alive></router-view>

myactive在style里面设置高亮显示的样式

设置数据来源

要安装vue-resource
在build的webpack.dev.conf.js的文件里面配置路由代理

const app = express()
var appData = require('../data.json') //设置数据来源
var seller = appData.seller; 
var goods = appData.goods
var ratings = appData.ratings
var apiRoutes = express.Router();
app.use('/api',apiRoutes);
    //配置路由代理
    before(app){
      app.get('/api/seller',(req,res)=>{
        res.json({
          errno:0,
          data:seller
        })
      }),
          app.get('/api/goods',(req,res)=>{
            res.json({
              errno:0,
              data:goods
            })
          }),
          app.get('/api/ratings',(req,res)=>{
            res.json({
              errno:0,
              data:ratings
            })
          })
    }
  },
配置成功的效果.png

图标图片的自适应

就是这样的图标.png

这里用的是stylus,stylus功能强大,可以写成函数等等。这里可以参考张鑫旭的博客
1、定义
image.png

image.png

关于更多设备像素比的知识可点击这里devicePixeRatio
2、引用
image.png

字体图标的使用

这里用的是iconmoon,就是将svg的文件上传到iconmoon做成字体图标文件。


svg文件.png

使用方法

    <i class="icon icon-keyboard_arrow_right"></i>

当页面超出时出现滚动效果(左侧菜单和右侧区域都要独立滚动)

    _initScroll() {
           this.meunScroll = new BScroll(this.$refs.menuWrapper, {
             click:true  //这里设置为click等于true的原因是因为BScroll阻止了点击事件
           });
           this.foodScroll = new BScroll(this.$refs.foodWrapper, {
               probeType: 3,
               click:true
          });

这里用的BScroll插件。安装命令是 cnpm install --save-dev better-scroll,然后在要使用的组件中引入即可

联动和选择效果

联动效果,即右侧区域滚动到某个分类块其左侧菜单也要高亮显示
首先我们想下逻辑。是不是要知道右侧区域的内容每个分类块的高度,当右侧内容列表的内容和和左侧菜单列表的下标值相等的时候,左侧菜单列表高亮显示。
算出高度

联动
  _calculateHeight(){ //获取foodList的高度
            let foodList = this.$refs.foodList;  //拿到foodlist
            let height = 0;
            this.listHeight.push(height);
            for (let i = 0; i < foodList.length; i++) {
              let item = foodList[i];
              height += item.clientHeight; //clientHeight是可见区域的高度
              this.listHeight.push(height);
             }
           }
  created() { //实例化创建完成之后的钩子函数
          this.classMap1 = ['decrease','discount','special','guarantee','invoice'];
          this.$http.get('/api/goods').then((response) => {
            response = response.body; //response.body返回一个对象
            if(response.errno === ERR_OK){
              this.goods = response.data;
              this.$nextTick(() => {  //this.$nextTick是在下次DOM更新循环结束时调用延迟回调函数。
                   this._initScroll();
                   this._calculateHeight();
              })
            }
          });
        },

监听右侧区域滚动事件

  //监听右侧区域的滚动事件
           this.foodScroll.on('scroll', (pos) => {
          // 判断滑动方向,避免下拉时分类高亮错误(如第一分类商品数量为1时,下拉使得第二分类高亮)
          if (pos.y <= 0) {
            this.scrollY = Math.abs(Math.round(pos.y));
            }
         });

高亮显示

 currentIndex() {
              for (let i = 0; i < this.listHeight.length; i++) {
                let height1 = this.listHeight[i];
                let height2 = this.listHeight[i + 1];
                if (!height2 || (this.scrollY >= height1 && this.scrollY < height2)) {
                  this._followScroll(i);
                  return i;
                }
              }
              return 0;
 _followScroll(index) {
              let menuList = this.$refs.menuList;
              let el = menuList[index];
              this.meunScroll.scrollToElement(el, 300, 0, -100); 
              //scrollToElement(el, time, offsetX, offsetY, easing)
            }
image.png

条件成立时给它加上高亮显示的样式

选择
     selectMenu(index,event) {
          if (!event._constructed) {  //这里会触发两次事件,浏览器原生事件和自己派发事件。如果不穿$event进来的话,那么就会打印两次
            return;                  //这里是把原生的事件给return掉
          }
          let foodList = this.$refs.foodList;
          let el = foodList[index];
          this.foodScroll.scrollToElement(el, 300);//300是滚动的时间
        }

在左侧菜单点击触发

父组件goods项子组件传选中的商品

food子组件和shopcart,还有cartcontrol组件都会接收父组件goods传过来的值

 //在goods组件里面选了多少foods
         selectFoods() {
            let foods = [];
            this.goods.forEach((good) => {
              good.foods.forEach((food) => {
                if (food.count) {
                  foods.push(food);
                }
              });
            });
            return foods;
         }

购物车小球动画

 beforeDrop(el) {
        let count = this.balls.length;
        while (count--) {
          let ball = this.balls[count];
          if (ball.show) {
            let rect = ball.el.getBoundingClientRect(); //getBoundingClientRect用于获取某个元素相对于视窗的位置集合
            let x = rect.left - 32;
            let y = -(window.innerHeight - rect.top - 22);
            el.style.display = '';
            el.style.webkitTransform = `translate3d(0,${y}px,0)`;//y轴转
            el.style.transform = `translate3d(0,${y}px,0)`;
            let inner = el.getElementsByClassName('inner-hook')[0];
            inner.style.webkitTransform = `translate3d(${x}px,0,0)`; //x轴转
            inner.style.transform = `translate3d(${x}px,0,0)`;
          }
        }
      },
      dropping(el, done) {
        /* eslint-disable no-unused-vars */
        let rf = el.offsetHeight;
        this.$nextTick(() => {
          el.style.webkitTransform = 'translate3d(0,0,0)';
          el.style.transform = 'translate3d(0,0,0)';
          let inner = el.getElementsByClassName('inner-hook')[0];
          inner.style.webkitTransform = 'translate3d(0,0,0)';
          inner.style.transform = 'translate3d(0,0,0)';
          el.addEventListener('transitionend', done);
        });
      },
      afterDrop(el) {
        let ball = this.dropBalls.shift();
        if (ball) {
          ball.show = false;
          el.style.display = 'none';
        }
      }
    }

调用

<transition name="drop" @before-enter="beforeDrop" @enter="dropping" @after-enter="afterDrop">
            <div class="ball" v-show="ball.show">
              <div class="inner inner-hook"></div>
            </div>
          </transition>

星级评分

这里的星级评分的逻辑还算比较简单。它接受父组件传来的两个参数,一个是星星的大小,一个是分数。

     props:{
            size:{
                type:Number
            },
            score:{
                type:Number
            }
        }

关键代码:怎么显示全星、半星、空星

 itemClasses() {
                let result = [];  //math.floor()对数字进行下舍入
                let score = Math.floor(this.score*2) / 2;
                let hasDecimal = score%1 !=0;  //余数为0的话就不是小数
                let integer = Math.floor(score);
                for(let i =0; i<integer;i++){
                    result.push(CLS_ON);
                }
                if(hasDecimal){
                    result.push(CLS_HALF);
                }
                while(result.length<LENGTH){
                    result.push(CLS_OFF);
                }
                return result;
            }

flex布局实现左侧固定右侧自适应

html代码

<div class="goods">
<div class="menu-wrapper"></div>
<div class="foods-wrapper"></div>
</div>

stylus代码

.goods
  display:flex
  .menu-wrapper
     flex:0 0 100px;
     width:100px
  .foods-wrapper
     flex:1

这样就实现了左侧固定右侧区域自适应

水平方向滚动

   _initScroll(){
        if(!this.scroll){
          this.scroll = new BScroll(this.$refs.seller,{
            click:true
          });
        }else{
          this.scroll.refresh();
        }
      }
商家实景水平滚动.png

打包编译

node build  //会生成dist文件夹
dist文件夹.png

可以建一个小型的prod.server.js
在手机访问输入自己的ip加端口号,如果不能访问的话,在config文件夹下的index.js文件里面的dev将host改为0.0.0.0,在电脑端用localhost加端口访问


.png

个人总结:一路走来,确实遇到不少的困难,是因为老师的教学视频是1.0的,而现在vue都升级2.x了。所以在这个过程中难眠会遇到一些困难,我记得第一次接触这个视频,因为版本不对,自己就放弃了,但是后来又重新打开来看,加深对vue的认识

主要有建立数据mocks、路由配置(哈希路由和历史路由、高亮显示、配置默认页面)、字体图标(svg文件编程字体文件,iconmoon)、图标图标做自适应(主要使用媒体查询)、stylus实战(主要是stylus可以写成函数的形式,方便于整合样式代码)、flex布局、父子组件传递数据、Bscroll插件的使用(当移动端的内容超出时滚动、水平也是)、vue的过渡等等

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

推荐阅读更多精彩内容

  • 目录 UI组件 开发框架 实用库 服务端 辅助工具 应用实例 Demo示例 UI组件 element★13489 ...
    余生社会阅读 19,658评论 7 233
  • 转载 :OpenDiggawesome-github-vue 是由OpenDigg整理并维护的Vue相关开源项目库...
    果汁密码阅读 23,083评论 8 124
  • 感赏今天医院同事带我去好吃的老友面,而且价钱比平时去吃的任何一家粉店都要便宜。以后想吃好东西又多了一个地方。 感赏...
    丽丽丫丫阅读 96评论 0 0
  • 雨水糊了我的眼 似乎想洗刷眼中的迷茫 教堂的十字架洗礼在绵绵细雨中 寺院的沉钟捶击心中的挣扎 我停步在山腰 雨水打...
    顾熙卿阅读 183评论 0 2
  • 时间这匹野马不羁的溜达,奔腾,碾压所有途中的百态的投影。不觉恍然间过了二十八个年头,却是伸手马尾也薅不下一根。着实...
    红尘静水阅读 275评论 0 0