Vue項目開發
環境:
1、安裝 node.js、git等
2、創建倉庫
碼雲網站或者GitHub創建一個倉庫
SSH公鑰
git bush中: 將單引號部分替換爲郵箱(最好爲碼雲註冊郵箱)
ssh-keygen -t rsa -C "[email protected]"
運行下面代碼:出現公鑰
cat ~/.ssh/id_rsa.pub
複製公鑰到碼雲
克隆項目:
複製項目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 travel
,npm 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:
項目碼雲:
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"></div>
</div>
<div class="header-input">
<span class="iconfont"></span>
輸入城市/景點/遊玩主題
</div>
<router-link to='/city'>
<div class="header-right">
{{this.city}}
<span class="iconfont arrow"></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 應用的核心就是 store(倉庫)。“store”基本上就是一個容器,它包含着你的應用中大部分的狀態 (state)。Vuex 和單純的全局對象有以下兩點不同:
- Vuex 的狀態存儲是響應式的。當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那麼相應的組件也會相應地得到高效更新。
- 你不能直接改變 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()
}
}