慕课网Vue2.5去哪儿实战课程(4)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014465934/article/details/87966148

8.旅游网站城市列表页面开发

本章将讲解景点详情页面的开发,主要讲解渐隐渐显 Header 组件的制作,公用组件的拆分,路由参数的获取与处理,以及递归组件的使用。在详情页面,我们还会对通用动画效果进行代码封装。

8.1路由配置

router-link用于页面的跳转:

	<router-link to='/city'>
		<div class="header-right">
    		城市
    		<span class="iconfont arrow-icon">&#xe64a;</span>
    	</div>
	</router-link>

8.2搜索框布局

注意搜索框中box-sizing:border-box

8.3列表布局

8.4BetterScroll的使用和字母表布局

BetterScroll插件的使用:

<div ref="wrapper">
	<div></div>
</div>

<script type="text/javascript">

import Bscroll from 'better-scroll'

export default{
	name:'CityList',
	//生命周期函数,会在DOM挂载结束后执行
	mounted(){
		this.scroll = new Bscroll(this.$refs.wrapper)
	}
}	
</script>

8.5页面的动态数据渲染

主要是数组和对象循环:

//数组
"hotCities": [{
			"id": 1,
			"spell": "beijing",
			"name": "北京"
		}, {
			"id": 3,
			"spell": "shanghai",
			"name": "上海"
		}, {
			"id": 47,
			"spell": "xian",
			"name": "西安"
		}, {
			"id": 239,
			"spell": "sanya",
			"name": "三亚"
		}, {
			"id": 188,
			"spell": "lijiang",
			"name": "丽江"
		}, {
			"id": 125,
			"spell": "guilin",
			"name": "桂林"
		}]

//对象
		"cities": {
			"A": [{
				"id": 56,
				"spell": "aba",
				"name": "阿坝"
			}, {
				"id": 57,
				"spell": "akesu",
				"name": "阿克苏"
			}, {
				"id": 58,
				"spell": "alashanmeng",
				"name": "阿拉善盟"
			}],
			"B": [{
				"id": 1,
				"spell": "beijing",
				"name": "北京"
			}, {
				"id": 66,
				"spell": "baicheng",
				"name": "白城"
			}, {
				"id": 67,
				"spell": "baise",
				"name": "百色"
			}]
}

数组和对象循环:

//数组循环
<div class="area">
				<div class="title border-topbottom">
					热门城市
				</div>
				<div class="button-list">
					<div 
						class="button-wrapper" 
						v-for="item of hot"
						:key="item.id">
						<div class="button">{{item.name}}</div> 
					</div>
				</div>
</div>

//对象循环
<div class="area" 
				v-for="(item,key) of cities"
				:key="key">
				<div class="title border-topbottom">{{key}}</div>
				<div class="item-list"
					v-for="innerItem of item"
					:key="innerItem.id">
					<div class="item border-bottom">{{innerItem.name}}</div>
				</div>
</div>

在这里插入图片描述
8.6兄弟组件数据传递

兄弟组件间传递数据可以通过父组件作为中转站进行传递。

//CityAlphabet.vue
<ul class="list">
		<li  class="item" 
			v-for="(item,key) of cities"
			@click="handleLetterClick"
		>
			{{key}}
		</li>
</ul>

export default{
	props:{
		cities:Object
	},
	name:'CityAlphabet',
	methods:{
		handleLetterClick(e){
			//子组件(兄弟)之间数据传递,可以通过父组件作为中转站进行数据传递
			this.$emit('change',e.target.innerText) //向外传递事件和内容
			//console.log(e.target.innerText)
		}
	}
}

//City.vue
<CityAlphabet :cities="cities"
					  @change="handleLetterChange"></CityAlphabet>
<CityList :cities="cities"
		 		  :hot="hotCities"
		          :letter="letter"></CityList>

