這是一個仿餓了么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">

{{ 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!