vue通过Vuex和localStorage实现本地存储和状态管理,页面刷新后状态保持不变

一、需求问题:
在vue的项目开发中,我们可能会经常遇到这样的需求,实现多个复杂的页面之间通信,传统的父子、子父、非父子组件通信可能是满足不了,这个时候可以用到Vuex状态管理器。但是Vuex在页面刷新以后,状态会丢失,所以可以用到本地存储技术localStorage去解决。这里以点击选择城市列表的城市后,所有页面的城市都会跟着变换,在刷新页面后,城市信息保存,不会发生丢失。

二、需求分析:

  1. 在选择城市列表的页面中,需要用到localStorage,存储当前的城市列表信息。由于后端返回的城市列表信息是数组,而localStorage不支持存储数组,但是可以存储字符串,可以用到JSON.stringify()。在请求接口以后通过localStoragesetItem()方法和JSON.stringify()就可以存储城市信息了。在mounted()一开始进行加载的时候,就可以通过localStoragegetItem()方法从本地存储中进行取数据。当城市数据存在后,再通过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));
                }
            });
        }
        
    },
  1. Vuex的状态管理中,状态可以抽离modules模块化,在store文件夹中可以建立一个city的文件夹,建立index.js文件,在最外面的store下的index.js中通过import进行引入。在city中,在state中存储数据源nm和id,在mutations中写 CITY_INFO() 方法,传入statepayload的值,将传递过来的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
};
  1. 在选择城市列表的每一个城市可以添加一个事件,当点击后就会传递值,@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');
        }

三、需求实现:

完整代码如下:

  1. 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>
  1. 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
  }
})
  1. 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
};

你可能感兴趣的:(Vue,vue,Vuex,localStorage,本地存储和状态管理,页面刷新状态不丢失)