前言
前进!前进!不择手段地前进!!

距离Vue3.0 beta 发布已经过了半个多月了。本来这个东西上个月就应该写了,由于公司上个月赶项目一直没时间。趁着劳动节把这个东西写了一下,也顺便把一些坑过了一下。
介绍
页面比较简单,算是把 Composition API 过了一下了

- 基于
Vue3.0 beta这种页面也比较老套了 - 涉及了
Vue3.0 betaandVuex4 betaandaxios
安装vue3环境
题外话: 听说
vite不错,这几天得了解下🤪
- 安装(升级)最近的
vue-cli vue create projectNameps: 如果自己不会手动装vuex4或者vue-router4这些库最好是直接安装步骤全部安装(减少一些踩坑的时间成本)vue add vue-next这个命令会把项目中的一些依赖自动升级成支持vue3的版本npm run serve
Composition-API
这里建议直接看文档 https://vue-composition-api-rfc.netlify.app/#summary
🤐直接贴代码了
毕竟思路都差不多,可以的话建议直接看 https://github.com/notbucai/vue3-demo
useScroll
1import { ref } from 'vue'; 2export const useScrollTop = () => { 3 const top = ref(0); 4 window.addEventListener('scroll', () => { 5 const scrollTop = document.documentElement.scrollTop; 6 top.value = scrollTop; 7 }); 8 return top; 9}
store
1import { createStore } from "vuex"; 2 3export default createStore({ 4 state: { 5 shopcart: {}, 6 foodList: [], 7 }, 8 getters: { 9 // TODO: 这里的金额计算 是"有问题"的 10 // 计算金额与数量 11 settlement(state) { 12 13 let allPrice = 0; 14 let allCount = 0; 15 16 const shopcart = state.shopcart; 17 const foodList = state.foodList; 18 // 得到所有食物列表 19 const foods = foodList.reduce((previousValue, currentValue) => { 20 return previousValue.concat(currentValue.foods); 21 }, []); 22 // 通过购物车计算金额数量 23 Object.keys(shopcart).forEach(key => { 24 const count = shopcart[key]; 25 if (!count) return; 26 const food = foods.find(item => item.item_id == key); 27 const price = food.specfoods[0].price; 28 // 这里只是一种简单的处理浮点金额的方案(不保险),实际上后端直接返回以分为单位的金额会更好,【在后端计算金额一定情况下可能更靠谱】 29 allPrice += (price * 10000 * count); 30 allCount += count; 31 }); 32 33 return { 34 price: allPrice / 10000, 35 count: allCount, 36 } 37 } 38 }, 39 mutations: { 40 ADD_ITEM(state, id) { 41 state.shopcart[id] = state.shopcart[id] || 0; 42 state.shopcart[id]++; 43 }, 44 SUB_ITEM(state, id) { 45 state.shopcart[id] = state.shopcart[id] || 0; 46 state.shopcart[id]--; 47 }, 48 SET_FOOD_LIST(state, payload) { 49 state.foodList = payload; 50 } 51 }, 52 actions: { 53 setFoodList({ commit }, payload) { 54 commit('SET_FOOD_LIST', payload) 55 }, 56 addItem({ commit }, id) { 57 commit('ADD_ITEM', id) 58 }, 59 subItem({ commit }, id) { 60 commit('SUB_ITEM', id) 61 }, 62 } 63});
页面
1<template> 2 <div class="Catering"> 3 <header class="header"> 4 <div class="shop"> 5 <div class="shop-pic"> 6 <img 7 src="https://upload.jianshu.io/users/upload_avatars/7759683/995f635d-357b-4539-b008-55aa6b0ac140.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/96/h/96/format/webp" 8 alt 9 /> 10 </div> 11 <div class="shop-name">一袋米</div> 12 </div> 13 <div class="navbar"> 14 <div class="nav-item">点餐</div> 15 <div class="nav-item">评价</div> 16 <div class="nav-item">商家</div> 17 </div> 18 <div class="outher-box"></div> 19 </header> 20 <main class="main"> 21 <div class="left"> 22 <div 23 class="left-item" 24 v-for="(item, index) in list" 25 :class="{active:elIndex == index}" 26 :key="index" 27 @click="srcollTo(index)" 28 >{{item.name}}</div> 29 </div> 30 <div class="right"> 31 <template v-for="(_item, index) in list"> 32 <!-- 这里的ref就又点难受了 --> 33 <div :key="index" :ref="el=>itemEls[index] = el"> 34 <food-item :item="_item" /> 35 </div> 36 </template> 37 </div> 38 <settlement-bottom /> 39 </main> 40 </div> 41</template> 42<script> 43import { watch, ref, onBeforeUpdate, onMounted, computed } from 'vue'; 44import { useStore } from 'vuex'; 45import { useScrollTop } from '../hooks'; 46import { getList } from '../ajax/index'; 47import FoodItem from './components/FoodItem'; 48import SettlementBottom from './components/SettlementBottom'; 49 50export default { 51 components: { FoodItem, SettlementBottom }, 52 setup() { 53 // store 54 const store = useStore(); 55 56 const list = computed(() => store.state.foodList); 57 58 onMounted(async () => { 59 // NOTE: 获取数据 60 let resList = await getList(); 61 // 存入store 62 store.dispatch('setFoodList', resList); 63 }); 64 65 // 组节点列表 66 const itemEls = ref([]); 67 // 当前选中节点 68 const elIndex = ref(0); 69 70 // 监听数据 71 const scrollTop = useScrollTop([]); 72 73 onBeforeUpdate(() => { 74 // 更新时清空 75 itemEls.value = []; 76 }); 77 78 // 监听滚动条变化 79 watch(scrollTop, () => { 80 let index = 0; 81 // 判断距离头部最近的元素 实际上这里是可以缓存高度的 82 itemEls.value.forEach((el, i) => { 83 // console.log('el=>',el,'val=>', scrollTop.value); 84 if (el.offsetTop > scrollTop.value) return; 85 index = i; 86 }); 87 // 绑定节点 88 elIndex.value = index; 89 }); 90 91 const srcollTo = index => { 92 elIndex.value = index; 93 const el = itemEls.value[index]; 94 const offsetTop = el.offsetTop; 95 window.scrollTo(0, offsetTop); 96 }; 97 98 return { 99 list, 100 itemEls, 101 elIndex, 102 srcollTo 103 }; 104 } 105}; 106</script> 107<style lang="scss"> 108...... 109</style>
FoodItem.vue
1<template> 2 <div class="FoodItem"> 3 <div class="food-title">{{item.name}}</div> 4 <div class="food-item" v-for="(food) in item.foods" :key="food.item_id"> 5 <div class="food-pic"> 6 <img 7 src="https://upload.jianshu.io/users/upload_avatars/16175630/e2ee85e5-7cb0-429d-a517-bb1c6f1833e4?imageMogr2/auto-orient/strip|imageView2/1/w/80/h/80/format/webp" 8 alt 9 /> 10 </div> 11 <div class="food-main"> 12 <div class="food-info"> 13 <div class="title">{{food.name}}</div> 14 <div class="info">{{food.applicable_quantity_text}}</div> 15 </div> 16 <div class="money">¥{{food.specfoods[0].price}}</div> 17 </div> 18 <div class="food-action"> 19 <div class="action-item minus" v-if="shopcart[food.item_id]" @click="handleMinus(food)">-</div> 20 <div class="action-num" v-if="shopcart[food.item_id]">{{shopcart[food.item_id]}}</div> 21 <div class="action-item puls" @click="handlePuls(food)">+</div> 22 </div> 23 </div> 24 </div> 25</template> 26<script> 27// import { reactive, unref } from 'vue'; 28import { useStore } from 'vuex'; 29import { computed } from 'vue'; 30export default { 31 components: {}, 32 props: { 33 item: Object 34 }, 35 setup() { 36 // store 37 const store = useStore(); 38 // 购物车 39 const shopcart = computed(() => store.state.shopcart); 40 41 const handleChangeCount = (food, type) => { 42 const id = food.item_id; 43 const actionName = type > 0 ? 'addItem' : 'subItem'; 44 store.dispatch(actionName, id); 45 }; 46 47 const handleMinus = food => { 48 handleChangeCount(food, -1); 49 }; 50 const handlePuls = food => { 51 handleChangeCount(food, 1); 52 }; 53 54 return { 55 shopcart, 56 handleMinus, 57 handlePuls 58 }; 59 } 60}; 61</script> 62<style lang="scss" scoped> 63...... 64</style>
SettlementBottom.vue
1<template> 2 <div class="SettlementBottom"> 3 <div class="num_box">x{{settlement.count}}</div> 4 <div class="price_box">¥{{settlement.price}}</div> 5 <div class="submit_btn" @click="handleSubmit">结算</div> 6 </div> 7</template> 8<script> 9import { useStore } from 'vuex'; 10import { computed } from 'vue'; 11export default { 12 props: {}, 13 setup() { 14 const store = useStore(); 15 const settlement = computed(() => store.getters.settlement); 16 const handleSubmit = () => { 17 const sc = store.state.shopcart; 18 const shopcart = Object.keys(sc).map(key => ({ 19 id: key, 20 count: sc[key] 21 })); 22 23 console.log('shopcart=>', shopcart); 24 console.log('如果需要原食物的参数可以直接去state中获取'); 25 }; 26 return { 27 settlement, 28 handleSubmit 29 }; 30 } 31}; 32</script> 33<style lang="scss" scoped> 34...... 35</style>
