VUE高仿餓了麼app

VUE高仿餓了麼appjavascript

VUE 搭建簡介

剛學習了VUE高仿餓了麼app課,記錄課的要點,鞏固知識。
圖片描述圖片描述圖片描述

圖片描述圖片描述圖片描述css

VUE 優點

Vue.js 是一個用於建立 web 交互界面的。其特色是html

簡潔 HTML 模板 + JSON 數據,再建立一個 Vue 實例,就這麼簡單。
數據驅動 自動追蹤依賴的模板表達式和計算屬性。
組件化 用解耦、可複用的組件來構造界面。
輕量 ~24kb min+gzip,無依賴。
快速 精確有效的異步批量 DOM 更新。
模塊友好 經過 NPM 或 Bower 安裝,無縫融入你的工做流。

VUE 搭建工具

借用express + data 構建擬後臺vue

vue 1.0
express
vue.router
vue.rescrouse
better-scroll
less

CSS 使用的要點

1像素邊框製做

設備上像素 = 樣式像素 * 設備縮放比例java

屏幕寬度 320px 480px 640px
縮放比例   1    1.5    2

當樣式像素必定時,因手機有320px,640px等.各自的縮放比差別,因此設備顯示像素就會有1Npx,2Npx.爲保設計稿還原度,解決就是用media + scale.web

.border(@borderColor){
    position: relative;

    &::after{
        content: "";
        position: absolute;
        bottom: 0;
        left: 0;
        width: 100%;
        border-top: 1px solid @borderColor;
    }
}

@media (min-device-pixel-ratio: 1.5) {
    .border{

        &::after{
            transform: scaleY(0.7);
        }
    }
}

@media (min-device-pixel-ratio: 2) {
    .border{

        &::after{
            transform: scaleY(0.5);
        }
    }
}

經過查詢它的縮放比,在媒體寬爲1.5倍時, round(1px 1.5 / 0.7) = 1px 在媒體寬爲2倍時, round(1px 2 / 0.5) = 1px.express

自適應寬

圖片描述

在商品路由中,導航寬度固定80px的,由於手機分辨率大小不一,因此食物詳情自適應.解決就是flex佈局.json


css
api

<style type="text/less">
    .food{
        display: flex;
        width: 100%;

        .nav{
            flex: 0 0 80px;
            width: 80px;
        }

        .foodList{
            flex: 1;
        }
    }
</style>

html數組

<div class="food">
    <section class="nav"></section>
    <section class="foodList"></section>
</div>

在父元素設彈性佈局,導航裏設彈性爲0,定寬爲80px.商品食物詳情彈性爲1.就適應寬度變化.

Sticky footer

圖片描述

作商家彈出頁時,信息高度是沒法預約的,有可能溢出window高度,也可能少於window高度,但底部按鈕,當信息高度少於window高度,要固定在底部40px.解決就是用sticky footer佈局


css

<style type="text/less">
    .showDetil{
        position: absolute;
        width: 100%;
        height: 100%;

        .sellerDetil{
            width: 100%;
            min-height: 100%;
            padding-bottom: 40px;
        }

        .btn{
            position: relative;
            top: -40px;
            height: 40px;
        }
    }
</style>

html

<div class="showDetil">
    <section class="sellerDetil"></section>
    <section class="btn"></section>
</div>

父元素高相同window高,信息最小高就相同window高,按鈕這時就溢出了.
再設置底的填充,底內邊距高就是按鈕的高. 按鈕在用相對定位,定在信息的底填充裏.
因信息最少高度是100%,所按鈕要不釘在底部了.要不溢出.

自適相等寬高

圖片描述

在食物彈出頁.設計圖食物圖的寬高是相等,每張圖的寬高比例有可能有區別,但也要作自適應.解決就是用padding邊距.


css

<style type="text/less">
    .imgs{
        width: 100%;
        height: 0;
        position: relative;
        padding-top: 100%;

        .image{
            position: absolute;
            top: 0;
            width: 100%;
            height: 100%;
            left: 0;
        }
    }
</style>

html

<div class="imgs">
    <img src="..." class="image">
</div>

在父元素,邊距的長是取決去寬的,所其寬度與邊距的長是相等的.
在把高設爲0,寬爲100%,上邊距100%,上邊據就盒子的高.盒子是爲正形.
子元素設寬與高爲100%,那也是正形.

VUE要點

小圖標的編選

圖片描述

根據後臺輸出的數據,斷定顯示那個的圖標.這vue典型的數據.驅動.解決是使用:class困綁數據


html

