使用Vuex的实现数据共享&localStorage&keep-alive


使用Vuex的实现数据共享

需求:我们点击城市页时首页的城市也能改变

City.vue和Home.vue是没有一个共用的父级组价的,这样两个页面是没有一个共用的父级组件进行中转。方法一:使用Bus总线(比较麻烦)

方法二:Vuex(数据框架)
main.js引入store

1
2
3
4
5
6
7
8
import  store  from  './store'
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})

src\store\index.js 代码:

1
2
3
4
5
6
7
8
9
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutation'
Vue.use(Vuex)
export default new Vuex.Store({
state,
mutations
})

src\store\mutation.js

1
2
3
4
5
6
7
8
export default{
changeCity(state,city){
state.city=city
try{
localStorage.city=city
}catch(e){}
}
}

src\store\state.js

1
2
3
4
5
6
7
8
9
10
let  defaultCity  ='北京'
try{
if(localStorage.city){
defaultStatus=localStorage.city
}
}catch(e){}
export default{
city: defaultCity

}

src\pages\city\components\List.vue使用部分代码其他页面修改同理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    <div class="button">{{ this.currentCity }}</div>
<div
class="item border-bottom"
v-for="innerItem of item"
:key="innerItem.id"
@click="handleCityClick(innerItem.name)"
>
{{ innerItem.name }}
</div>


<div class="button">{{ this.$store.state.city }}</div>
methods: {
handleCityClick(city) {
this.$store.commit("changeCity", city);
//页面跳转--编程式导航push
this.$router.push("/");
}
},

localStorage

刚开始在实现首页右上角城市定位显示的时候,src 目录下新建了一个 store 目录,存储了 Vuex 中的默认数据,city 直接设置成了“北京”,但是其实这样去写,是有问题的,点击城市,会改变这个 city,但是当页面刷新了,就又变回了北京。

考虑到在真实的项目中,如果你这次选中了一个城市,下次再打开这个网页的时候,上次选的城市还应该在的,怎么解决这个问题呢?

这时可以借助 HTML5 中提供了一个新的 api,叫做 localStorage,它可以实现本地存储,在这里也就是实现保存城市的功能。

store/index.js中,这样去写代码,当用户尝试去改变城市的时候,我不但把 state 中的 city 改了,同时还去存一个 localStorage,直接写 localStorage.city = city 就可以了。然后让 stare 中 city 的默认值是 localStorage.city || “北京”,就可以了。也就是 city 的值我默认先去 localStorage 中取,如果取不到,才用默认的 “北京”。

store/index.js中,这样去写代码,当用户尝试去改变城市的时候,我不但把 state 中的 city 改了,同时还去存一个 localStorage,直接写 localStorage.city = city 就可以了。然后让 stare 中 city 的默认值是 localStorage.city || “北京”,就可以了。也就是 city 的值我默认先去 localStorage 中取,如果取不到,才用默认的 “北京”。

store/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
city: localStorage.city || "北京"
},
mutations: {
changeCity(state, city) {
state.city = city;
localStorage.city = city;
}
}
})

这个时候打开页面,当用户选择一个城市,然后刷新页面,可以看到上次选择的城市还在。但是当使用 localStorage 的时候,建议在外层包裹一个 try{ }catch(e){ },因为在某些浏览器,如果用户关闭了本地存储这样的功能,或者使用隐身模式,使用 localStorage 可能导致浏览器直接抛出异常,代码就运行不了了,为了避免这种问题,建议在外层加一个 try{ }catch(e){ },怎么加呢?

先定义一个默认的 defaultCity 等于“北京”,然后写一个 try{ }catch(e){ },这样写:如果有 localStorage.city,default.city 就等于 localStorage.city,下边 state 中的 city 就可以等于 defaultCity 了,同样在 mutations 的 changeCity 中也要写一个 try{ }catch(e):

store/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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) { }
}
}
})

现在我们看到 store/index.js 这个文件慢慢的变得复杂起来了,实际上,在真正的项目开发和之中,会做进一步的拆分,也就是把这个文件拆分为 State、Actions、Mutations,在 store 中创建一个文件叫 state.js(只存储公用数据),然后把设置默认数据的这块代码放进去,并通过 export 导出,内容就是在 index.js 中定义的 state 对象里的内容:代
码见上面方法二中:

这样,我们就将 vuex 的代码拆分成了 State、Actions、Mutations 这几个部分,未来它的维护性也会得到比较大的提高。

keep-alive

使用 keep-alive 优化网页性能

