Vue+webpack+axios項目實戰

Vue項目開發

環境:

1、安裝 node.js、git等

2、創建倉庫

碼雲網站或者GitHub創建一個倉庫

在這裏插入圖片描述

SSH公鑰

git bush中: 將單引號部分替換爲郵箱(最好爲碼雲註冊郵箱)

ssh-keygen -t rsa -C "[email protected]"

運行下面代碼:出現公鑰

cat ~/.ssh/id_rsa.pub

SSH生成

複製公鑰到碼雲

在這裏插入圖片描述

克隆項目:

複製項目SSH

在這裏插入圖片描述

進入你想把項目要放入文件目錄,打開git bush,運行git clone [email protected]:xxxx.git (git clone加上覆制的SSH) ,即目錄位置就多出來了一個travel-test(碼雲倉庫名)文件夾

在這裏插入圖片描述

3、初始化本地倉庫

運行 git install --global vue-cli 安裝

打開項目所在目錄(注意一定要在項目所在文件夾而不是項目文件夾,在travel-test的外層目錄不是travel目錄下)

一下windows環境最好在cmd運行

運行 vue init webpack travel-test travel-test爲項目文件名

根據提示(可在git bush或者cmd)

cd travelnpm run dev或者npm run start

項目已在8080端口跑起來

4、本地和線上項目同步

查看項目發現本地項目比碼雲線上項目多出許多文件夾,同步線上線下:

關掉服務器,確保git bush在項目目錄下(travel-test下)

1、運行 git status 查看狀態

2、運行 git add . 將所有文件增加到本地緩衝區

3、運行 git commit -m 'project init' 提交項目

4、運行 git push 將提交代碼推到線上倉庫

開發開始

文件引入

項目index.hteml下

<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">

在src下新建assets目錄,再在其目錄下新建styles目錄,將reset.css和normalize.css放入styles文件夾下,打開build目錄下的webpack.base.conf.js裏,配置style目錄簡寫方式使(src/assets/styles)可簡寫爲styles目錄

加入'styles': resolve('src/assets/styles')

resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      'vue$': 'vue/dist/vue.esm.js',
      '@': resolve('src'),
      'styles': resolve('src/assets/styles'),
      'common': resolve('src/common'),
    }
  },

入口文件mian.js導入reset.css或者normalize.css,重置瀏覽器默認樣式

import 'styles/reset.css'

入口文件mian.js導入border.css解決移動端1px邊框問題

import 'styles/border.css'

安裝fastclick ,解決在移動端在某些設備和瀏覽器上click事件會延遲300ms執行,體驗不太好

npm install fastclick --save

入口文件mian.js導入

import fastClick from 'fastclick'

fastClick.attach(document.body)

導入iconfont

import 'styles/iconfont.css'

安裝stylus,stylus-loader

npm install stylus --save
npm install stylus-loader --save

在這裏插入圖片描述

初始化完成提交項目

1、運行 git status 查看狀態

2、運行 git add . 將所有文件增加到本地緩衝區

3、運行 git commit -m 'project init' 提交項目

4、運行 git push 將提交代碼推送到線上倉庫

開發開始

參考app:

http://piao.qunar.com/touch/

項目碼雲:

https://gitee.com/GueYue/events

home頁面編寫

1、header部分
開始

home頁面路由配置

在這裏插入圖片描述
src下新建pages文件夾,新建home文件夾,home文件夾下,新建Home.vue,並寫下如下代碼

<template>
  <div>
	Home
  </div>