<template>
    <ul>
        <li v-for="date in goods">
            <span :class="classmap[date.type]"></span>
        </li>
    </ul>
</template>

js

<script type="text/javascript">
    export default{
        data() {
            return {
                classmap: ['decrease', 'discount', 'guarantee', 'invoice', 'special']
            };
        }
    }
</script>

css

<style type="text/less">
.bgimg(@imgs) {
    background-image: url('@imgs+".png"') 0 0 no-repeat ~'/' 100% 100%;
}
    .decrease{
        display: inline-block;
        height: 12px;
        width: 12px;
    
       .bgimg('decrease_3');
    }

     .discount{
        display: inline-block;
        height: 12px;
        width: 12px;
    
        .bgimg('discount_3');
    }

    .guarantee{
        display: inline-block;
        height: 12px;
        width: 12px;
    
        .bgimg('guarantee_3');
    }

    .invoice{
        display: inline-block;
        height: 12px;
        width: 12px;
    
        .bgimg('invoice_3');
    }

    .special{
        display: inline-block;
        height: 12px;
        width: 12px;
    
        .bgimg('special_3');
    }
</style>

經過v-for,遍歷數據,因此date.type獲得數據並判斷類型.而後通classmap數組斷定綁那個class.來加圖標.

小球動畫

圖片描述

點擊加食物時,觸動小球彈出的動畫,小球的落點是在車的中央.但起點是根各個節點位子而又差異的.解決使用transitions + events + dispatch事件冒泡

cartcontrol子組件

圖片描述


html

<template>
    <div class="cartcontrol">
        <section class="cart-decrease" @click.stop.prevent="decreaseCart" v-show="food.count > 0" transition="move"></section>  
        <section class="cart-count" v-show="food.count > 0">{{food.count}}</section>
        <section class="cart-add" @click.stop.prevent="addCart"> </section>
    </div>
</template>

js

<script type="text/javascript">
export default {
    props: {
        food: {
            type: Object
        }
    },
    methods: {
        addCart(event) {
            if (!this.food.count) {
                Vue.set(this.food, 'count', 1);
                this.food.count = 1;
            } else {
                this.food.count++;
            };
            this.$dispatch('cart.add', event.target);
        },
        decreaseCart() {
            if (this.food.count) {
                this.food.count--;
            };
        }
    }
};
</script>

在加食物,觸發了addCart事件,設用set方法給數據加屬性,並使cart.add事件冒泡出去,event.target做爲事件參數,即節點冒泡出去.

goods父組件


html

<template>
    <shop v-ref:shop :delivery-price="seller.deliveryPrice" :min-price="seller.minPrice" :select-foods="selectFoods"></shop>
</template>

js

<script>
    export default {
        methods: {
            _drop(target) {
                this.$refs.shop.drop(target);
            }
        },
        events: {
            'cart.add'(target) {
                this._drop(target);
            }
        },
        components: {
            shop,
            cartcontrol,
            food
        }
    };
</script>

在冒泡被events鉤子監聽,與觸動_drop方法,經過接口得到購物車組建的事件,就把control組建event.target傳入購物車組建的事件,及把control節點傳入了shop組建.

shop組建


html

<template>
    <div class="shopcart">
        <section class="ball-container">
            <div transition="drop" v-for="ball in balls" v-show="ball.show" class="ball">
                <div class="inner inner-hook"></div>
            </div>
        </section>
    </div>
</template>

js

<script type="text/javascript">
    export default {
        data() {
            return {
                balls: [
                {
                    show: false
                },
                {
                    show: false
                },
                {
                    show: false
                },
                {
                    show: false
                },
                {
                    show: false
                }],
                dropBalls: [],
                fold: true
            };
        },
        methods: {
            drop(el) {
                for (var i = 0; i < this.balls.length; i++) {
                    let ball = this.balls[i];
                    if (!ball.show) {
                        ball.show = true;
                        ball.el = el;
                        this.dropBalls.push(ball);
                        return;
                    };
                };
            }
        },
        transitions: {
            drop: {
                beforeEnter(el) {
                    let count = this.balls.length;
                    while (count--) {
                        let ball = this.balls[count];
                        if (ball.show) {
                            let rect = ball.el.getBoundingClientRect();
                            let x = rect.left - 32;
                            let y = -(window.innerHeight - rect.top - 22);
                            el.style.display = '';
                            el.style.transform = `translate3d(0,${y}px,0)`;
                            let inner = el.getElementsByClassName('inner-hook')[0];
                            inner.style.transform = `translate3d(${x}px,0,0)`;
                        }
                    }
                },
                enter(el) {
                    let rf = el.offsetHeight;
                    this.$nextTick(() => {
                        el.style.transform = 'translate3d(0,0,0)';
                        let inner = el.getElementsByClassName('inner-hook')[0];
                        inner.style.transform = 'translate3d(0,0,0)';
                    });
                },
                afterEnter(el) {
                    let ball = this.dropBalls.shift();
                    if (ball) {
                        ball.show = false;
                        el.style.display = 'none';
                    };
                }
            }
        },
        components: {
            cartcontrol
        }
    };
