日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > vue >内容正文

vue

vue + vue-router + vue-resource + es6 + stylus + webpack 高仿饿了么外卖App商家详情

發布時間:2024/1/1 vue 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 vue + vue-router + vue-resource + es6 + stylus + webpack 高仿饿了么外卖App商家详情 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

VUE高仿餓了么app

本項目github地址:https://github.com/motysla/eleme.git

VUE 搭建簡介

剛學習了VUE高仿餓了么app課,記錄課的要點,鞏固知識。

VUE 優勢

Vue.js 是一個用于創建 web 交互界面的。其特點是

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

VUE 搭建工具

借用express + data 構建擬后臺

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

CSS 使用的要點

1像素邊框制作

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

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

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

.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.

自適應寬

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


css

<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接口調用子組件的方法,并把數據傳入方法去.

實戰是最重要.

總結

以上是生活随笔為你收集整理的vue + vue-router + vue-resource + es6 + stylus + webpack 高仿饿了么外卖App商家详情的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。