</template>
<script>
export default {
  name: 'Home'
</script>

<style>
</style>

router下的index.js中

導入模板

import Home from '@/pages/home/Home'
routes: [
    {
      path: '/',
      name: 'Home',
      component: Home
    }]
內容

第一個功能的開發可以就在master分支開發,不用修改git分支

在home文件夾下新建components文件夾,components下新建Header.vue 文件,寫下

<template>
  <div class="header">
    <div class="header-left">
      <div class="iconfont back-icon">&#xe624;</div>
    </div>
    <div class="header-input">
      <span class="iconfont">&#xe632;</span>
      輸入城市/景點/遊玩主題
    </div>
    <router-link to='/city'>
      <div class="header-right">
        {{this.city}}
        <span class="iconfont arrow">&#xe6aa;</span>
      </div>
    </router-link>
  </div>
</template>

<script>
export default {
  name: 'HomeHeader',
}
</script>

<style lang="stylus" scoped>
  @import '~styles/varibles.styl'
  .header
    line-height: $headerHeight
    display: flex
    background-color: $bgColor
    /* 具體樣式略 */
	......
</style>

修改Home.vue使用Header組件

<template>
  <div>
    <home-header></home-header>
  </div>
</template>
<script>
import HomeHeader from './components/Header'
export default {
  name: 'Home',
  components: {
    HomeHeader
  }
</script>

<style>
</style>

2、輪播圖
開始

在碼雲上創建分支home-swiper

項目git bush裏運行如下代碼

1、git pull 線上分支同步到線下

2、git checkout home-swiper 切換到home-siwper分支開發

3、npm run start 或者 npm run dev 啓動項目

安裝vue-awesome-swiper 2.6.7版本

[email protected] --save

mian.js導入

import VueAwesomeSwiper from 'vue-awesome-swiper'
import 'swiper/dist/css/swiper.css'

Vue.use(VueAwesomeSwiper)
內容

home目錄下的components下新建Swier.vue 多餘代碼併爲刪除

<template>
  <div class="swiper">
    <swiper :options="swiperOption" v-if="showSiwper">
      <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>
<script>
export default {
  name: 'HomeSwiper',
  props: {
    list: Array
  },
  data () {
    return {
      swiperOption: {
        pagination: '.swiper-pagination',
        loop: true
      }
    }
  },
  computed: {
    showSiwper () {
      return this.list.length
    }
  }
}
</script>
<style lang="stylus" scoped>
.swiper >>> .swiper-pagination-bullet-active
  background: #fff
.swiper
  overflow: hidden
  width: 100%
  height: 0
  padding-bottom: 26.66%
  .swiper-img
    width: 100%
</style>

Home.vue

<template>
  <div>
    <home-header></home-header>
    <home-swiper :list="swiperList"></home-swiper>
    <home-icons :list="iconList"></home-icons>
    <home-recommend :list="recommendList"></home-recommend>
    <home-weekend :list="weekendList"></home-weekend>
  </div>
</template>
<script>
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcons from './components/Icons'
import HomeRecommend from './components/Recommend'
import HomeWeekend from './components/Weekend'
import axios from 'axios'
import { mapState } from 'vuex'

export default {
  name: 'Home',
  components: {
    HomeHeader,
    HomeSwiper,
    HomeIcons,
    HomeRecommend,
    HomeWeekend
  },
  data () {
    return {
      swiperList: [],
      iconList: [],
      recommendList: [],
      weekendList: [],
      lastCity: ''
    }
  },
  computed: {
    ...mapState(['city'])
  },
  methods: {
    getHomeInfo () {
      axios.get('api/index.json?city=' + this.city)
        .then(this.getHomeInfoSucc)
    },
    getHomeInfoSucc (res) {
      res = res.data
      if (res.ret && res.data) {
        const data = res.data
        this.swiperList = data.swiperList
        this.iconList = data.iconList
        this.recommendList = data.recommendList
        this.weekendList = data.weekendList
      }
    }
  },
  mounted () {
    this.lastCity = this.city
    this.getHomeInfo()
  },
  activated () {
    if (this.lastCity !== this.city) {
      this.lastCity = this.city
      this.getHomeInfo()
    }
  }
}
</script>

<style>
</style>

結束(提交和合並分支)

提交代碼

1、運行 git status 查看狀態

2、運行 git add . 將所有文件增加到本地緩衝區

3、運行 git commit -m 'XXXXX' 提交項目

4、運行 git push 將提交代碼推送到線上倉庫

合併分支

1、運行 git checkout master 切換到主分支

2、運行 git merge home-swiper 合併wiper分支到主分支

3、運行 git push 將提交代碼推送到線上倉庫

Ajax傳值

安裝axios

npm install axios --save

更改你配置項

在config目錄下的index.js找到dev(開發環境)配置proxyTbale

當我們請求api目錄的時候把請求轉發到http://localhost:8080 上,並把以api開頭的請求,則把路徑替換到本地的/static/mock文件夾下

module.exports = {
  dev: {

    // Paths
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    proxyTable: {
      '/api': {
        target: 'http://localhost:8080',
        pathRewrite: {
          '^/api':'/static/mock'
        }
      }
    },

請求數據

接收到數據後通過組件傳值把數據分發到各個模塊

請求的是static/mock/index.json

<template>
  <div>
    <home-header></home-header>
    <home-swiper :list="swiperList"></home-swiper>
    <home-icons :list="iconList"></home-icons>
    <home-recommend :list="recommendList"></home-recommend>
    <home-weekend :list="weekendList"></home-weekend>
  </div>
</template>
<script>
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcons from './components/Icons'
import HomeRecommend from './components/Recommend'
import HomeWeekend from './components/Weekend'
import axios from 'axios'
import { mapState } from 'vuex'

export default {
  name: 'Home',
  components: {
    HomeHeader,
    HomeSwiper,
    HomeIcons,
    HomeRecommend,
    HomeWeekend
  },
  data () {
    return {
      swiperList: [],
      iconList: [],
      recommendList: [],
      weekendList: [],
      lastCity: ''
    }
  },
  computed: {
    ...mapState(['city'])
  },
  methods: {
    getHomeInfo () {
      axios.get('api/index.json?city=' + this.city)
        .then(this.getHomeInfoSucc)
    },
    getHomeInfoSucc (res) {
      res = res.data
      if (res.ret && res.data) {
        const data = res.data
        this.swiperList = data.swiperList
        this.iconList = data.iconList
        this.recommendList = data.recommendList
        this.weekendList = data.weekendList
      }
    }
  },
  mounted () {
    this.lastCity = this.city
    this.getHomeInfo()
  },
  activated () {
    if (this.lastCity !== this.city) {
      this.lastCity = this.city
      this.getHomeInfo()
    }
  }
}
</script>

<style>
</style>

better-scroll插件的使用

GitHub地址:

https://github.com/ustbhuangyi/better-scroll

安裝

npm install better-scroll --save

使用

<template>
  <div class="list" ref="wrapper">
    <div>
      <div class="area">
        <div class="title">當前城市</div>
        <div class="button-list">
          <div class="button-wrapper">
            <div class="button">{{this.currentCity}}</div>
          </div>
        </div>
      </div>
      <div class="area">
        <div class="title">熱門城市</div>
        <div class="button-list">
          <div class="button-wrapper" v-for="item of hot" :key="item.id" @click="handleCityClick(item.name)">
            <div class="button">{{item.name}}</div>
          </div>
        </div>
      </div>
      <div class="area" v-for="(item, key) of cities" :key="key" :ref="key">
        <div class="title">{{key}}</div>
        <div class="item-list">
          <div class="item border-bottom" v-for="innerItem of item" :key="innerItem.id" @click="handleCityClick(innerItem.name)">{{innerItem.name}}</div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import Bscroll from 'better-scroll'
import { mapState, mapMutations } from 'vuex'
export default {
  name: 'CityList',
  props: {
    hot: Array,
    cities: Object,
    letter: String
  },
  mounted () {
    this.scroll = new Bscroll(this.$refs.wrapper)
  },
  computed: {
    ...mapState({
      currentCity: 'city'
    })
  },
  watch: {
    letter () {
      if (this.letter) {
        const element = this.$refs[this.letter][0]
        this.scroll.scrollToElement(element)
      }
    }
  },
  methods: {
    handleCityClick (city) {
      // this.$store.commit('changeCity', city)
      this.changeCity(city)
      this.$router.push('/')
    },
    ...mapMutations(['changeCity'])
  }
}
</script>

<style lang="stylus" scoped>
@import '~styles/varibles.styl'
@import '~styles/mixins.styl'
.list
  position: absolute
  .title
    title-common()
    /* 省略css*/
  ......
</style>

靈活的timer實現數據節流提高性能

<template>
  <div>
    <div class="search">
      <input type="text" class="search-input" placeholder="輸入城市名或拼音" v-model="keyword">
    </div>
    <div class="search-content" ref="search" v-show="keyword">
      <ul>
        <li class="search-item" v-for="item of list" :key="item.id" @click="handleCityClick(item.name)">{{item.name}}</li>
        <li class="search-item" v-show="hasNoData">沒有找到匹配數據</li>
      </ul>
    </div>
  </div>
</template>

<script>
import Bscroll from 'better-scroll'
import { mapMutations } from 'vuex'
export default {
  name: 'CitySearch',
  props: {
    cities: Object
  },
  data () {
    return {
      keyword: '',
      list: [],
      timer: null
    }
  },
  computed: {
    hasNoData () {
      return !this.list.length
    }
  },
  watch: {
    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)
    }
  },
  mounted () {
    this.scroll = new Bscroll(this.$refs.search)
  },
  methods: {
    handleCityClick (city) {
      // this.$store.commit('changeCity', city)
      this.changeCity(city)
      this.$router.push('/')
    },
    ...mapMutations(['changeCity'])
  }
}
</script>

<style lang="stylus" scoped>
  ......
</style>

Vuex實現數據渲染

vuex

每一個 Vuex 應用的核心就是 store(倉庫)。“store”基本上就是一個容器,它包含着你的應用中大部分的狀態 (state)。Vuex 和單純的全局對象有以下兩點不同:

  1. Vuex 的狀態存儲是響應式的。當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那麼相應的組件也會相應地得到高效更新。
  2. 你不能直接改變 store 中的狀態。改變 store 中的狀態的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個狀態的變化,從而讓我們能夠實現一些工具幫助我們更好地瞭解我們的應用。

簡單的使用:

src下創建store文件夾在store文件夾下創建index.js

導出的是通過Vuex創建的store倉庫,並設置倉庫中state裏設置數據city默認值爲北京

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
export default new Vuex.Store({
  state:{
  	city: '北京'
  }
})

main.js中

import store from './store'

new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})