</script>

傳入節點數據,過渡執行前可插入一個beforeEnter事件,通getBoundingClientRect定位.動畫執行後可插入一個afterEnter,還原小球

接後臺數據

與後臺的配合,經過插vue.resource + express 鏈接獲得數據

dev-server

<script type="text/javascript">
    import express from 'express';
    var app = express();

    var appData = require('../data.json');
    var seller = appData.seller;
    var goods = appData.goods;
    var ratings = appData.ratings;
    
    var apiRoutes = express.Router();
    
    apiRoutes.get('/seller', function (req, res) {
        res.json({
            errno: 0,
            data: seller
        });
    });
    
    apiRoutes.get('/goods', function (req, res) {
        res.json({
            errno: 0,
            data: goods
        });
    });
    
    apiRoutes.get('/ratings', function (req, res) {
        res.json({
            errno: 0,
            data: ratings
        });
    });
    app.use('/api', apiRoutes);
</script>

經過與配和框架express,連到數據。並放在api裏.

main.js

import VueResource from 'vue-resource';
Vue.use('VueResource');

引進插件和使用,在全局也可使用.

header組建

<script type="text/javascript">
    export default{
        created() {
            this.$http.get('/api/ratings').then((response) => {
                var response = response.body;
                if (response.errno === 0) {
                    this.ratings = response.data;
                };
            });
        }
    }
</script>

在框架的鉤子,及建立就經過http.get連到express發的數據,通參數response獲得.body表示數據以json格式響應.注意接收數據是異步實現,若是出報錯undefined,可用v-if判斷,當獲取數據後在渲染.

評分類換

圖片描述

用戶的滿意度有,推薦與吐槽再加上所有,就三個分頁,分頁經過按鈕切換.如何製做呢?解決是使用v-show進判斷.

ratingselect子組件

圖片描述


html

<template>
    <div class="ratingselect">
        <div class="rating-type">
            <span @click="select(2, $event)" class="block positive" :class="{'active':selectType === 2}">{{desc.all}}<span class="count">{{ratings.length}}</span></span>
            <span  @click="select(0, $event)" class="block positive" :class="{'active':selectType === 0}">{{desc.positive}}<span class="count">{{positives.length}}</span></span>
            <span @click="select(1, $event)" class="block negative" :class="{'active':selectType === 1}">{{desc.negative}}<span class="count">{{negatives.length}}</span></span>
        </div>
        <div @click="toggleContent" class="switch" :class="{'on':onlyContent}">
            <span class="iconfont arre">&#xe905;</span>
            <span class="text">只看有內容的評價</span>
        </div>
    </div>
</template>

js

<script type="text/javascript">
export default{
    props: {
        ratings: {
            type: Array,
            default() {
                return [];
            }
        },
        selectType: {
        type: Number,
        default: 2
        },
        onlyContent: {
            type: Boolean,
            default: true
        },
        desc: {
            type: Object,
            default() {
                return {
                    all: '所有',
                    positive: '滿意',
                    negative: '不滿意'
                };
            }
        }
    },
    methods: {
        select(type) {
            this.selectType = type;
            this.$dispatch('ratingtype.select', type);
        },
        toggleContent() {
            this.onlyContent = !this.onlyContent;
            this.$dispatch('ratingtype.toggleContent', this.onlyContent);
        }
    }
};
</script>

滿意是爲:0,不滿意是爲:1,所有是爲:2.

因在點擊切換按鈕,觸發方法,經過傳入參數來替換數據,數據selectType賦值等於參數.參數是自義定,然而能夠在參數下功夫,然用冒泡將數據分出.

food父組件
html

