低仿餓了么H5-純前端Vue版 + 手把手教學(xué)

這是一個仿餓了么H5的前端練手,數(shù)據(jù)是偽造的。曾用vue1寫過一個仿cnodeJs網(wǎng)站,在線預(yù)覽:https://hbxywdk.github.io/vue-cnodeJs/#!/,半年過去,工作中很少有機(jī)會用上vue,vue也早已更新到2.+,想想學(xué)了不用也是白學(xué)了,這里仿個餓了么增加下熟練度。

代碼地址:https://github.com/hbxywdk/eleme-vue2
預(yù)覽點(diǎn)這里:https://hbxywdk.github.io/eleme-vue2-static/#/

網(wǎng)頁是有假的賬戶密碼的部分頁面需要登錄 ↓ ,最好在Chrome手機(jī)模式下瀏覽。
username:admin
password:admin

本地預(yù)覽步驟

# clone 文件
git clone https://github.com/hbxywdk/eleme-vue2.git

# 進(jìn)入 eleme-vue2 文件夾
cd eleme-vue2

# install dependencies
npm install

# 運(yùn)行 npm run dev 會在瀏覽器打開 localhost:8080
npm run dev

如果你已經(jīng)對vue很了解,那么看看預(yù)覽就好不用繼續(xù)閱讀,如果你聽說過vue,想學(xué)習(xí)一下,請繼續(xù)看下去。

使用到的相關(guān)庫或工具:vue2 + vuex2 + vue-router2 + vue-swipe + vue-cli

默認(rèn)大家都已安裝Node.js,且知道基礎(chǔ)的ES6語法。

首先需要安裝vue-cli,vue-cli是一個快速搭建vue項目的工具,就不需要我們一行一行寫webpack配置了。

// 安裝 vue-cli
npm install -g vue-cli

// 初始化一個項目
vue init webpack my-project

// 然后一路回車,記得勾選上vue-router
// 輸入以下命令,等待瀏覽器打開
cd my-project
npm install
npm run dev

得到以下目錄結(jié)構(gòu)

-build
-config
-node_modules
-src            // 項目代碼所在文件夾
-static         
-.babelrc
-.editorconfig
-.eslintignore
-.eslintrc.js
-.gitignore
-index.html
-package.json
-README.md

src為項目代碼所在文件夾

手動添加vuex。(Vuex 是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式)
npm install vuex -S
在src目錄下創(chuàng)建store.js
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex) // 要記得use一下哦

export default new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    Count (state, platform) {
      state.count = platform
    }
  },
  actions: {
    setCount ({commit}, platform) {
      commit('Count', platform)
    }
  },
  getters: {
    getCount: (state) => state.count
  }
})

state為狀態(tài)數(shù)據(jù),觸發(fā)action,mutations會去改變state的值,getters對外提拱state的值。

修改main.js。(main.js為webpack打包入口文件)
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './vuex/store'  // 引入store 

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store, // 注入store 
  template: '<App/>',
  components: { App }
})

加入store

修改App.vue
<template>
  <div id="app">
    ![](./assets/logo.png)
    {{ asd }}                                     // here
    <button @click="change(233)">變?yōu)?33</button> // here
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'app',
  computed: {                                           // here ↓
    asd () {
      return this.$store.getters.getCount
    }
  },
  methods: {
    change (arg) {
      this.$store.dispatch('setCount', arg)
    }
  }
}                                                       // here ↑
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

computed 寫入 asd () {return this.$store.getters.getCount} ,通過vuex的getters就可得到state中的count,并在當(dāng)前文件夾中以 asd 存在。
methods 中寫入 this.$store.dispatch('setCount', arg), 執(zhí)行change 方法會觸發(fā)action ,改變count值為233,state值改變,computed 就會改變當(dāng)前asd的值,template中也會相應(yīng)改變。
更多vuex用法詳見:https://vuex.vuejs.org/zh-cn/intro.html

Vue-router。(vue-router通常用于制作單頁面應(yīng)用,如其名就是給vue使用的路由,不清楚路由的可以百度一下)

在components文件夾中新建幾個我們要用到的 .vue 文件,直接復(fù)制默認(rèn)的Hello.vue文件即可,重命名并修改其中的不需要的內(nèi)容。

修改router/index.js
import Vue from 'vue';
import Router from 'vue-router';
//  引入組件
import Homepage from 'components/Homepage';
import Order from 'components/Order';
import Myzone from 'components/Myzone';
import Business from 'components/Business';
import Login from 'components/Login';
import Search from 'components/Search';
Vue.use(Router);

export default new Router({
  routes: [
    {
      path: '/',
      name: 'homepage',
      component: Homepage
    },
    {
      path: '/order',
      name: 'order',
      component: Order
    },
    {
      path: '/myzone',
      name: 'myzone',
      component: Myzone
    },
    {
      path: '/business/:id',
      name: 'business',
      component: Business
    },
    {
      path: '/search/:keyword',
      name: 'Search',
      component: Search
    },
    {
      path: '/login',
      name: 'Login',
      component: Login
    }
  ]
});

我們引入空白組件,并定義路由,當(dāng)訪問對應(yīng)路徑時,如localhost:8080/#/business,App.vue中的<router-view></router-view>就會展示對應(yīng)內(nèi)容,做到無刷新跳轉(zhuǎn)。path: '/business/:id',后面的 :id 則是要傳遞的參數(shù),組件中可以this.$route.params.id得到其值。

template中用<router-link>來跳轉(zhuǎn),<router-link> 默認(rèn)會被渲染成一個 <a> 標(biāo)簽 <router-link to="/business/123">Go to Foo</router-link>,js中可用router.replace(location)來進(jìn)行跳轉(zhuǎn)。