当写完城市列表响应代码,启动服务,打开页面,这样看不存在什么问题,基本的一些业务逻辑都已经实现了,但是在控制台中打开 Network 网络这个选项,选择 XHR,当初次进入首页的时候,请求了一个 index.json 的文件,然后切换到列表页,又请求了一个 city.json,然后再回到首页,index.json 又请求了一次,再次去列表页,city.json 又请求了一次,也就是,每一次路由发生变化的时候,Ajax 都会重新的被发送。

ajax

思考是什么原因导致这样的问题呢,打开 Home.vue 首页这个组件,每一次打开这个首页的时候,都会被重新的渲染,所以 mounted 这个钩子就会被重新的执行,那么这个 Ajax 数据就会被重新获取,那么这么能让它只获取一次呢?

打开 main.js,可以看到入口组件是 App 这个组件,再打开 App.vue,router-view 显示的是当前地址所对应的内容,我们可以在外层包裹一个 keep-alive 的一个标签,他是 Vue 自带的一个标签,他的意思就是我的路由的内容被加载一次后,我就把路由中的内容放到内存之中,下一次再进入这个路由的时候,不需要重新渲染这个组件,去重新执行钩子函数,只要去内存里把以前的内容拿出来就可以。

keep-alive

这个时候,回到页面上,再打开 Network,进入到列表页,选择城市再返回首页,就不会再去加载 index.json 了,同样再进入列表页,也不会再去加载 city.json 了,他直接会从内存中调数据,而不会重新去法 Ajax 请求了。

keep-alive

这样还是存在逻辑上的问题的,当我在“北京”的时候,首页显示的是“北京”的内容,当切换为“上海”时,首页就应该显示“上海”的内容,所以城市发生改变的时候,首页还需要重新发一次 Ajax 请求,来获取不同城市的数据信息,我们对这一块做一个调整。

打开 Home.vue 组件,改一下 axios 请求地址这里,在他的后面带一个参数,让他等于 Vuex 中存的当前的城市,所以还需要在 Home.vue 组件中引用 Vuex,import { mapState } from “vuex”,然后再加一个计算属性:

1
2
3
computed:{
...mapState(['city'])
}

获取到城市对应的内容,然后就可以在发 Ajax 的时候,把 city 放在请求的参数里面:

1
2
axios.get("/api/index.json?city=" + this.city)
.then(this.getHomeInfoSucc);

这个时候,我们打开页面,可以看到请求参数里已经携带了当前的城市:

但是,例如当你切换了城市“桂林”,回到首页,并没有重新发 Ajax 请求,虽然上面的城市变成了“桂林”,但是底下的内容还是“北京”的内容,我们希望底下的内容跟着变,该怎么做呢?

当我们在 App.vue 中用了 keep-alive 的时候,这块的内容已经被缓存起来了,他直接取得是缓存里的数据,那如何去改变缓存里的数据呢?当你使用 keep-alive 的时候,组件中会多出一个生命周期函数 activted,

active

可以在 mounted 和 activated 两个生命周期函数下打印一些内容,到浏览器上看一下他俩的执行:

1
2
3
4
5
6
7
mounted() {
console.log("mounted");
this.getHomeInfo();
},
activated(){
console.log("activted");
}

打开页面,可以看到,mounted 和 activated 都会执行,当切换了城市,再回到首页的时候,组件的 mounted 就不会执行了,就只有 activated 会被执行,那么我们借助 activated 这个生命周期函数就可以实现我们想要的功能了。

首先在页面被挂载的时候,也就是 mounted 中一定会去发一个 Ajax 请求,当页面重新被显示的时候,activated 一定会被重新的执行,那么我们就可以在页面每次重新显示的时候,可以判断当前页面上的城市和上次页面上显示的城市是否是相同的,如果不相同的,就再发一次 Ajax 请求。

先在 data 中设置一个数据 lastCity,默认值是空,接着当页面被挂载的时候,让它等于 this.city,对上一次的城市做一个保存:

1
2
3
4
mounted() {
this.lastCity = this.city
this.getHomeInfo();
}

当页面被重新激活的时候,我们在 activted 中这样写:

1
2
3
4
5
6
activated() {
if(this.lastCity != this.city){
this.lastCity = this.city
this.getHomeInfo();
}
}

如果上一次的城市 lastCity 不等于当前城市的时候,就重新发一个 Ajax 请求,直接调用上面 getHomeInfo 方法就可以了。当上次的 city 和这次的 city 不一样时,还需要让他等于这次的 city。回到页面上,可以看到当切换的城市和上次的城市一样时,Ajax 就不会请求 city.json 了,当不一样时,才会去请求 city.json。

回到代码里面,通过 activted 这样一个 keep-alive 新增的生命周期函数,结合 lastCity 这样一个临时缓存变量,就实现了首页代码性能优化的调整。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!