Vue項(xiàng)目總結(jié)

項(xiàng)目簡(jiǎn)介

去哪兒的webapp版,實(shí)現(xiàn)其中的home界面,城市選擇頁(yè)面,詳情頁(yè)部分邏輯和界面

代碼結(jié)構(gòu)

image.png
  • build與config
    使用vue-cli腳手架工具以及webpack搭建好的開(kāi)發(fā)環(huán)境,build,config等目錄中包含相關(guān)配置文件,具體待研究,但修改其中的代碼后,本地服務(wù)器都要重新啟動(dòng)部分配置才能生route效。
  • node_modules
    用npm工具導(dǎo)入的部分插件和工具
  • src
    1.assets,存放項(xiàng)目的靜態(tài)資源,如圖標(biāo),圖標(biāo)css,以及常用的常量。
    2.common,放的是公共的組件,意為可能被各個(gè)頁(yè)面引用的組件,包括兩個(gè)組件,fade組件(封裝著transition動(dòng)畫(huà)的樣式),gallay(公共畫(huà)廊組件:是一個(gè)輪播畫(huà)廊)
    3.pages,最核心的代碼放置處(再后面會(huì)詳細(xì)介紹,分別對(duì)應(yīng)三個(gè)頁(yè)面的)


    image.png

    4.router,放有路由配置文件。
    5.store,放有vuex相關(guān)配置和代碼

  • static
    此文件夾放有整個(gè)項(xiàng)目的json數(shù)據(jù)文件,因?yàn)闆](méi)有后端服務(wù)器,所以請(qǐng)求的是本地的數(shù)據(jù)。(此處為了請(qǐng)求的路徑定位,還修改了config->indexks文件的內(nèi)容)

核心代碼實(shí)現(xiàn)

  • router:
    router的作用就是路由,讓url對(duì)應(yīng)的path和應(yīng)該調(diào)用的組件對(duì)應(yīng)起來(lái)。其中詳情頁(yè)的頁(yè)面比較特殊的,因?yàn)閷?duì)于每個(gè)ID,有自己的詳情頁(yè)。
export default new Router({
  routes: [{
    path: '/',
    name: 'Home',
    component: Home
  }, {
    path: '/city',
    name: 'City',
    component: City
  }, {
    path: '/detail/:id',
    name: 'Detail',
    component: Detail
  }],
  scrollBehavior (to, from, savedPosition) {
    return { x: 0, y: 0 }
  }
})

  • main
    main是根組件所在地,我們看下它的結(jié)構(gòu),它引入了路由,fastClick是針對(duì)部分手機(jī)兼容的,幾個(gè)CSS是一些基礎(chǔ)樣式,邊框樣式,圖標(biāo)樣式,還引入了我們的awesomeSwiper,store。
    在根組件傳入,路由,和store
import Vue from 'vue'
import App from './App'
import router from './router'
import fastClick from 'fastclick'
import 'styles/reset.css'
import 'styles/border.css'
import 'styles/iconfont.css'
import VueAwesomeSwiper from 'vue-awesome-swiper'
import store from './store'
import 'swiper/dist/css/swiper.css'

Vue.config.productionTip = false
fastClick.attach(document.body)
Vue.use(VueAwesomeSwiper)
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})
  • Home
  1. Home.vue
    主頁(yè)有個(gè)城市顯示在右上位置。這個(gè)東西我們使用了vuex技術(shù)存儲(chǔ)在了store里。
    通過(guò)這句可以取到store里的city值。
    這里首先使用了mapstate這個(gè)輔助函數(shù),他可以把多個(gè)state狀態(tài)轉(zhuǎn)化成該子組件的計(jì)算屬性。使用...對(duì)象展開(kāi)運(yùn)算符還可以把這個(gè)對(duì)象拆分成一個(gè)個(gè)屬性。
...mapState(['city'])