更多用法詳見http://router.vuejs.org/zh-cn/essentials/getting-started.html

下面進(jìn)入到代碼部分

整個項目使用 rem 方式布局,根據(jù)屏幕的寬度來自適應(yīng)不同手機(jī)屏幕。
數(shù)據(jù)為偽造。
首頁 Homepage.vue

// 部分代碼
export default {
  mounted () {
    this.$store.dispatch('setLoading', true);// 設(shè)置當(dāng)前狀態(tài)為加載中,顯示加載動畫
    var time = Math.floor(Math.random() * 2000); // 模擬請求等待
    setTimeout(() => {
      this.$store.dispatch('setLoading', false); // 頁面顯示
      this.showMe = true;
    }, time);
  },
  computed: {
    ...mapGetters([
      'getLogin',
      'getFalseHotWord',
      'getFalseBussinessbrief' // 商家簡略信息
    ])
  }
};

由于是純前端頁面,故使用setTimeout模擬下數(shù)據(jù)的加載,展示加載動畫,computed中使用mapGetters來得到state中的數(shù)據(jù),這樣寫與this.$store.getters.getCount實(shí)質(zhì)上沒有什么區(qū)別,但需要得到的數(shù)據(jù)較多時能少寫不少代碼。(使用前需引入)

導(dǎo)航部分使用 Vue-swipe 插件,https://github.com/ElemeFE/vue-swipe

商家列表部分,只有五個偽商家數(shù)據(jù),加載更多的不斷添加這五個偽數(shù)據(jù)。

Business.vue(商家詳情頁)
// 部分代碼
export default {
  computed: {
    // 通過id找到store中對應(yīng)店鋪信息
    business_info () {
      return this.$store.getters.getFalseBussinessInfo[this.$route.params.id];
    }
  },
  methods: {
    // 右列表控制左列表樣式
    rightControlLeftClass () {
       // code
    },
    // 左列表點(diǎn)擊控制右列表滾動
    leftControlRightScroll (index) {
       // code
    },
    // 監(jiān)控網(wǎng)頁的resize來改變商品列表的高度
    watchHei () {
       // code
    },
    // 列表中的加按鈕點(diǎn)擊
    add_food (n, x, e) { // n 為大類 x為大類種商品
       // code
    },
    // 向購物車添加
    add_shopping_car (type, typename, foodname, foodid, foodprice) {
      if (!this.shoppingCarList[foodid]) {
        this.shoppingCarList[foodid] = {
          'type_accumulation': type,
          'type_name': typename,
          'name': foodname,
          'one_food_id': foodid,
          'unit_price': foodprice,
          'count': 1
        };
      } else {
        this.shoppingCarList[foodid].count++;
      }
      // 購物車改變 相關(guān)計算
      this.spChangeComputeAll();
    },
    // 購物車改變 相關(guān)計算
    spChangeComputeAll () {
       // code
    },
    // 結(jié)賬
    checkout () {
       // code
    },
    // 添加商品拋球效果實(shí)現(xiàn)
    ball_fly (e) {
      // 每次添加獲得點(diǎn)擊按鈕的坐標(biāo)與目標(biāo)位置的坐標(biāo),算得高度差值,并生成如下結(jié)構(gòu)小球
      // <div class="father">
      //   <div class="child"></div>
      // </div>
      // father水平方向勻速運(yùn)動,child垂直方向使用css3垂直上拋后再下降,即可得到拋球效果。
    },
  }
};

商家詳情頁占一整屏,最外層不允許出現(xiàn)滾動條,分上下兩部分,上半部分給固定rem高度,下半部分高度動態(tài)計算,屏幕大小改變再次計算,下半部分,左右各一個ul列表,設(shè)置高度100%,overflow-y:auto;超出即可滾動。

左右列表
右ul滾動監(jiān)控所有標(biāo)題行的offsetTop,對比當(dāng)前ul的scrollTop,給左ul添加對應(yīng)樣式。點(diǎn)擊左列表,來控制右列表滾動。

購物車
data中定義一個購物車對象{ },添加某樣商品時,已存在就+1,不存在則添加屬性,每次添加計算總價等相關(guān)數(shù)據(jù)。

添加商品的拋球效果
每次添加獲得點(diǎn)擊按鈕的坐標(biāo)與目標(biāo)位置的坐標(biāo),算得高度差值,利用初中物理,水平方向勻速運(yùn)動,垂直方向垂直上拋運(yùn)動,并添加向下的重力,即可得到拋球效果。

結(jié)算
結(jié)算時將當(dāng)前購物車中計算出的簡略信息添加到state中,清空購物車,跳轉(zhuǎn)路由。

其他幾個頁面基本只是展示功能這里也就不再贅述。

如果想要更詳細(xì)的內(nèi)容,可以直接閱讀代碼,內(nèi)有更詳細(xì)的注釋。

這個練習(xí)還有許多不夠完善的地方,有機(jī)會的話我會完善這些不足。
如果可以的話順手點(diǎn)個star,Thanks!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 兔 倌 1980年底,部隊精簡整編,每個連隊要有三分之一的戰(zhàn)士退伍,包括80年入伍的新...
    小五子_94ae閱讀 1,308評論 1 3
  • 昨天在B 站看到一個視頻。是幾個女孩子講述她們整容的經(jīng)歷的。片子不長,但是幾個女生都講了自己整容的原因。有的是覺得...
    谷雨寫在四月后閱讀 1,439評論 0 3
  • 紅色歷史知多少調(diào)查問卷 2017年7月7日肖靜 雨后的空氣格外清新,小紅旗實(shí)踐隊來到紫荊山進(jìn)行問卷調(diào)查。這是我的第...
    我想靜靜_24d6閱讀 138評論 0 0