這是一個點餐系統,包含用戶點餐、商家出餐、管理員管理三部分功能 這個項目原本是校內實訓,須要用java編寫,我負責一部分。可是我不太喜歡用java,且時間足夠,就本身獨自作了一份,用於學習。 項目的功能和需求是根據前期小組討論出來的,也基本都是仿餓了麼的 各項功能基本都實現了javascript
線上地址:(比較慢)47.93.254.91:3333前端
源碼地址:chihuobaovue
登陸帳號:
用戶:12345678910
商家:11112222333
管理員:admin2
登陸密碼都是123456
複製代碼
npm install
npm run dev
cd server #打開koa2後臺,會開啓3333端口
npm install
node bin/www
複製代碼
npm run build #打包
cp dist/* server/public/ #將打包好的文件放到koa2靜態目錄
複製代碼
頂層就是vue-cli的結構,主要看前端src和後臺server的結構java
─ src
├── common #
│ ├── audio #音頻
│ ├── images #圖片
│ ├── javascript #api接口、cache、config等js文件
│ ├── style #公用style
├── components #組件
├── pages #頁面,處理業務,主要分爲三個模塊
│ ├── admin
│ ├── seller
│ ├── user
│ ├── index.vue
│ ├── login.vue
├── router #路由
│ ├── index.js
├── store #vuex的store,分了三個模塊
│ ├── admin
│ ├── seller
│ ├── user
│ ├── index.js
├── App.vue
├── main.js
複製代碼
─ server
├── app
├── ├── common # 工具
├── ├── controllers # 業務
├── ├── models # 定義數據庫模型
├── db_vue # 導出來的數據庫數據
├── routes # 路由
├── app.js
├── config.js # 短信api的key相關
複製代碼
我以前用react,爲了熟悉webpack就沒有使用腳手架(如yeoman),深深感覺到了babel的複雜,webpack配置的繁瑣。用到vue-cli簡直就是一個字:爽,各類複雜的配置都配好了,如使用sass下載後在style配置一下就行了,不用再到webpack配置,這些發雜的配置本該就不要重複作。如今Parceiljs打包工具也出來了,之後能夠更爽快的開發了node
對vue的感受就是真的對新手很友好,官網教程很全,例子不少,上手快。 使用vuex + map輔助函數用起來很方便 下面是一個登錄的例子react
# login.vue
# 先請求登陸,返回用戶信息,經過vuex的mapAction函數調用actions,這裏vuex分了user、seller、admin模塊
methods: {
...mapAction('user',
[
'saveUserInfo'
]
),
login () {
_loginApi(phone, pass).then(res => {
this.saveUserInfo(res.data)
this.$router.push('/home')
})
}
}
複製代碼
# user/actions.js
# 調用函數,先作一個客戶端存儲存到localStorage,再存到state中
import { _saveUserInfo } from 'common/javascript/cache'
export function saveUserInfo ({commit, state}, info) {
commit(types.SET_USER_INFO, _saveUserInfo(info))
}
複製代碼
# index.vue
# 須要數據的組件用vuex的mapGetters函數獲取
<template>
<user-header :userInfo='userInfo'></user-header>
</template>
<script>
export default {
computed: {
...mapGetters(
'user',
[
'userInfo',
'reLogin'
]
)
}
}
</script>
複製代碼
數據的流向是單向的 webpack
一開始沒有分模塊是這樣寫的ios
# store.js
#
export default new Vuex.Store({
getter,
state,
mutations,
actions
})
# 組件調用,直接調用
computed: {
...mapGetters(['suggestion'])
},
methods: {
...mapActions(['saveUserInfo']),
...mapMutations({
setCoordinate: 'SET_COORDINATE'
})
}
複製代碼
分了模塊寫法有區別的git
# store.js
# 各模塊分別有各自的state、getters、actions
# 模塊結構本身定義,因此能夠定義一個頂層公用的,再在裏層分模塊
export default new Vuex.Store({
modules: {
user,
seller,
admin
}
})
# 組件調用
# 調用要有模塊名,mapActions取不一樣模塊時要分開取
computed: {
...mapGetters(
'user',
[
'suggestionList',
'userInfo'
]
)
},
methods: {
...mapMutations({
setCoordinate: 'user/SET_COORDINATE'
}),
...mapActions('user',
[
'saveInfo'
]
),
...mapActions('seller',
[
'saveSellerInfo'
]
)
}
複製代碼
通常父子組件,是父組件向子組件傳入數據,子組件顯示數據,數據單向流動。 當子組件須要傳遞數據給父組件時,經過觸發函數,以參數的形式向父組件傳遞數據,跟react數據傳遞同樣github
# 父組件
<food-card @addOne='addOne' :info='info'></food-card>
# 子組件
<p class='name'>{{info.dishName}}</p>
<span class='money'>¥{{info.dishPrice}}</span>
<div :class='_status' @click='addToCart'>加入購物車</div>
...
props: {
info: {
type: Object, #定義父組件傳入的數據類型,當傳入類型和定義的不一致,vue會警告
default: {}
}
},
methods: {
addToCart () {
this.$emit('addOne', this.info) #用this.$emit觸發父組件的addOne函數
}
}
複製代碼
上面的例子中,不能修改父組件傳入的數據。若要修改數據,則須要在$emit前複製一份數據而後修改,再傳遞給父組件,也能夠用sync實現父子組件數據雙向綁定。sync在2.0被移除由於這破壞了單向數據流,但2.3又引入了,由於有場景須要如一些複用的組件。但sync和之前的實現又有點不同,它只是一個語法糖,會被擴展爲一個自動更新父組件屬性的 v-on 監聽器。 而且子組件須要顯示觸發更新:this.$emit('update:xx', newVal)
# 父組件
<card-item :data.sync='item'></card-item>
#會被擴展爲這樣
<comp :data="item" @update:data="newVal => item = newVal"></comp>
# 子組件
<input class='commend' type="text" v-model='commend' placeholder="寫下對此菜品的評價">
export default {
data () {
return {
commend: ''
}
},
watch: {
commend (newC) {
this.data.commend = newC
this.$emit('update:data', this.data) #顯示觸發data的更新達到雙向數據綁定
}
},
props: {
data: {
type: Object,
default: {}
}
}
}
複製代碼
使用了element-ui樣式框架,有時須要對他們的組件作一些樣式的修改。但它是封裝好的,我就須要查看源代碼才知道它內部定義的類或標籤來自定義樣式,可是發現無效,舉個例子
<el-rate v-model="item.score" disabled show-text text-color="#ff9900">
</el-rate>
<style scoped lang='sass'>
.el-rate #組件都自帶同名的類
div
background: red
<style>
#發現element-ui經過jsfiddle演示的代碼卻沒問題,就查找不一樣點,而後發現是style標籤的scoped致使的,可能侷限了樣式的做用範圍。去掉就能夠了,此時要注意樣式是全局的,因此要注意類名的使用
複製代碼
在查看商鋪頁面,能夠選擇不一樣類型商家,也能夠搜索商家,能夠有不一樣的實現方法。能夠把狀態全放在在組件內或vuex管理,可是這樣刷新後狀態就消失了。因此我選擇用url的hash來保存狀態,經過監聽路由變化來加載不一樣數據。商家列表數據放在vuex
# 商家頁面,place.vue
data () {
return {
pageNum: 1,
totalPage: 1,
keyword: '',
loading: false
}
},
created () {
this.getList()
# 滾動加載下一頁
window.onscroll = () => {
if (!this.loading && this.__getScrollHeight() <= (this.__getWindowHeight() + window.scrollY + 100)) {
if (this.pageNum < this.totalPage) {
this.loading = true
this.pageNum++
this.getList()
}
}
}
},
watch: {
$route () {
this.getList()
}
},
methods: {
changeTag (tag) {
this.pageNum = 1
this.shopType = tag
this.keyword = ''
# this.clearShopList()
this.$router.push({path: '/place', query: {shopType: code, keyword: undefined}})
},
search (str) {
this.keyword = str
this.pageNum = 1
this.shopType = 1
# this.clearShopList()
this.$router.push({path: '/place', query: {shopType: undefined, keyword: str}})
},
getList () {
const { keyword, shopType } = this.$router.currentRoute.query
this.loading = true
_getShopList(keyword, shopType, this.pageNum).then(res => { #請求數據,而後concat到商家list存入vuex
...
})
}
}
複製代碼
後面使用時,發現了bug:在別的頁面變更路由,這裏會加載了重複的數據。因此要限定監聽路由變更的路由,在本頁面纔有效
watch: {
$route () {
if (this.$router.currentRoute.name === 'place') {
this.getList()
}
}
}
複製代碼
而後又發現bug:從別的頁面回到這裏,也加載了重複的數據,解決辦法是離開組件時把原數據刪除。要這樣作是由於我把數據存入了vuex,感受沒必要要存入vuex..
beforeDestroy () {
window.onscroll = null
this.clearShopList()
},
methods: {
...mapMutations({
clearShopList: 'user/CLEAR_SHOP_LIST'
})
}
複製代碼
請求有時須要出現異常如401,須要讓用戶從新登陸,我用的是axios
# 這是一個請求封裝,返回異常所有調用reLogin的action返回登陸頁
import store from '../../store'
export function basePOST (api, params) {
return axios({
method: 'post',
url: api,
headers: {
'content-Type': 'application/x-www-form-urlencoded'
},
data: config.toFormData({
...params
})
}).then(res => {
return res.data
}).catch(() => {
store.dispatch('user/reLogin')
})
}
複製代碼
npm install koa-generator -g
koa2 server
cd server && npm install
npm start
複製代碼
const session = require('koa-session2')
app.use(session({
key: 'sessionid---'
}))
複製代碼
# 注意,時間須要用變量放入,不然無效
var staticCache = require('koa-static-cache')
const cacheTime = 365 * 24 * 60 * 60
app.use(staticCache(path.join(__dirname, 'public'), {
maxAge: cacheTime
}))
複製代碼
var compress = require('koa-compress')
app.use(compress({
filter: function (contentType) {
return /text/i.test(contentType)
},
threshold: 2048,
flush: require('zlib').Z_SYNC_FLUSH
}))
複製代碼
在建表時須要注意數據類型,若schema定義是Number,存入的倒是String,會報錯。 若schema沒有定義字段,建立collection時傳入其餘字段,會存不進去 數據庫的Number數據,用字符查找會找不到如:user.find({age: '18'})
mongodb用的是中時區的時間,咱們是東八區,因此時間都會晚8小時,用moment插件處理 moment是用在客戶端,而不是存儲。存儲的數據是中時區的,在顯示數據時修正 由於moment不少地方都要用,因此我直接將它放入Vue的原型,這樣全部vue實例均可以拿到這個方法
#main.js
import Vue from 'vue'
import moment from 'moment'
Object.defineProperty(Vue.prototype, '$moment', {value: moment})
#組件中
<p class='fr date'>{{$moment(item.commentDate).format('YYYY-MM-DD HH:mm:ss')}}</p>
複製代碼
用post傳輸數據,用x-www-form-urlencoded格式,可是表單裏有個對象數組:
{
userId: 13546515,
dishs: [{id: 4545, num: 2, price: 12}, {id: 1446, num: 1, price: 8}],
...
}
複製代碼
我用node作後臺,拿這個數據一點問題都沒有,可是小組內跟java後臺配合,不能這樣傳對象數組字符串。他須要直接用List<>包裝,須要這樣傳遞才行
let dishs = {}
_dishs.forEach((item, index) => {
for (let key in item) {
if (!dishs[`dishs[${index}]`]) {
dishs[`dishs[${index}]`] = {}
}
dishs[`dishs[${index}]`][key] = item[key]
}
})
let temp = {}
let result = {}
for (let i in dishs) {
for (let j in dishs[i]) {
if (!temp[i]) temp[i] = {}
result[`${i}.${j}`] = dishs[i][j]
}
}
複製代碼
server/db_vue
路徑是導出的數據,能夠導入到本身的mongodb數據庫
# d是數據庫名,c是collection名
mongoimport -d vue -c users --file vue/users.json
複製代碼
整體來講,項目結構還算清晰,我對Vue還不是很熟悉,因此運用的還不是很好,好比使用Vuex的使用,我對於不一樣組件須要共享的數據存入store或同時存在本地,對於單個組件內的數據,感受不必存入store。 對koa2也不是很熟悉,一開始老是忘記await等各類小問題,寫完雖然作了緩存壓縮,由於不是一個後端,因此性能上仍是弄很差,線上的可能比較卡,由於是學生服務器。 寫完這個對Vue熟悉一點了,接下來會繼續學學Vue的原理,學學新東西 以上就是對項目的總結,若是有錯,望指正