這個(gè)頁(yè)面主要使用ajax發(fā)送了一些請(qǐng)求,
其中在mounted也就是第一次掛載的時(shí)候,請(qǐng)求一次,且更換城市。
用一個(gè)lastCity保存一下上一次的城市,再一次切換到此城市的時(shí)候,有可能vuex里面的city已經(jīng)改了,比如在城市選擇頁(yè)修改了,那么我們就要重新發(fā)ajax請(qǐng)求,請(qǐng)求這個(gè)city特應(yīng)的主頁(yè)。

  1. header
    這里也會(huì)取到vuex的city值,并放在header顯示。
    這里有個(gè)小細(xì)節(jié)是把代碼重復(fù)用到的變量放進(jìn)styl這個(gè)常量表里。
  @import '~styles/varibles.styl'
  .header
    display: flex
    line-height: $headerHeight
  1. swiper
    這里用到了vue-asome-swiper這個(gè)插件。
    最外層是模板
    然后是wrapper包裹塊
    然后是swiper這個(gè)標(biāo)簽,這是插件實(shí)現(xiàn)好的被你引入的。
    slide意為可滑動(dòng)的區(qū)域。
    下面還有個(gè)swiper-pagenation表示顯示幾個(gè)點(diǎn)的選項(xiàng)卡。
<template>
  <div class="wrapper">
    <swiper :options="swiperOption" v-if="list.length">
      <!-- slides -->
      <swiper-slide v-for="item of list" :key="item.id">
        <img class="swiper-img" :src="item.imgUrl" />
      </swiper-slide>
      <div class="swiper-pagination"  slot="pagination"></div>
    </swiper>
  </div>

</template>

他們需要如此配置:(里面是各個(gè)參數(shù)的作用放在一起了)

      swiperOption: {
        pagination: '.swiper-pagination',
        loop: true
        //代表不自動(dòng)滑動(dòng)
        autoplay: false
      }
      //fraction代表,用1/2的方式,默認(rèn)是...的方式。
        paginationType: 'fraction',
      //這兩個(gè)的作用是:如果不加這兩個(gè)參數(shù),重新進(jìn)入swiper的時(shí)候,寬度計(jì)算會(huì)出現(xiàn)問(wèn)題。
        observeParents: true,
        observer: true
  1. icons
    這里主要實(shí)現(xiàn)細(xì)節(jié):
    計(jì)算屬性里把a(bǔ)jax得到的數(shù)據(jù)進(jìn)行一番計(jì)算,生成pages數(shù)組,每個(gè)pages數(shù)組里放的又是一個(gè)個(gè)圖標(biāo),這樣可以達(dá)到分頁(yè)的效果了。
  computed: {
    pages () {
      const pages = []
      this.iconsList.forEach((item, index) => {
        const page = Math.floor(index / 8)
        if (!pages[page]) {
          pages[page] = []
        }
        pages[page].push(item)
      })
      return pages
    }
  }

看swipter的實(shí)現(xiàn)細(xì)節(jié),我們先在外層循環(huán)一下pages里面的頁(yè),再對(duì)每一頁(yè)的數(shù)組進(jìn)行一次維護(hù)。

    <swiper :options="swiperOption">
      <swiper-slide v-for="(page , index) of pages" :key = "index">
        <div class="icon" v-for="item of page" :key="item.id">
          <div class="icon-img">
            <img class="icon-img-content" :src='item.imgUrl'>
          </div>
          <p class="icon-desc">{{item.desc}}</p>
        </div>
      </swiper-slide>
    </swiper>

說(shuō)一下用到的CSS穿透:swiper自帶的一些class里面的屬性我們?cè)趺锤模?/p>

image.png

image.png
  1. Recommand
    這里要提到的是:
    ellipsis() 這個(gè)函數(shù)是為了讓一行不用顯示滿(mǎn),多余的不換行而是變成...
    把這個(gè)函數(shù)直接封裝在常量表,他長(zhǎng)這個(gè)樣子
ellipsis()
  overflow: hidden
  white-space: nowrap
  text-overflow: ellipsis