export default{
	name:'City',
	components:{
		CityHeader:CityHeader,
		CitySearch:CitySearch,
		CityList:CityList,
		CityAlphabet:CityAlphabet
	},
	data:function () {
		return{
			cities:{}, //ABCD对应城市
			hotCities:[], //热门城市
			letter:''
		}
	},
	methods:{
		getCityInfo(){
			axios.get('/api/city.json')
				.then(this.handleGetCityIndoSucc)
		},
		handleGetCityIndoSucc(res){
			res = res.data
			if (res.ret && res.data) {
				const data = res.data
				this.cities = data.cities
				this.hotCities = data.hotCities
			}
			console.log(res.data)
		},
		//父组件监听到子组件传递的事件之后触发函数
		//父组件通过属性的形式传递数据给子组件
		handleLetterChange(letter){
			this.letter = letter
			console.log(letter)
		}
	},
	mounted(){
		this.getCityInfo()
	}
}	

//CityList.vue
<div class="area" 
				v-for="(item,key) of cities"
				:key="key"
				:ref="key">
				<div class="title border-topbottom">{{key}}</div>
				<div class="item-list"
					v-for="innerItem of item"
					:key="innerItem.id">
					<div class="item border-bottom">{{innerItem.name}}</div>
				</div>
</div>

export default{
	name:'CityList',
	props:{
		hot:Array,
		cities:Object,
		letter:String
	},
	//生命周期函数,会在DOM挂载结束后执行
	mounted(){
		this.scroll = new Bscroll(this.$refs.wrapper)
	},
	watch:{
		letter (){
			if(this.letter){
				//通过字母获取DOM节点
				const element = this.$refs[this.letter][0]
				this.scroll.scrollToElement(element)
			}
			console.log(this.letter)
		}
	}
}

数据传递过程:
子组件CityAlphabet通过绑定点击事件@click="handleLetterClick"触发函数,子组件通过$emit向外传递事件和内容,把要传递内容放在参数中this.$emit('change',e.target.innerText),然后在父组件中,通过对对应子组件绑定事件进行监听接收数据,<CityAlphabet :cities="cities" @change="handleLetterChange"></CityAlphabet>,监听到事件然后就触发函数,在函数中把数据保存在data中,

data:function () {
		return{
			cities:{}, //ABCD对应城市
			hotCities:[], //热门城市
			letter:''
		}
},
methods:{
		//父组件监听到子组件传递的事件之后触发函数
		//父组件通过属性的形式传递数据给子组件
		handleLetterChange(letter){
			this.letter = letter
			console.log(letter)
		}
}

然后在子组件属性中设置:

<CityList :cities="cities"
		:hot="hotCities"
		 :letter="letter"></CityList>

这样在子组件中,通过props就可以接收到值了

export default{
	name:'CityList',
	props:{
		hot:Array,
		cities:Object,
		letter:String
	},
	//生命周期函数,会在DOM挂载结束后执行
	mounted(){
		this.scroll = new Bscroll(this.$refs.wrapper)
	},
	watch:{
		letter (){
			if(this.letter){
				//通过字母获取DOM节点
				const element = this.$refs[this.letter][0]
				this.scroll.scrollToElement(element)
			}
			console.log(this.letter)
		}
	}
}

别忘记父组件中data存储数据,子组件中props接收数据。

8.7列表性能优化

刷新频率过快,可以使用节流timer。

8.8搜索逻辑实现

8.9Vuex实现数据共享

在这里插入图片描述

这个状态自管理应用包含以下几个部分:
1.state,驱动应用的数据源;
2.view,以声明方式将 state 映射到视图;
3.actions,响应在 view 上的用户输入导致的状态变化。

以下是一个表示“单向数据流”理念的极简示意:

在这里插入图片描述
action响应页面操作,导致状态变化,state数据改变,然后通过view以声明的方式将state映射到视图。

代码示例:

//子组件CityList.vue
<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>
methods:{
		handleCityClick(city){
			this.$store.dispatch('changeCity',city)
		}
}

