一、需求问题:
在vue的项目开发中,我们可能会经常遇到这样的需求,实现多个复杂的页面之间通信,传统的父子、子父、非父子组件通信可能是满足不了,这个时候可以用到Vuex状态管理器。但是Vuex在页面刷新以后,状态会丢失,所以可以用到本地存储技术localStorage
去解决。这里以点击选择城市列表的城市后,所有页面的城市都会跟着变换,在刷新页面后,城市信息保存,不会发生丢失。
二、需求分析:
localStorage
,存储当前的城市列表信息。由于后端返回的城市列表信息是数组,而localStorage
不支持存储数组,但是可以存储字符串,可以用到JSON.stringify()
。在请求接口以后通过localStorage
的setItem()
方法和JSON.stringify()
就可以存储城市信息了。在mounted()
一开始进行加载的时候,就可以通过localStorage
的getItem()
方法从本地存储中进行取数据。当城市数据存在后,再通过JSON.parse()
方法可以将存储的数组转换为对象。如果不存在,那么重新发起数据请求。mounted() {
// 从本地存储中去取数据
var cityList = window.localStorage.getItem('cityList');
var hotList = window.localStorage.getItem('hotList');
// 刷新后,当cityList和 hotList 都存在的时候,直接用本地的,否则发起新的请求
if ( cityList && hotList ) {
// JSON.parse 将对象再转换回来为数组
this.cityList = JSON.parse(cityList);
this.hotList = JSON.parse(hotList);
this.isLoading = false;
} else {
this.axios.get('/api/cityList').then((res) => {
// console.log(res);
// 数据格式: [ { index: 'A', list: [{ nm: '北京', id: 1}]}]
var msg = res.data.msg;
if( msg === 'ok') {
this.isLoading = false;
var cities = res.data.data.cities;
var {cityList, hotList} = this.formatCityList(cities);
this.cityList = cityList;
this.hotList = hotList;
// 数据请求完后,将城市数据存储到本地存储localStorage
// localStorage 只能存对象,需要用到JSON.stringify() 将数组转换为对象
window.localStorage.setItem('cityList', JSON.stringify(cityList));
window.localStorage.setItem('hotList', JSON.stringify(hotList));
}
});
}
},
Vuex
的状态管理中,状态可以抽离modules
模块化,在store
文件夹中可以建立一个city
的文件夹,建立index.js
文件,在最外面的store
下的index.js
中通过import
进行引入。在city
中,在state
中存储数据源nm和id,在mutations
中写 CITY_INFO()
方法,传入state
和payload
的值,将传递过来的payload
的nm和id值赋值给state
数据源中的nm和id值。代码如下:
index.js
import Vue from 'vue'
import Vuex from 'vuex'
import city from './city'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
modules: {
city
}
})
city
文件夹下的index.js
const state = {
nm: window.localStorage.getItem('nowNm') || '北京',
id: window.localStorage.getItem('nowId') || 1
};
const actions = {
};
const mutations = {
CITY_INFO (state, payload) {
state.nm = payload.nm;
state.id = payload.id;
}
};
export default {
namespaced: true,
state,
actions,
mutations
};
@tap="handleToCity( item.nm, item.id)
,那么在methods
中写handleToCity
这个方法,通过this.$store.commit
去提交mutations
中的CITY_INFO
这个方法去传递nm和id的值。当获得改变后的值,就可以通过localStorage.setItem
去存储最新的城市信息,那么在state中一开始会从本地获取最新的城市信息,如果没有那么就选择默认的城市信息。通过this.$router.push
可以实现点击后跳转到相应的路由。代码如下:
handleToCity( nm, id) {
this.$store.commit('city/CITY_INFO', { nm, id});
window.localStorage.setItem('nowNm', nm);
window.localStorage.setItem('nowId', id);
this.$router.push('/movie/nowPlaying');
}
三、需求实现:
完整代码如下:
city.vue
<template>
<div class="city_body">
<div class="city_list">
<Loading v-if="isLoading"/>
<Scroller v-else ref="city_list">
<!-- better-scroll 的父元素只能有一个,不能够有多个 -->
<div>
<div class="city_hot">
<h2>热门城市</h2>
<ul class="clearfix">
<li v-for="item in hotList" :key="item.id" @tap="handleToCity( item.nm, item.id)">{{ item.nm }}</li>
</ul>
</div>
<div class="city_sort" ref="city_sort">
<div v-for="item in cityList" :key="item.index">
<h2>{{ item.index }}</h2>
<ul>
<li v-for="itemList in item.list" :key="itemList.id" @tap="handleToCity( itemList.nm, itemList.id)">{{ itemList.nm }}</li>
</ul>
</div>
</div>
</div>
</Scroller>
</div>
<div class="city_index">
<ul>
<li v-for="(item, index) in cityList" :key="item.index" @touchstart="handleToIndex(index)">{{ item.index }}</li>
</ul>
</div>
</div>
</template>
<script>
// import func from '../../../vue-temp/vue-editor-bridge';
// import func from './vue-temp/vue-editor-bridge';
export default {
name: 'City',
data() {
return {
cityList: [],
hotList: [],
isLoading: true
}
},
mounted() {
// 从本地存储中去取数据
var cityList = window.localStorage.getItem('cityList');
var hotList = window.localStorage.getItem('hotList');
// 刷新后,当cityList和 hotList 都存在的时候,直接用本地的,否则发起新的请求
if ( cityList && hotList ) {
// JSON.parse 将对象再转换回来为数组
this.cityList = JSON.parse(cityList);
this.hotList = JSON.parse(hotList);
this.isLoading = false;
} else {
this.axios.get('/api/cityList').then((res) => {
// console.log(res);
// 数据格式: [ { index: 'A', list: [{ nm: '北京', id: 1}]}]
var msg = res.data.msg;
if( msg === 'ok') {
this.isLoading = false;
var cities = res.data.data.cities;
var {cityList, hotList} = this.formatCityList(cities);
this.cityList = cityList;
this.hotList = hotList;
// 数据请求玩后,将城市数据存储到本地存储localStorage
// localStorage 只能存对象,需要用到JSON.stringify() 将数组转换为对象
window.localStorage.setItem('cityList', JSON.stringify(cityList));
window.localStorage.setItem('hotList', JSON.stringify(hotList));
}
});
}
},
methods: {
formatCityList(cities) {
var cityList = [];
var hotList = [];
for(var i=0;i<cities.length; i++) {
if(cities[i].isHot === 1){
hotList.push( cities[i]);
}
}
for(var i=0; i<cities.length; i++) {
var firstLetter = cities[i].py.substring(0,1).toUpperCase();
if(toCom(firstLetter)) { // 新添加到索引中
cityList.push({index: firstLetter, list: [{ nm: cities[i].nm, id: cities[i].id}]});
}else { // 累计到已有的索引
for(var j=0; j<cityList.length; j++){
if(cityList[j].index === firstLetter) {
cityList[j].list.push( { nm: cities[i].nm, id: cities[i].id} );
}
}
}
}
// 城市数据索引index的排序
cityList.sort((n1,n2) => {
if(n1.index > n2.index) {
return 1;
}
else if (n1.index < n2.index) {
return -1;
} else {
return 0;
}
})
// 判断index是否存在于cityList中
function toCom(firstLetter) {
for(var i=0;i<cityList.length;i++){
if(cityList[i].index === firstLetter) {
return false;
}
}
return true;
}
// console.log(cityList);
// console.log(hotList);
return {
cityList,
hotList
}
},
// 点击右侧索引,左侧城市列表也滚动到指定的位置
handleToIndex(index) {
var h2 = this.$refs.city_sort.getElementsByTagName('h2');
// this.$refs.city_sort.parentNode.scrollTop = h2[index].offsetTop;
this.$refs.city_list.toScrollTop( -h2[index].offsetTop);
},
// 切换城市的方法
handleToCity( nm, id) {
this.$store.commit('city/CITY_INFO', { nm, id});
window.localStorage.setItem('nowNm', nm);
window.localStorage.setItem('nowId', id);
this.$router.push('/movie/nowPlaying');
}
}
}
</script>
<style scoped>
#content .city_body{ margin-top: 45px; display: flex; width:100%; position: absolute; top: 0; bottom: 0;}
.city_body .city_list{ flex:1; overflow: auto; background: #FFF5F0;}
.city_body .city_list::-webkit-scrollbar{
background-color:transparent;
width:0;
}
.city_body .city_hot{ margin-top: 20px;}
.city_body .city_hot h2{ padding-left: 15px; line-height: 30px; font-size: 14px; background:#F0F0F0; font-weight: normal;}
.city_body .city_hot ul li{ float: left; background: #fff; width: 29%; height: 33px; margin-top: 15px; margin-left: 3%; padding: 0 4px; border: 1px solid #e6e6e6; border-radius: 3px; line-height: 33px; text-align: center; box-sizing: border-box;}
.city_body .city_sort div{ margin-top: 20px;}
.city_body .city_sort h2{ padding-left: 15px; line-height: 30px; font-size: 14px; background:#F0F0F0; font-weight: normal;}
.city_body .city_sort ul{ padding-left: 10px; margin-top: 10px;}
.city_body .city_sort ul li{ line-height: 30px; line-height: 30px;}
.city_body .city_index{ width:20px; display: flex; flex-direction:column; justify-content:center; text-align: center; border-left:1px #e6e6e6 solid;}
</style>
store
下的index.js
import Vue from 'vue'
import Vuex from 'vuex'
import city from './city'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
},
modules: {
city
}
})
city
下的index.js
const state = {
nm: window.localStorage.getItem('nowNm') || '北京',
id: window.localStorage.getItem('nowId') || 1
};
const actions = {
};
const mutations = {
CITY_INFO (state, payload) {
state.nm = payload.nm;
state.id = payload.id;
}
};
export default {
// namespaced 开启命名视图
namespaced: true,·
state,
actions,
mutations
};