還有,我們可以用一個(gè)router-link來(lái)進(jìn)行跳轉(zhuǎn)操作,目的地由:to屬性指定,然后還可以指定這個(gè)標(biāo)簽的類(lèi)型,用tag就好

      <router-link
        :to="'/detail/'+item.id"
        tag="li"
        class="item border-bottom"
        v-for="item of recommandList"
        :key="item.id"
      >

結(jié)構(gòu): ul>router-link>img+(div>title+desc+button)

  • city
  1. City.vue
    同樣要ajax請(qǐng)求,同時(shí)這個(gè)頁(yè)面有l(wèi)etter這個(gè)屬性。這個(gè)字符串會(huì)跟其子組件的某些letter進(jìn)行雙向綁定。
    接收到ajax返回的數(shù)據(jù)后,會(huì)傳給需要的子組件。
  2. header
    header沒(méi)什么特別的,就布局一下。但是也有一個(gè)router-link標(biāo)簽to向返回主頁(yè)。充當(dāng)返回鍵。
  3. List
    這個(gè)組件的html結(jié)構(gòu)是
    而且城市列表是一個(gè)嵌套循環(huán)結(jié)構(gòu)。
    list>(當(dāng)前城市+熱門(mén)城市+城市列表)
    其中當(dāng)前城市從mapstate中取得,并作為一個(gè)計(jì)算屬性。
    整個(gè)頁(yè)面是只有屏幕框大小的,overflow-hidden
    而list的滑動(dòng)效果怎么實(shí)現(xiàn)呢?
 position: absolute
    overflow: hidden
    top: 1.58rem
    left: 0
    right: 0
    bottom: 0

Bscroll傳一個(gè)元素進(jìn)去,就可以在這個(gè)元素實(shí)現(xiàn)滑動(dòng)效果,于是我們得以滑動(dòng)list上的城市。

具體操作:在html標(biāo)簽上指定ref="wrapper",然后通過(guò)this.$refs.wrapper可以得到元素的引用。

  mounted () {
    this.scroll = new Bscroll(this.$refs.wrapper)
  }

同時(shí),點(diǎn)擊事件會(huì)更改當(dāng)前城市,在熱門(mén)城市和普通城市的div都綁定了handle事件,在其中觸發(fā)changecity事件修改vuex中的事件

    handleCityClick (city) {
      // this.$store.commit('changeCity', city)
      this.changeCity(city)
      this.$router.push('/')
    },
    //這里用展開(kāi)運(yùn)算符和map輔助函數(shù)完成將mutations里面changeCity和this.changeCity綁定起來(lái)。
    ...mapMutations(['changeCity'])

還對(duì)letter這個(gè)變量進(jìn)行了偵聽(tīng)器,當(dāng)letter變化的時(shí)候。
通過(guò)refs取到各個(gè)list的子項(xiàng)元素,其是一個(gè)數(shù)組,且只有一項(xiàng),然后讓當(dāng)前的scroll滾動(dòng)到對(duì)應(yīng)位置。

      if (this.letter) {
        const element = this.$refs[this.letter][0]
        this.scroll.scrollToElement(element)
      }
  1. alphabet
    字母表的標(biāo)簽指令如下:
        <li
          class="item"
          v-for="item of letters"
          :key="item"
          @click="handleClick"
          @touchstart.prevent="handleTouchStart"
          @touchmove="handleTouchMove"
          @touchend="handleTouchEnd"
          :ref = "item"
        >

class,外層循環(huán),key,點(diǎn)擊事件,拖動(dòng)事件,ref屬性方便取到組件。

      touchStatus: false,
      startY: 0,
      timer: null

updated在頁(yè)面的data改變且頁(yè)面要重新渲染的時(shí)候就會(huì)變化。

下面這個(gè)函數(shù)會(huì)在點(diǎn)擊字母的時(shí)候向外相應(yīng)一個(gè)change函數(shù)且被其父組件city組件捕捉到,而city組件根據(jù)傳出來(lái)的參數(shù)改變其 this的letter,且這個(gè)letter又與list這個(gè)子組件的letter雙向綁定,達(dá)到兩個(gè)子組件通信的效果。

    handleClick (e) {
      this.$emit('change', e.target.innerText)
    },