在這裏插入圖片描述

因爲在創建根實例的時候把store傳入了

{{this.$store.state.city}}

修改city

<div class="item border-bottom" @click="handleCityClick(innerItem.name)">{{innerItem.name}}</div>
methods: {
    handleCityClick (city) {
    	this.$store.dispatch('change', city)
    },
  }

修改store下index.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
export default new Vuex.Store({
  state: {
  	city: '北京'
  },
  actions: {
  	changeCity (ctx,city) {
  		ctx.commit('changeCity', city)
  	}
  },
  mutations: {
  	changeCity (state, city){
  		state.city = city
  	} 
  }
})

無異步操作簡化

若無異步操作,則Actions並無必要,組件可以直接調用mutations

即上述代碼簡單改爲:

methods: {
    handleCityClick (city) {
    	this.$store.dispatch('change', city)
    },
  }
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
export default new Vuex.Store({
  state: {
  	city: '北京'
  },
  mutations: {
  	changeCity (state, city){
  		state.city = city
  	} 
  }
})

增加緩存操作(類似cookie)

使用localStorage最好添加try…catch 因爲用戶可能會關閉本地存儲或者爲隱身模式可能會導致拋出異常

export default new Vuex.Store({
  state: {
  	city: localStorage || '北京'
  },
  mutations: {
  	changeCity (state, city){
  		state.city = city
  		localStorage.city = city
  	} 
  }
})

改:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

let defaultCity = '上海'
try {
  if (localStorage.city) {
    defaultCity = localStorage.city
  }
} catch (e) {}

export default new Vuex.Store({
  state: {
  	city: defaultCity
  },
  mutations: {
  	changeCity (state, city) {
    state.city = city
    try {
      localStorage.city = city
    } catch (e) {}
  }
  }
})

拆分

當項目有些大時,index.js太雜亂,不易於維護,所以需要拆分

在store中創建state.js文件

let defaultCity = '上海'
try {
  if (localStorage.city) {
    defaultCity = localStorage.city
  }
} catch (e) {}

export default {
  city: defaultCity
}

在store中創建mutations.js文件

export default {
  changeCity (state, city) {
    state.city = city
    try {
      localStorage.city = city
    } catch (e) {}
  }
}

store中index.js修改

import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'

Vue.use(Vuex)

export default new Vuex.Store({
  state,
  mutations
})

store使用代碼的優化

import { mapState } from 'vuex'
  computed: {
    ...mapState(['city'])
  },

import { mapState, mapMutations } from 'vuex'
<div class="button">{{this.currentCity}}</div>

將Vuex中公用數據映射到這個組件的計算屬性裏,映射過來的名字叫做currentCity

computed: {
    ...mapState({
      currentCity: 'city'
    })
  },