<template>
    <transiton>
        <div class="rating">
            <h4 class="title">商品評價</h4>
            <ratingselect :select-type="selectType" :only-content="onlyContent" :desc="desc" :ratings="food.ratings"></ratingselect>
            <div class="rating-wrapper">
              <ul v-show="food.ratings && food.ratings.length">
                <li v-for="rating in food.ratings" v-show="needShow(rating.rateType,rating.text)" class="rating-item">
                  <div class="user">
                    <span class="name">{{rating.username}}</span>
                    <img class="avatar" width="12" height="12" :src="rating.avatar">
                  </div>
                  <div class="time">{{rating.rateTime | formatDate}}</div>
                  <p class="text">
                    <span v-if="rating.rateType === 0" class="arrowUp iconfont">&#xe901;</span>
                    <span v-if="rating.rateType === 1" class="arrowDown iconfont">&#xe902;</span>{{rating.text}}
                  </p>
                </li>
              </ul>
              <div class="no-rating" v-show="!food.ratings || !food.ratings.length">暫無評價</div>
            </div>
        </div>
    </transiton>
</template>

js

<script type="text/javascript">
    import Vue from 'vue';

    import ratingselect from 'components/ratings/ratingselect';

    const POSITIVE = 0;
    const NEGATIVE = 1;
    const ALL = 2;
  
   export default {
        data() {
            return {
                showFlage: false,
                selectType: ALL,
                onlyContent: true,
                desc: {
                    all: '所有',
                    positive: '推薦',
                    negative: '吐槽'
                }
            };
        },
        methods: {
            needShow(type, text) {
                if (this.onlyContent && !text) {
                return false;
                }
                if (this.selectType === ALL) {
                    return true;
                } else {
                    return type === this.selectType;
                }
            }
        },
        events: {
            'ratingtype.select'(type) {
                this.selectType = type;
                this.$nextTick(() => {
                    this.scroll.refresh();
                });
            },
            'ratingtype.toggleContent'(onlyContent) {
                this.onlyContent = onlyContent;
                this.$nextTick(() => {
                    this.scroll.refresh();
                });
            }
        },
        components: {
            ratingselect
        }
    };
</script>

在事件鉤子上,實行監聽,把冒泡觸發並賦值,數據就獲得.在遍歷數據,用v-show進行判斷.

VUE雜項

過渡流程

只在v-if,v-show,v-for觸動節點的變更效果

當 show 屬性改變時,Vue.js 將相應地插入或刪除元素,按照以下規則改變過渡的 CSS 類名:

若是 show 變爲 false,Vue.js 將:

調用 beforeLeave 鉤子;
添加 v-leave 類名到元素上以觸發過渡;
調用 leave 鉤子;
等待過渡結束(監聽 transitionend 事件);
從 DOM 中刪除元素並刪除 v-leave 類名;
調用 afterLeave 鉤子。
若是 show 變爲 true,Vue.js 將:

調用 beforeEnter 鉤子;
添加 v-enter 類名到元素上;
把它插入 DOM;
調用 enter 鉤子;
強制一次 CSS 佈局,讓 v-enter 確實生效。而後刪除 v-enter 類名,以觸發過渡,回到元素的原始狀態;
等待過渡結束;
調用 afterEnter 鉤子。

better-scroll

節點溢滿時,是設計稿沒有滾動條的,要上下移動.解決使用better-scroll插件.


html

<div class="sellerx" v-el:seller style="overflow: hidden;">
    <div class="seller-content"></div>
</div>

js

<script type="text/javascript">
    ready() {
         this.$nextTick(() => {
             if (!this.scroll) {
                 this.scroll = new Bscroll(this.$els.seller, {
                    click: true
                });
            } else {
                this.scroll.refresh();
            }
        });
    },
</script>

但父元素設置溢出隱藏,可用插件的移動顯出子節點超的內容.要在節點放個接口,使用框架鉤子,建立betterScroll事例,那藏的內容通立體相上下移.better-scroll是調用樣式的translate是子節點上下引動.

less樣式處理

經過引入樣式,有是會錯誤.解決使用設置標籤

<style type="text/less"></style>

處理器會識別到標籤的樣式類別,編譯樣式.

esLint

在使用eslint語法校驗時,常常報錯,但能夠在eslintrc設置進行忽略.

no mixed spaces and tabs

是把標籤縮進與空格捆和使用,解決是可用tab代替空格.

Expected indentation of 2 space characters but found 3 indent

'indent': 0,
'space-before-function-paren': 0
設置縮進空行.

defined but never use

可在前加註銷
/ eslint-disable no-unused-vars /

後序

要靈活的用vue,先要處理好數據的邏輯.
然而要懂得基本的數據傳遞屬性.

子組件傳給父組件-
能夠用接口ref;也能夠子組件的冒泡把數據傳去,父組件用鉤子events監聽並接到數據.
父組件傳給子組件-
能夠在子組件props鉤子,接收父組件的傳遞.也能夠父組件用ref接口調用子組件的方法,並把數據傳入方法去.

實戰是最重要.

相關文章
相關標籤/搜索