//通过Vuex创建的仓库
export default new Vuex.Store({
  state: {
  	city:'北京'
  },
  actions: {
  	//子组件通过this.$store.dispatch('changeCity',city),传递到action
  	//ctx表示上下文
  	changeCity(ctx,city){
  		//通过commit提交到mutations
  		ctx.commit('changeCity',city)
  	}
  },
  mutations: {
  	changeCity(state,city){
  		//state指公用数据
  		state.city = city
  	}
  },
});

或者我们直接通过mutations,不经过actions也可以:

methods:{
		handleCityClick(city){
			this.$store.commit('changeCity',city)
		}
}
//通过Vuex创建的仓库
export default new Vuex.Store({
  state: {
  	city:'北京'
  },
  mutations: {
  	changeCity(state,city){
  		//state指公用数据
  		state.city = city
  	}
  },
});

8.10Vuex的高级使用及localStorage

localStorage:

可以在子组件中直接使用vuex的状态:

import {mapState} from 'vuex'
export default {
	name:'HomeHeader',
	computed:{
		//把vuex里面的数据映射到该组件computed的计算属性里,city映射到mapState
		...mapState(['city'])
	}
}

	<router-link to='/city'>
		<div class="header-right">
    		{{this.city}}
    		<span class="iconfont arrow-icon">&#xe64a;</span>
    	</div>
	</router-link>

甚至关于vuex中state、action、mutations也可以在子组件中使用:

import {mapState, mapMutations} from 'vuex'

export default{
	name:'CityList',
	computed:{
		//把vuex里面的数据映射到该组件computed的计算属性里,city映射到mapState,且到该组件还可以通过对象形式另命名为currentCity
		...mapState({
			currentCity:'city'
		})
	},
	methods:{
		handleCityClick(city){
			//this.$store.dispatch('changeCity',city)
			//有了下面直接子组件引用mutations,可以直接这样写
			this.changeCity(city)
			//通过router进行跳转,跳转到首页
			this.$router.push('/')
		},
		//在子组件中直接使用mutations,
		...mapMutations(['changeCity'])
	}
}

<div class="button-list">
			<div class="button-wrapper">
				<div class="button">
					{{this.currentCity}}
				</div> 
		</div>
</div>

vuex中getter使用:

store.js中的getter类似组件中的computed一样,用于计算store.js中数据

Vue.use(Vuex);

//通过Vuex创建的仓库
export default new Vuex.Store({
  state: state,
  actions: {
  	//子组件通过this.$store.dispatch('changeCity',city),传递到action
  	//ctx表示上下文
  	changeCity(ctx,city){
  		//通过commit提交到mutations
  		ctx.commit('changeCity',city)
  	}
  },
  mutations:mutations,
  getters:{
  	doubleCity(state){
  		return state.city + ' ' + state.city
  	}
  }
});

<router-link to='/city'>
		<div class="header-right">
    		<!-- {{this.doubleCity}} -->
    		{{this.city}}
    		<span class="iconfont arrow-icon">&#xe64a;</span>
    	</div>
</router-link>

export default {
	name:'HomeHeader',
	computed:{
		//把vuex里面的数据映射到该组件computed的计算属性里,city映射到mapState
		...mapState(['city']),
		//vuex中getters映射到computed的计算属性里
		//store.js中的getter类似组件中的computed一样,用于计算store.js中数据
		...mapGetters(['doubleCity'])
	}
}

Vuex中核心概念:
1.state
数据源,存放数据用
2.action
Action 提交的是 mutation,而不是直接变更状态,要commit到mutation。
Action 可以包含任意异步操作。
3.mutation
同步对数据改变
4.getter
类似组件中的computed,对Vuex中数据进行计算
5.module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

8.11使用keep-alive优化网页性能

猜你喜欢

转载自blog.csdn.net/u014465934/article/details/87966148