有一個mutation叫changeCity,然後我把這個mutation映射到這個組件的一個叫做changeCity的方法裏

methods: {
    handleCityClick (city) {
      // this.$store.commit('changeCity', city)
      this.changeCity(city)
      this.$router.push('/')
    },
    ...mapMutations(['changeCity'])
  }

最後代碼

倉庫代碼

store文件夾下index.js中

import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'

Vue.use(Vuex)

export default new Vuex.Store({
  state,
  mutations
})

store文件夾下state.js中

let defaultCity = '上海'
try {
  if (localStorage.city) {
    defaultCity = localStorage.city
  }
} catch (e) {}

export default {
  city: defaultCity
}

store文件夾下mutations.js中

export default {
  changeCity (state, city) {
    state.city = city
    try {
      localStorage.city = city
    } catch (e) {}
  }
}
使用代碼
<div class="button">{{this.currentCity}}</div>
computed: {
    ...mapState({
      currentCity: 'city'
    })
  },
methods: {
    handleCityClick (city) {
      // this.$store.commit('changeCity', city)
      this.changeCity(city)
      this.$router.push('/')
    },
    ...mapMutations(['changeCity'])
  }

總結

若用到非常複雜的應用系統,比如後臺管理等、經常會有很多共用數據在Vuex中進行存儲那麼我們需要用到模塊,詳細學習到Vuex下的Module文檔

keepalive性能優化

加keepalive

  methods: {
    getCityInfo () {
      axios.get('api/city.json')
        .then(this.handleGetCityInfoSucc)
    },
    handleGetCityInfoSucc (res) {
      res = res.data
      if (res.ret && res.data) {
        const data = res.data
        this.cities = data.cities
        this.hotCities = data.hotCities
      }
    },
    handleLetterChange (letter) {
      this.letter = letter
    }
  },
  mounted () {
    this.getCityInfo()
  }

由上述代碼可知,當每次加載頁面即這個組建時,就會發送一次請求,若頻繁的打開而不更改頁面,那麼性能將會很低,所以我麼可以在程序的入口組件裏使用keepalive

他的意思是當路由的內容被加載過一次之後,他就將路由的內容放到內存之中,下一次在進這個路由的時候不需要你去重新渲染這個組件,重新執行鉤子函數,只需要從內存裏把以前的內容拿出來就可

<keep-alive>
      <router-view></router-view>
</keep-alive>

在這裏插入圖片描述

解決需要重新加載頁面內容時的問題

有時候我們在這個頁面點擊、做出其他改變影響頁面內容時需要重新請求數據重新渲染內容,那麼我們就需要使用到加上keepalive後組件裏會多出兩個個生命週期函數activated,unactivated,他在頁面重新渲染的時候會執行,我們只需要在activated裏判斷數據是否需要重新請求,若需要則重新請求數據即可

 activated () {
    if (this.lastCity !== this.city) {
      this.lastCity = this.city
      this.getHomeInfo()
    }
 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章