下面看觸摸事件的處理:

    handleTouchStart () {
      this.touchStatus = true
    },
  handleTouchEnd () {
      this.touchStatus = false
    }

這兩個(gè)不必說(shuō),看最主要的函數(shù):

 handleTouchMove (e) {
      if (this.touchStatus) {
        if (this.timer) {
          clearTimeout(this.timer)
        }
        this.timer = setTimeout(() => {
          const touchY = e.touches[0].clientY - 79
          const index = Math.floor((touchY - this.startY) / 20)
          if (index >= 0 && index < this.letters.length) {
            this.$emit('change', this.letters[index])
          }
        }, 16)
      }
    },

這個(gè)函數(shù)首先使用了事件防抖,16ms內(nèi)只觸發(fā)一次函數(shù),然后計(jì)算一下每次觸摸大的位置,與字母表與頂端相減,然后算出你點(diǎn)了哪個(gè)元素,把這個(gè)字母通過(guò)index發(fā)送出去。

  1. search
    有一個(gè)li標(biāo)簽在最底下,它有v-show屬性,根據(jù)有沒(méi)有搜索到一些值來(lái)顯示。
    用V-model對(duì)輸入框的數(shù)據(jù)進(jìn)行了雙向綁定。然后watch了他的變化。也使用了事件防抖。遍歷整個(gè)cities數(shù)組,用indexof查找spell和name有沒(méi)有共同,有則加入結(jié)果,然后將this.list重新賦值為這個(gè)res。
    keyword () {
      if (this.timer) {
        clearTimeout(this.timer)
      }
      if (!this.keyword) {
        this.list = []
        return
      }
      this.timer = setTimeout(() => {
        const result = []
        for (let i in this.cities) {
          this.cities[i].forEach((value) => {
            if (value.spell.indexOf(this.keyword) > -1 ||
              value.name.indexOf(this.keyword) > -1) {
              result.push(value)
            }
          })
        }
        this.list = result
      }, 100)
  • Detail
  1. Detail.vue
    這里面同樣發(fā)送了ajax請(qǐng)求。注意對(duì)get參數(shù)的傳遞方式。可以這樣。其中route.params.id是從url取參數(shù)。
      axios.get('/api/detail.json?', {
        params: {
          id: this.$route.params.id
        }
      })
        .then(this.getHomeInfoSucc)
  1. banner
    banner中就調(diào)用了畫(huà)廊組件,也即是公共組件里的gallary
    <fade-animation>
      <common-gallary
        :imgs="bannerImgs"
        v-show="showGallary"
        @close="handleGallaryClose"
      ></common-gallary>
    </fade-animation>

這個(gè)組件有個(gè)show屬性,

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,641評(píng)論 19 139
  • 之前一段時(shí)間都在使用 vue 開(kāi)發(fā)后臺(tái)管理系統(tǒng),在摸索的過(guò)程中對(duì) vue 本身和模塊化、規(guī)范化開(kāi)發(fā)有了更深的認(rèn)知,...
    威少_吳閱讀 880評(píng)論 0 2
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,173評(píng)論 25 708
  • 上一篇文章http://www.itdecent.cn/p/674e75b41642介紹了項(xiàng)目里文件夾的作用分類(lèi)和...
    威少_吳閱讀 1,352評(píng)論 0 4
  • 如果是大學(xué)的我 一定會(huì)說(shuō):很遺憾 有過(guò)矛盾的好朋友確實(shí)是會(huì)越走越遠(yuǎn)的。 就拿我和B來(lái)說(shuō)。我們高中認(rèn)識(shí) 是當(dāng)時(shí)最好最...
    Milk牛奶閱讀 668評(píng)論 0 2

友情鏈接更多精彩內(nèi)容