仿美团pc,koa+ssr(三)
一,通过SSR渲染,将城市服务的数据传给客户端,不是通过异步请求接口获取数据
store-->新建modules目录--》新建geo.js
nuxt之vuex中使用nuxtServerInit方法
官网介绍nuxtServerInit方法
https://zh.nuxtjs.org/guide/vuex-store/#nuxtserverinit-%E6%96%B9%E6%B3%95
vuex中模块化使用示例
挂载vuex中state模块及nuxtServerInit方法, store-->index.js
在geo.vue组件中获取数据
<template> <div class="m-geo"> <i class="el-icon-location"/>{{ $store.state.geo.position.city }} <nuxt-link class="changeCity" to="/changeCity">切换城市</nuxt-link> [香河 廊坊 天津] </div> </template>
二,通过SSR渲染,将商品列表数据传给客户端,不是通过异步请求接口获取数据
store-->modules-->新建home.js
const state = () => ({menu: [], hotPlace: []}) const mutations = { setMenu(state, val) { state.menu = val }, setHotPlace(state, val) { state.hotPlace = val } } const actions = { setMenu: ({ commit }, menu) => { commit('setMenu', menu) }, setHotPlace: ({ commit }, hotPlace) => { commit('setHotPlace', hotPlace) } } export default {namespaced: true, state, mutations, actions}
store-->index.js,通过ssr服务端获取数据,存储到vuex,让客户端获取数据
import Vue from 'vue' import Vuex from 'vuex' import geo from './modules/geo' import home from './modules/home' Vue.use(Vuex) const store = () => new Vuex.Store({ modules: { geo, home }, actions: { async nuxtServerInit({ commit }, {req, app}) { const { status, data: { province, city } } = await app.$axios.get('/geo/getPosition') // 提交到geo.js文件中的setPosition函数,将当前城市位置{city,province}保存在vuex中 commit('geo/setPosition',status===200?{city,province}:{city:'',province:''}) const {status:status2,data:{menu}}=await app.$axios.get('geo/menu') //提交到home.js文件中的setMenu函数,将menu商品分类数据保存在vuex中 commit('home/setMenu',status2===200?menu:[]) const {status:status3,data:{result}}=await app.$axios.get('/search/hotPlace',{ params:{ city:app.store.state.geo.position.city.replace('市','') } }) //提交到home.js文件中的setHotPlace函数,将result热门景点数据保存在vuex中 commit('home/setHotPlace',status3===200?result:[]) } } }) export default store
menu.vue组件中,获取数据
<template> <div class="m-menu"> <!-- 左侧商品列表 --> <dl class="nav" @mouseleave="mouseleave"> <dt>全部分类</dt> <dd v-for="(item,idx) in $store.state.home.menu" :key="idx" @mouseenter="enter"> <i :class="item.type"/>{{ item.name }}<span class="arrow"/> </dd> </dl> <!-- 右侧商品列表 --> <div v-if="kind" class="detail" @mouseenter="sover" @mouseleave="sout"> <template v-for="(item,idx) in curdetail.child"> <h4 :key="idx">{{ item.title }}</h4> <span v-for="v in item.child" :key="v">{{ v }}</span> </template> </div> </div> </template>
computed:{ curdetail:function(){ return this.$store.state.home.menu.filter(item => item.type===this.kind)[0] } },
三,点击输入框,输入关键字,发送请求,获取与输入关键词有关的数据,以及,热门景点数据填充
在searchbar.vue组件中
<template> <div class="search-panel"> <el-row class="m-header-searchbar"> <el-col :span="3" class="left"> <img src="//s0.meituan.net/bs/fe-web-meituan/e5eeaef/img/logo.png" alt="美团"> </el-col> <el-col :span="15" class="center"> <div class="wrapper"> <el-input v-model="search" placeholder="搜索商家或地点" @focus="focus" @blur="blur" @input="input"/> <button class="el-button el-button--primary"><i class="el-icon-search"/></button> <dl v-if="isHotPlace" class="hotPlace"> <dt>热门搜索</dt> <dd v-for="(item,idx) in $store.state.home.hotPlace.slice(0,5)" :key="idx"> <a :href="'/products?keyword='+encodeURIComponent(item.name)">{{ item.name }}</a> </dd> </dl> <dl v-if="isSearchList" class="searchList"> <dd v-for="(item,idx) in searchList" :key="idx"> <a :href="'/products?keyword='+encodeURIComponent(item.name)">{{ item.name }}</a> </dd> </dl> </div> <p class="suggest"> <a v-for="(item,idx) in $store.state.home.hotPlace.slice(0,5)" :key="idx" :href="'/products?keyword='+encodeURIComponent(item.name)">{{ item.name }}</a> </p> <ul class="nav"> <li><nuxt-link to="/" class="takeout">美团外卖</nuxt-link></li> <li><nuxt-link to="/" class="movie">猫眼电影</nuxt-link></li> <li><nuxt-link to="/" class="hotel">美团酒店</nuxt-link></li> <li><nuxt-link to="/" class="apartment">民宿/公寓</nuxt-link></li> <li><nuxt-link to="/" class="business">商家入驻</nuxt-link></li> </ul> </el-col> <el-col :span="6" class="right"> <ul class="security"> <li><i class="refund"/><p class="txt">随时退</p></li> <li><i class="single"/><p class="txt">不满意免单</p></li> <li><i class="overdue"/><p class="txt">过期退</p></li> </ul> </el-col> </el-row> </div> </template> <script> import _ from 'lodash' export default { data(){ return { search:'', isFocus:false, hotPlace:[], searchList:[] } }, computed:{ isHotPlace:function(){ return this.isFocus&&!this.search }, isSearchList:function(){ return this.isFocus&&this.search } }, methods:{ focus:function(){ this.isFocus=true }, blur:function(){ let self=this; setTimeout(function(){ self.isFocus=false },200) }, // 做个防抖延时函数,引入lodash input:_.debounce(async function(){ let self=this; let city=self.$store.state.geo.position.city.replace('市','') self.searchList=[] let {status,data:{top}}=await self.$axios.get('/search/top',{ params:{ input:self.search, city } }) // 截取十条数据 self.searchList=top.slice(0,10) },300) } } </script> <style lang="css"> </style>
四,移入不同的标签,展示不同的图片数据
在artistic.vue组件中
<template> <section class="m-istyle"> <dl @mouseover="over"> <dt>有格调</dt> <dd :class="{active:kind==='all'}" kind="all" keyword="景点">全部</dd> <dd :class="{active:kind==='part'}" kind="part" keyword="美食">约会聚餐</dd> <dd :class="{active:kind==='spa'}" kind="spa" keyword="丽人">丽人SPA</dd> <dd :class="{active:kind==='movie'}" kind="movie" keyword="电影">电影演出</dd> <dd :class="{active:kind==='travel'}" kind="travel" keyword="旅游">品质出游</dd> </dl> <ul class="ibody"> <li v-for="item in cur" :key="item.title"> <el-card :body-style="{ padding: '0px' }" shadow="never"> <img :src="item.img" class="image"> <ul class="cbody"> <li class="title">{{ item.title }}</li> <li class="pos"><span>{{ item.pos }}</span></li> <li class="price">¥<em>{{ item.price }}</em><span>/起</span></li> </ul> </el-card> </li> </ul> </section> </template> <script> export default { data: () => { return { // 默认属性 kind: 'all', list: { // 通过kind属性获取对应的数据 all: [], part: [], spa: [], movie: [], travel: [] } } }, computed: { // 计算不同类型数据 cur: function () { return this.list[this.kind] } }, // 刚加载页面,加载默认的数据 async mounted(){ let self=this; let {status,data:{count,pois}}=await self.$axios.get('/search/resultsByKeywords',{ params:{ // 默认 keyword:'景点', city:self.$store.state.geo.position.city } }) if(status===200&&count>0){ let r= pois.filter(item=>item.photos.length).map(item=>{ return { title:item.name, pos:item.type.split(';')[0], price:item.biz_ext.cost||'暂无', img:item.photos[0].url, url:'//abc.com' } }) self.list[self.kind]=r.slice(0,9) }else{ self.list[self.kind]=[] } }, methods: { // 移入事件 over: async function (e) { let dom = e.target let tag = dom.tagName.toLowerCase() let self = this if (tag === 'dd') { // 获取自定义的属性,请求参数 this.kind = dom.getAttribute('kind') let keyword = dom.getAttribute('keyword') let {status,data:{count,pois}}=await self.$axios.get('/search/resultsByKeywords',{ params:{ keyword, city:self.$store.state.geo.position.city } }) if(status===200&&count>0){ // 过滤出有图片的数据,并且数据库的字段和前端的字段需要映射(前端字段和数据库字段不一致) let r= pois.filter(item=>item.photos.length).map(item=>{ return { title:item.name, pos:item.type.split(';')[0], price:item.biz_ext.cost||'暂无', img:item.photos[0].url, url:'//abc.com' } }) // 截取9条数据,通过kind属性获取对应的数据 self.list[self.kind]=r.slice(0,9) }else{ self.list[self.kind]=[] } } } }, } </script> <style lang="scss"> @import "@/assets/css/index/artistic.scss"; </style>